Vue 3 发布已有数年,Composition API 已经成为 Vue 生态的标准开发范式。然而在代码评审中,我们仍然频繁看到 Options API 思维下的"Composition API 写法"——将选项式代码机械地搬进 setup 函数,未能发挥 Composition API 的真正威力。
组合优于继承:Composable 设计原则
Composition API 的核心思想是"按功能组织代码,而非按选项分散代码"。一个好的 Composable 应遵循"单一职责"——每个 Composable 只负责一个明确的功能领域:
useUserAuth:认证状态、登录/登出、Token 管理useProductList:列表数据、分页、筛选、排序useFormValidation:表单校验规则、错误状态、提交处理useMediaQuery:响应式断点检测
命名规范:必须使用 use 前缀,这是 Vue 社区的约定,也是 ESLint 规则可以自动检查的。返回结构建议浅层对象而非嵌套对象,减少使用方的解构心智负担。
状态管理:什么该进 Pinia?
并非所有状态都需要中心化 Store。我们遵循简单的决策树:组件内部状态 → ref/reactive;父子传递 → props/emits 或 provide/inject;跨组件共享且有复杂逻辑 → Pinia Store。
一个常见反模式是"过早抽象"——在所有状态上套一层 Pinia。这样不仅增加代码量,还使状态流变得难以追踪。正确的做法是"延迟决策":先写组件内的 ref,当确实需要跨组件共享时,再重构为 Store。
异步数据的三态模式
// composables/useAsyncData.ts
export function useAsyncData<T>(fetcher: () => Promise<T>) {
const data = shallowRef<T | null>(null)
const isLoading = ref(false)
const error = shallowRef<Error | null>(null)
async function execute() {
isLoading.value = true
error.value = null
try {
data.value = await fetcher()
} catch (e) {
error.value = e as Error
} finally {
isLoading.value = false
}
}
return { data, isLoading, error, execute }
}
这种模式让模板可以优雅地处理三种状态,避免"闪现错误"和"无限 Loading"。对于更复杂的场景(缓存、去重、后台刷新),推荐使用 TanStack Query(Vue 版)或 VueUse 的 useAsyncState。