认识
一、认识
组合式函数 (Composables)
是一个利用 Vue
的组合式 API
来封装和复用有状态逻辑的函数。其中 组合式函数 的约定和最佳实践为:
二、规范
2.1 命名
命名
: 组合式函数约定用驼峰命名法命名,并以use
作为开头。
2.2 参数
输入参数
: 尽管其响应性不依赖 ref
,组合式函数仍可接收 ref
参数。如果编写的组合式函数会被其他开发者使用,你最好在处理输入参数时兼容 ref
而不只是原始的值。
2.3 返回值
返回值
: 推荐的约定是组合式函数始终返回一个包含多个 ref
的普通的非响应式对象,这样该对象在组件中被解构为 ref
之后仍可以保持响应性。如果我们是用 reactive
, 那么从组合式函数中返回一个 reactive
响应式对象会导致在对象的解构中丢失与组合式函数内状态的响应性连接。与之相反,ref
则可以维持这一响应性连接。
2.4 副作用
副作用
: 在组合式函数中的确可以执行副作用, 请注意以下规则:
-
确保能访问到
DOM
-
确保在
onUnmounted()
时清理副作用。
2.5 使用限制
使用限制
: 组合式函数在 <script setup>
或 setup()
钩子中,应始终被同步地调用。在某些场景下,你也可以在像 onMounted()
这样的生命周期钩子中使用他们。这个限制是为了让 Vue
能够确定当前正在被执行的到底是哪个组件实例,只有能确认当前组件实例,才能够:
-
将生命周期钩子注册到该组件实例上
-
将计算属性和监听器注册到该组件实例上,以便在该组件被卸载时停止监听,避免内存泄漏。
三、语法
四、对比
4.1 组合式函数 vs mixin
Vue 2
的用户可能会对 mixins
选项比较熟悉。它也让我们能够把组件逻辑提取到可复用的单元里。然而 mixins
有三个主要的短板:
-
不清晰的数据来源:当使用了多个
mixin
时,实例上的数据属性来自哪个mixin
变得不清晰,这使追溯实现和理解组件行为变得困难。这也是我们推荐在组合式函数中使用ref + 解构模式
的理由:让属性的来源在消费组件时一目了然。 -
命名空间冲突:多个来自不同作者的
mixin
可能会注册相同的属性名,造成命名冲突。若使用组合式函数,你可以通过在解构变量时对变量进行重命名来避免相同的键名。 -
隐式的跨
mixin
交流:多个mixin
需要依赖共享的属性名来进行相互作用,这使得它们隐性地耦合在一起。而一个组合式函数的返回值可以作为另一个组合式函数的参数被传入,像普通函数那样。
基于上述理由,我们不再推荐在 Vue 3
中继续使用 mixin
。保留该功能只是为了项目迁移的需求和照顾熟悉它的用户。
4.2 组合式函数 vs 无渲染组件
组合式函数相对于无渲染组件的主要优势是: 组合式函数不会产生额外的组件实例开销。当在整个应用中使用时,由无渲染组件产生的额外组件实例会带来无法忽视的性能开销。
我们推荐在纯逻辑复用时使用组合式函数,在需要同时复用逻辑和视图布局时使用无渲染组件。
4.3 组合式函数 vs React Hooks
组合式 API
提供了和 React Hooks
相同级别的逻辑组织能力,但它们之间有着一些重要的区别。
React Hooks
在组件每次更新时都会重新调用。这就产生了一些即使是经验丰富的 React
开发者也会感到困惑的问题。这也带来了一些性能问题,并且相当影响开发体验。例如:
-
Hooks
有严格的调用顺序,并不可以写在条件分支中。 -
React
组件中定义的变量会被一个钩子函数闭包捕获,若开发者传递了错误的依赖数组,它会变得“过期”。这导致了React
开发者非常依赖ESLint
规则以确保传递了正确的依赖,然而,这些规则往往不够智能,保持正确的代价过高,在一些边缘情况时会遇到令人头疼的、不必要的报错信息。 -
昂贵的计算需要使用
useMemo
,这也需要传入正确的依赖数组。 -
在默认情况下,传递给子组件的事件处理函数会导致子组件进行不必要的更新。子组件默认更新,并需要显式的调用
useCallback
作优化。这个优化同样需要正确的依赖数组,并且几乎在任何时候都需要。忽视这一点会导致默认情况下对应用进行过度渲染,并可能在不知不觉中导致性能问题。 -
要解决变量闭包导致的问题,再结合并发功能,使得很难推理出一段钩子代码是什么时候运行的,并且很不好处理需要在多次渲染间保持引用 (通过
useRef
) 的可变状态。
相比起来,Vue
的组合式 API
:
-
仅调用
setup()
或<script setup>
的代码一次。这使得代码更符合日常JavaScript
的直觉,不需要担心闭包变量的问题。组合式API
也并不限制调用顺序,还可以有条件地进行调用。 -
Vue
的响应性系统运行时会自动收集计算属性和侦听器的依赖,因此无需手动声明依赖。 -
无需手动缓存回调函数来避免不必要的组件更新。
Vue
细粒度的响应性系统能够确保在绝大部分情况下组件仅执行必要的更新。对Vue
开发者来说几乎不怎么需要对子组件更新进行手动优化。
我们承认 React Hooks
的创造性,它是组合式 API
的一个主要灵感来源。然而,它的设计也确实存在上面提到的问题,而 Vue
的响应性模型恰好提供了一种解决这些问题的方法。