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 等。

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

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

相关推荐
狸克先生3 分钟前
如何用AI写小说(二):Gradio 超简单的网页前端交互
前端·人工智能·chatgpt·交互
sinat_384241095 分钟前
在有网络连接的机器上打包 electron 及其依赖项,在没有网络连接的机器上安装这些离线包
javascript·arcgis·electron
baiduopenmap17 分钟前
百度世界2024精选公开课:基于地图智能体的导航出行AI应用创新实践
前端·人工智能·百度地图
loooseFish25 分钟前
小程序webview我爱死你了 小程序webview和H5通讯
前端
小牛itbull29 分钟前
ReactPress vs VuePress vs WordPress
开发语言·javascript·reactpress
请叫我欧皇i37 分钟前
html本地离线引入vant和vue2(详细步骤)
开发语言·前端·javascript
533_40 分钟前
[vue] 深拷贝 lodash cloneDeep
前端·javascript·vue.js
guokanglun1 小时前
空间数据存储格式GeoJSON
前端
GIS瞧葩菜1 小时前
局部修改3dtiles子模型的位置。
开发语言·javascript·ecmascript
zhang-zan1 小时前
nodejs操作selenium-webdriver
前端·javascript·selenium