为什么要使用 composition(组合) api
更好的逻辑复用
在 vue2 中,我们实现逻辑复用主要使用的 mixins 这一api。但是这个api存在一些缺陷,比如:
- 不清晰的数据来源:如果在同一个组件中使用多个
mixin,对具体mixin的溯源会变的不清晰。 - 命名空间冲突:多个来自不同作者的
mixin可能会注册相同的属性名,造成命名冲突。 - 隐式的跨
mixin交流:多个mixin需要依赖共享的属性名来进行相互作用,这使得它们隐性地耦合在一起。
vue3 中使用 composition api 解决了 mixins 进行逻辑复用时带来的问题。
更灵活的代码组织
vue2 使用 options(选项) api ,当单个组件变得复杂起来时(代码量达到 300 以上),我们在开发的时候会上下来回对应数据、方法与视图。开发起来会变得困难。
使用 composition api 我们可以将同一个逻辑关注点相关的代码被归为了一组:我们无需再为了一个逻辑关注点在不同的选项块间来回滚动切换。这在复杂度高的项目中是很关键的,降低阅读、重构的成本。
更好的类型推导
composition api 主要利用基本的变量和函数,它们本身就是类型友好的。
用 composition api 重写的代码可以享受到完整的类型推导,不需要书写太多类型标注。
更小的生产包体积
options api 需要依赖 this 上下文对象访问属性,被编译的模板可以直接访问 <script setup> 中定义的变量,无需一个代码实例从中代理。这对代码压缩更友好,因为本地变量的名字可以被压缩,但对象的属性名则不能。
如何使用 composition api
命名
组合式函数约定用驼峰命名法命名,并以“use”作为开头,例如我们在 vue3 中使用 vue-router:
const router = useRouter()
对输入参数进行处理
composition api 可能会接收到 ref 参数,我们可以用 unref() 工具函数对入参进行处理:
import { unref } from 'vue'
function useFeature(maybeRef) {
// 若 maybeRef 确实是一个 ref,它的 .value 会被返回
// 否则,maybeRef 会被原样返回
const value = unref(maybeRef)
}
返回值
推荐的约定是组合式函数始终返回一个包含多个 ref 的普通的非响应式对象,这样该对象在组件中被解构为 ref 之后仍可以保持响应性:
// x 和 y 是两个 ref
const { x, y } = useMouse()
副作用
在 composition api 下使用副作用要注意以下两点:
-
如果你的应用用到了服务端渲染
(SSR),请确保在组件挂载后才调用的生命周期钩子中执行DOM相关的副作用,例如:onMounted()。这些钩子仅会在浏览器中被调用,因此可以确保能访问到DOM。
-
确保在
onUnmounted()时清理副作用。举例来说,如果一个组合式函数设置了一个事件监听器,它就应该在onUnmounted()中被移除 (就像我们在useMouse()示例中看到的一样)。当然也可以像之前的useEventListener()示例那样,使用一个组合式函数来自动帮你做这些事。
使用限制
composition api 函数在 <script setup> 或 setup() 钩子中,应始终被同步地调用。在某些场景下,你也可以在像 onMounted() 这样的生命周期钩子中使用他们。
这个限制是为了让 Vue 能够确定当前正在被执行的到底是哪个组件实例,只有能确认当前组件实例,才能够:
-
将生命周期钩子注册到该组件实例上
-
将计算属性和监听器注册到该组件实例上,以便在该组件被卸载时停止监听,避免内存泄漏。
与 React Hooks 对比
React Hooks存在一些问题:
-
Hooks有严格的调用顺序,并不可以写在条件分支中。 -
React组件中定义的变量会被一个钩子函数闭包捕获,若开发者传递了错误的依赖数组,它会变得“过期”。这导致了React开发者非常依赖ESLint规则以确保传递了正确的依赖,然而,这些规则往往不够智能,保持正确的代价过高,在一些边缘情况时会遇到令人头疼的、不必要的报错信息。 -
昂贵的计算需要使用
useMemo,这也需要传入正确的依赖数组。 -
在默认情况下,传递给子组件的事件处理函数会导致子组件进行不必要的更新。子组件默认更新,并需要显式的调用
useCallback作优化。这个优化同样需要正确的依赖数组,并且几乎在任何时候都需要。忽视这一点会导致默认情况下对应用进行过度渲染,并可能在不知不觉中导致性能问题。 -
要解决变量闭包导致的问题,再结合并发功能,使得很难推理出一段钩子代码是什么时候运行的,并且很不好处理需要在多次渲染间保持引用 (通过
useRef) 的可变状态。
vue3 composition api的改进
-
仅调用
setup()或<script setup>的代码一次。这使得代码更符合日常JavaScript的直觉,不需要担心闭包变量的问题。composition api也并不限制调用顺序,还可以有条件地进行调用。 -
Vue的响应性系统运行时会自动收集计算属性和侦听器的依赖,因此无需手动声明依赖。 -
无需手动缓存回调函数来避免不必要的组件更新。
Vue细粒度的响应性系统能够确保在绝大部分情况下组件仅执行必要的更新。对Vue开发者来说几乎不怎么需要对子组件更新进行手动优化。