返回首页

Vue 3 Composition API 实战:5 个让代码更清晰的技巧

从“能写”到“写得清楚”——把 setup 里的逻辑组织得更容易维护。

11 约 2 分钟 · 1749 字 前端

写了一段时间 Composition API 之后,我发现真正决定可维护性的并不是“用没用 ref / reactive”,而是逻辑怎么组织。把这阵子踩过的坑整理成几条简单的实践,自己复盘也方便其他人参考。

1. 用 composable 拆“一团乱麻”的 setup

setup 里如果出现“数据 + 副作用 + 事件 + 业务规则”全部堆在一起,就该往外抽了。一个判断标准:它能不能被另一个组件复用? 如果能,就给它一个 useXxx 的名字。

// composables/useToggle.ts
export function useToggle(initial = false) {
  const value = ref(initial)
  const toggle = () => (value.value = !value.value)
  return { value, toggle }
}

哪怕只被一处用到,抽出来也能让 setup 变成“一句话讲清楚组件在干嘛”。

2. ref 还是 reactive?优先用 ref

两个都能用,但 ref 有几个无可替代的好处:

  • 可以直接被解构和返回(reactive 解构会丢响应式)
  • <template> 里自动解包,书写体验和 data() 一样
  • 包基本类型时语义更清晰

只有一种场景我会优先选 reactive:确定是一个对象,且不需要整体替换,例如一个表单 model。

3. 异步状态用 useAsyncData 或自封装的“三件套”

不要在 setup 里随手 await fetch(...),会让初始渲染卡住、错误处理也变得分散。统一的封装至少返回三样东西:

const { data, pending, error, refresh } = useAsyncData('posts', () => $fetch('/api/posts'))

自己封装时也尽量贴近这个形状,所有调用点的心智模型就是一致的。

4. watch 的依赖一定要显式

// 不好:依赖什么不清楚,容易触发多次
watchEffect(() => { fetchSomething(route.params.id, filter.value) })

// 更清晰:依赖列出来,行为可控
watch(
  [() => route.params.id, filter],
  ([id, f]) => fetchSomething(id, f),
  { immediate: true },
)

watchEffect 适合“纯响应式副作用”,业务请求建议老老实实写 watch,既能精确控制 immediate / flush,也方便后续加上 { deep: true } / { once: true } 等选项。

5. 组件之间“传东西”,先想清楚方向

方向 推荐方式
父 → 子 defineProps
子 → 父 defineEmits
跨多层 provide / inject(或一个轻量 store)
全局 Pinia / useState / 自定义 composable

一个简单原则:能用 props/emits 解决就不要 provide,能用 provide 解决就不要全局 store。每往“更全局”的方向走一步,可测性和可读性都会下降一点。

小结

Composition API 给了我们把逻辑组织好的工具,但工具本身不会替我们做决策。把 setup 当成“目录”,把细节藏进 useXxx 里,代码就会比之前清晰一大截。