Vue 组合式函数的最佳实践和设计模式

大家好,这里是大家的林语冰。

就我们的系统而言,组合式函数负责存储主要业务逻辑(比如计算、操作、流程),因此它们是 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 }
}

然后可以在组件中使用它,如下所示:

组合式函数规约

根据上面的示例,以下是所有组合式函数都应遵循的规约:

  1. 组合式函数文件名应以 use 开头,举个栗子,useSomeAmazingFeature.ts
  2. 它可以接受输入参数,这些参数可以是字符串等基本类型,也可以接受 refgetter,但它需要使用 toValue 辅助函数
  3. 组合式函数应该返回一个 ref 值,该值可以在解构组合式函数后访问,比如 const { x, y } = useMouse()
  4. 组合式函数可以保存全局状态,该状态可以在 App 中访问和修改。
  5. 组合式函数可能会导致副作用,比如添加窗口事件侦听器,但在卸载组件时应清除它们。
  6. 组合式函数能且仅能在 <script setup>setup() 钩子中调用。它们也应该在这些上下文中同步调用。在某些情况下,您还可以在生命周期钩子中调用它们,比如 onMounted()
  7. 组合式函数可以在内部调用其他组合式函数。
  8. 组合式函数应在内部包装某些逻辑,当过于复杂时,应将它们提取到单独的组合式函数中,以便于测试。

我的建议

有状态 or/and 纯函数的组合式函数

在代码标准化的某个时刻,您可能会得出这样的结论:您希望对组合式函数中的状态保留做出决定。

最容易测试的函数是那些不存储任何状态的函数(即它们是简单的输入/输出函数),举个栗子,组合式函数可能负责将字节转换为人类可读的值。它接受一个值并返回一个不同的值 ------ 它不存储任何状态。

不要误会我的意思,您可以完全保留有状态和无状态的组合式函数。但这应该是一个书面决定,以便之后更易于和它们合作。

组合式函数的单元测试

我们希望使用 Vitest 为我们的前端 App 实施单元测试。在后端工作时,进行单元测试代码覆盖率非常有用,因为您主要关注逻辑。虽然但是,在前端,您通常使用视觉效果。

因此,我们认为对整个组件进行单元测试可能不是最好的主意,因为我们基本上会对框架本身进行单元测试(如果按下按钮,检查状态是否更改或模式是否打开)。

由于我们已将所有业务逻辑移至组合式函数(基本上是 JS/TS 函数)内,因此它们很容易使用 Vitest 进行测试,并且允许我们拥有更稳定的系统。

组合式函数的作用域

不久前,在 VueStorefront 中,我们开发了自己的组合式函数(早在它们实际上像这样被调用之前)。在我们的方法中,我们使用可组合项来映射电子商务的业务领域,如下所示:

js 复制代码
const { cart, load, addItem, removeItem, remove, ... } = useCart()

这种方法绝对有用,因为它允许将域(domain)包装在一个函数中。在 useProductuseCategory 等更简单的示例中,实现和维护相对简单。虽然但是,正如您在此处的 useCart 示例中所看到的,当包装一个包含更多逻辑而不仅仅是数据获取的域时,这个组合式函数正在发展成为一种非常难以开发和维护的形状。

此时,我开始为 Nuxt 生态系统做出贡献,其中引入了不同的方法。在这种新方法中,一个组合式函数仅能且仅能负责一件事。因此,我们的想法不是构建一个巨大的 useCart 组合式函数,而是为每个功能构建组合式函数,即 useAddToCartuseFetchCartuseRemovefromCart 等。

因此,维护和测试这些组合式函数应该更容易。

您现在收看的是《前端翻译计划》,学废了的小伙伴可以订阅此专栏合集,我们每天佛系投稿,欢迎持续关注前端生态。谢谢大家的点赞,掰掰~

相关推荐
m0_748256786 分钟前
SpringBoot 依赖之Spring Web
前端·spring boot·spring
web1350858863535 分钟前
前端node.js
前端·node.js·vim
m0_5127446436 分钟前
极客大挑战2024-web-wp(详细)
android·前端
若川1 小时前
Taro 源码揭秘:10. Taro 到底是怎样转换成小程序文件的?
前端·javascript·react.js
潜意识起点1 小时前
精通 CSS 阴影效果:从基础到高级应用
前端·css
奋斗吧程序媛1 小时前
删除VSCode上 origin/分支名,但GitLab上实际上不存在的分支
前端·vscode
IT女孩儿1 小时前
JavaScript--WebAPI查缺补漏(二)
开发语言·前端·javascript·html·ecmascript
m0_748256563 小时前
如何解决前端发送数据到后端为空的问题
前端
请叫我飞哥@4 小时前
HTML5适配手机
前端·html·html5
@解忧杂货铺5 小时前
前端vue如何实现数字框中通过鼠标滚轮上下滚动增减数字
前端·javascript·vue.js