组合式 API 是一系列 API 的集合,使我们可以使用函数而不是声明选项的方式书写 Vue 组件。它是一个概括性的术语,涵盖了以下方面的 API:
ref()
和reactive()
,使我们可以直接创建响应式状态、计算属性和侦听器。onMounted()
和onUnmounted()
,使我们可以在组件各个生命周期阶段添加逻辑。provide()
和inject()
,使我们可以在使用响应性 API 时,利用 Vue 的依赖注入系统。在 Vue 3 中,组合式 API 基本上都会配合<script setup>
语法在单文件组件中使用。下面是一个使用组合式 API 的组件示例:
<script setup> import { ref, onMounted } from 'vue' // 响应式状态 const count = ref(0) // 更改状态、触发更新的函数 function increment() { count.value++ } // 生命周期钩子 onMounted(() => { console.log(`计数器初始值为 ${count.value}。`) }) </script> <template> <button @click="increment">点击了:{{ count }} 次</button> </template>
虽然这套 API 的风格是基于函数的组合,但组合式 API 并不是函数式编程。组合式 API 是以 Vue 中数据可变的、细粒度的响应性系统为基础的,而函数式编程更强调数据不可变。
组合式 API 最基本的优势是它使我们能够通过组合函数来实现更加简洁高效的逻辑复用。它解决了所有mixins的缺陷,那是选项式 API 中一种逻辑复用机制。
许多用户都喜欢选项式 API,因为在默认情况下就能够写出有组织的代码:任何东西都有其对应的选项来管理。然而,选项式 API 在单个组件的逻辑复杂到一定程度时,也面临了一些无法忽视的限制。这些限制主要体现在需要处理多个逻辑关注点的组件中,在许多 Vue 2 已经上线的生产应用中可以看到这一点。
我们以 Vue CLI GUI 中的文件浏览器组件为例:这个组件承担了以下几个逻辑关注点:
这个组件最原始的版本是由选项式 API 写成的。而如果用组合式 API 重构这个组件,将会变成:
现在与同一个逻辑关注点相关的代码被归为了一组:我们无需再为了一个逻辑关注点在不同的选项块间来回滚动切换。此外,我们现在可以不费吹灰之力地将这一组代码移动到一个外部文件中,不再需要为了抽象而重新组织代码,大大降低了重构成本,这在长期维护的大型项目中非常关键。
近几年来,越来越多的开发者开始使用TypeScript书写更健壮可靠的代码,TypeScript还提供了非常好的IDE 开发支持。然而选项式 API 是在 2013
年创建的,那时并没有想到需要进行类型推导。因此我们做了一些荒谬复杂的类型体操来实现对选项式 API 的类型推导。但尽管做了这么多的努力,选项式 API 的类型推导仍然无法适配混入和依赖注入。
因此,很多想要搭配 TS 使用 Vue 的开发者采用了由vue-class-component提供的Class API。然而,基于 Class 的 API 非常依赖 ES 装饰器,在 Vue 2019 年开发完成后,它仍是一个仅处于 vue 2的语言功能。我们认为将这样一种不稳定的方案作为官方 API 的一种实现形式风险过高,在那之后装饰器提案还进行了一些较大的变动,在书写这篇文档时仍未到达 vue 3。另外,基于 Class 的 API 和选项式 API 在逻辑复用和代码组织方面有相同的限制。
相比之下,组合式 API 主要利用基本的变量和函数,它们本身就是类型友好的。用组合式 API 重写的代码可以享受到完整的类型推导,不需要书写太多类型标注。大多数时候,用 TypeScript 书写的组合式 API 代码和用 JavaScript 写都差不太多!这也同样让许多纯 JavaScript 用户能从IDE 中享受到部分类型推导功能。
搭配<script setup>
使用组合式 API 比等价情况下的选项式 API 更高效,对代码压缩也更友好。这是由于<script setup>
;形式书写的组件模板被编译为了一个内连函数,和<script setup>
中的代码位于同一作用域。不像选项式 API 需要依赖this
上下文对象访问属性,被编译的模板可以直接访问<script setup>
中定义的变量,无需一个代码实例从中代理。这对代码压缩更友好,因为变量的名字可以变得更短,但对象的属性名则不能。
对于有状态的逻辑来说,的确如此。当使用组合式 API 时,只需要用到一小部分选项:props
,emits
,name
和inheritAttrs
。如果使用<script setup>
,那么inheritAttrs
应该是唯一一个需要用额外的<script>
块书写的选项了。
如果你在代码中只使用了组合式 API(以及上述必需的选项),得益于编译时标记你可以减小生产包大概几 kb 左右的体积,因为丢掉了 Vue 之中关于选项式 API 的所有代码。注意这也会影响你依赖中的 Vue 组件。
可以。你可以在一个选项式 API 组件中使用setup()
选项。
然而,我们只推荐你在就旧项目中这样使用。它们长期基于选项式 API 开发、又可能想要集成新的功能,或是想要集成基于组合式 API 的第三方库。
不会,我们没有任何计划这样做。选项式 API 也是 Vue 不可分割的一部分,也有很多开发者喜欢它。我们也意识到组合式 API 主要适用于非常大型的项目,而对于中小型项目来说选项式 API 仍然是一个不错的选择。