大家好,这里是大家的林语冰。
就我们的系统而言,组合式函数负责存储主要业务逻辑(比如计算、操作、流程),因此它们是 App 的关键部分。不幸的是,随着时间的推移,我们没有那么多时间来创建某些组合式函数编程规约,因为我们的组合式函数可能不是真正的组合式函数。
我真的很高兴现在我们有时间重构构建全新组合式函数的方法,使它们可维护、易于测试且真正可用。
在本文中,我将总结我们创建的想法,并将它们与最佳实践和设计模式"梦幻联动"。
免责声明
本文属于是语冰的直男翻译了属于是,略有删改,仅供粉丝参考,英文原味版请临幸 Good practices and Design Patterns for Vue Composables。
所以本文将分为以下部分:
- 通用设计模式
- 我的建议
通用设计模式
基本组合式函数
Vue 文档表演了 useMouse
组合式函数,如下所示:
js
// mouse.js
import { ref, onMounted, onUnmounted } from 'vue'
// 约定俗成,组合式函数命名以 use 开头
export function useMouse() {
// 由组合式函数封装和管理的状态
const x = ref(0)
const y = ref(0)
// 组合式函数可以随着时间推移更新其管理的状态。
function update(event) {
x.value = event.pageX
y.value = event.pageY
}
// 组合式函数也可以钩入其所属组件的生命周期,
// 用以设置和清除副作用。
onMounted(() => window.addEventListener('mousemove', update))
onUnmounted(() => window.removeEventListener('mousemove', update))
// 将管理的状态公开为返回值
return { x, y }
}
稍后可以在组件中使用它,如下所示:
异步组合式函数
为了请求数据,Vue 建议遵循下述组合式函数结构:
js
export function useFetch(url) {
const data = ref(null)
const error = ref(null)
watchEffect(() => {
// 请求之前重置状态
data.value = null
error.value = null
// toValue() 解包潜在 refs 或 getters
fetch(toValue(url))
.then(res => res.json())
.then(json => (data.value = json))
.catch(err => (error.value = err))
})
return { data, error }
}
然后可以在组件中使用它,如下所示:
组合式函数规约
根据上面的示例,以下是所有组合式函数都应遵循的规约:
- 组合式函数文件名应以
use
开头,举个栗子,useSomeAmazingFeature.ts
- 它可以接受输入参数,这些参数可以是字符串等基本类型,也可以接受
ref
和getter
,但它需要使用toValue
辅助函数 - 组合式函数应该返回一个
ref
值,该值可以在解构组合式函数后访问,比如const { x, y } = useMouse()
- 组合式函数可以保存全局状态,该状态可以在 App 中访问和修改。
- 组合式函数可能会导致副作用,比如添加窗口事件侦听器,但在卸载组件时应清除它们。
- 组合式函数能且仅能在
<script setup>
或setup()
钩子中调用。它们也应该在这些上下文中同步调用。在某些情况下,您还可以在生命周期钩子中调用它们,比如onMounted()
。 - 组合式函数可以在内部调用其他组合式函数。
- 组合式函数应在内部包装某些逻辑,当过于复杂时,应将它们提取到单独的组合式函数中,以便于测试。
我的建议
有状态 or/and 纯函数的组合式函数
在代码标准化的某个时刻,您可能会得出这样的结论:您希望对组合式函数中的状态保留做出决定。
最容易测试的函数是那些不存储任何状态的函数(即它们是简单的输入/输出函数),举个栗子,组合式函数可能负责将字节转换为人类可读的值。它接受一个值并返回一个不同的值 ------ 它不存储任何状态。
不要误会我的意思,您可以完全保留有状态和无状态的组合式函数。但这应该是一个书面决定,以便之后更易于和它们合作。
组合式函数的单元测试
我们希望使用 Vitest 为我们的前端 App 实施单元测试。在后端工作时,进行单元测试代码覆盖率非常有用,因为您主要关注逻辑。虽然但是,在前端,您通常使用视觉效果。
因此,我们认为对整个组件进行单元测试可能不是最好的主意,因为我们基本上会对框架本身进行单元测试(如果按下按钮,检查状态是否更改或模式是否打开)。
由于我们已将所有业务逻辑移至组合式函数(基本上是 JS/TS 函数)内,因此它们很容易使用 Vitest 进行测试,并且允许我们拥有更稳定的系统。
组合式函数的作用域
不久前,在 VueStorefront 中,我们开发了自己的组合式函数(早在它们实际上像这样被调用之前)。在我们的方法中,我们使用可组合项来映射电子商务的业务领域,如下所示:
js
const { cart, load, addItem, removeItem, remove, ... } = useCart()
这种方法绝对有用,因为它允许将域(domain)包装在一个函数中。在 useProduct
或 useCategory
等更简单的示例中,实现和维护相对简单。虽然但是,正如您在此处的 useCart
示例中所看到的,当包装一个包含更多逻辑而不仅仅是数据获取的域时,这个组合式函数正在发展成为一种非常难以开发和维护的形状。
此时,我开始为 Nuxt 生态系统做出贡献,其中引入了不同的方法。在这种新方法中,一个组合式函数仅能且仅能负责一件事。因此,我们的想法不是构建一个巨大的 useCart
组合式函数,而是为每个功能构建组合式函数,即 useAddToCart
、useFetchCart
、useRemovefromCart
等。
因此,维护和测试这些组合式函数应该更容易。
您现在收看的是《前端翻译计划》,学废了的小伙伴可以订阅此专栏合集,我们每天佛系投稿,欢迎持续关注前端生态。谢谢大家的点赞,掰掰~