Vue 组件设计模式精选:从基础到高阶的最佳实践总结
Vue 的灵活性是双刃剑:正因为什么都能写,团队很容易陷入"万物皆超级组件"的困境,导致组件越来越重,难以复用,测试成本暴增。
好的组件设计模式能做到一件事:用清晰的职责分割和数据流向,让组件既强大又轻量。
1. 模式 1:无状态展示组件 vs 有状态容器组件
最基础也最容易忽视的分离:
展示型组件:
- 只接收 props,不维护状态
- 把数据流转化为 UI
- 易于测试与复用
容器型组件:
- 管理数据获取和状态
- 处理业务逻辑
- 传递数据给子展示组件
分离的好处是,一个业务页面改了,对应的展示组件可能完全不用改。
2. 模式 2:作用域插槽(Scoped Slot)的四种典型应用
不少人知道作用域插槽的语法,但不知道什么时候该用。常见的正确应用场景:
场景 A:列表渲染可定制
组件维护列表数据和筛选逻辑,插槽外部决定每一行怎么展示。
场景 B:模态/弹层框架
组件管理显示隐藏和尺寸,内容由使用方提供。
场景 C:表单字段自定义
Form 组件管理验证和提交,各字段类型由父级灵活注入。
场景 D:数据转换可选
组件提供原始数据和转换过的数据,使用方选择哪个。
作用域插槽的威力在于"框架由组件定,内容由使用方定"。
3. 模式 3:Render Props—当插槽不够时
JavaScript 的函数优先性,有些场景比插槽更灵活。虽然 Vue 3 推荐 <template #default="{ item }"> 语法,但用函数传 Props 有时更清晰:
<DataProvider v-slot="{ data, loading }">
<div v-if="loading">加载中...</div>
<div v-else>{{ data }}</div>
</DataProvider>
或者用组合函数的方式:
const { data, loading } = useDataFetch(url)
Render Props 的优势在于"一个组件可由多个来源驱动"。
4. 模式 4:复合模式—多个小组件组成一个功能
不要把一个复杂表单做成一个 1000 行组件,而要拆成:
- FormField(统一字段包装)
- FormGroup(字段分组)
- Form(整体提交管理)
彼此独立但互相协作,通过 provide/inject 自动传递验证状态和错误。
这样的好处是:
- FormField 可以在任何地方独立用
- Form 可以支持任何字段组合
- 每个部分都是可测试的
5. 模式 5:自定义 hooks 提取逻辑
当多个组件需要同一套逻辑时,不要复制粘贴,而要写 composable:
export const useFormValidation = (schema) => {
const values = ref({})
const errors = ref({})
const validate = () => { /* 验证逻辑 */ }
return { values, errors, validate }
}
这样 10 个组件可以共享同一套验证逻辑,一处修改全局生效。
6. 失败案例:过度抽象导致的冗余
一个常见的反面教材是"为了复用而复用":
- 写了一个"通用"组件,为了支持 20 种场景,加了 40 个 props
- 结果每次使用都要查文档,别人不敢改
- 最后还是有人绕过它写成了 50 行本地代码
相反,3 个"专用"组件,各司其职,往往更好维护。
7. 推荐的设计清单
新建组件时,问自己:
- 这个组件的单一职责是什么
- 数据流入是 props 还是 provide/inject
- 它需要暴露哪些插槽或事件给父级
- 有没有可复用的子逻辑值得提取成 hook
- 这个组件的测试用例至少有 3 个
- 文档有没有说清楚什么场景该用它


