出处:掘金
原作者:金泽宸
组件不是“能用”就行,而是“能复用、能扩展、能插拔”才算架构师级别
写在前面
在前端开发中,组件是最小的交互单元,也是系统构建的基石。 架构师如果不能设计出一套“可维护 + 可插拔 + 可复用 + 可测试”的组件体系,项目的复杂度会以指数级增长
这一篇,我们将深入解析高质量组件设计的三大核心模式,并通过多个实战案例拆解:
- 可复用(Reusable)
- 可扩展(Extensible)
- 可插拔(Pluggable)
可复用组件设计:让你的组件不止用一次
原则 1:数据驱动优先,props 替代硬编码
- ❌ 不可复用组件(写死 UI,一梭子就是干!)
<!-- Bad: Modal.vue -->
<template>
<div class="modal">
<h3>确认删除?</h3>
<p>删除后不可恢复</p>
<button @click="confirm">确定</button>
</div>
</template>
- ✅ 改造后(props 控制内容,经典常用)
<!-- Good: Modal.vue -->
<template>
<div class="modal">
<h3>{{ title }}</h3>
<p>{{ description }}</p>
<slot name="footer">
<button @click="onConfirm">确定</button>
</slot>
</div>
</template>
<script setup>
defineProps({
title: String,
description: String,
onConfirm: Function,
})
</script>
使用方式:
<Modal title="确认删除" description="删除后不可恢复" :onConfirm="deleteUser">
<template #footer>
<pf-button type="danger" @click="deleteUser">确认删除</pf-button>
</template>
</Modal>
可扩展组件设计:对变化保持开放
目标:当业务需求变化时,组件不被迫大改,而是通过扩展点实现新增功能
方法 1:暴露 slot
插槽
<!-- DataTable.vue -->
<template>
<table>
<thead>
<tr>
<th v-for="col in columns">{{ col.label }}</th>
</tr>
</thead>
<tbody>
<tr v-for="row in data">
<td v-for="col in columns">
<slot :name="col.prop" :row="row">
{{ row[col.prop] }}
</slot>
</td>
</tr>
</tbody>
</table>
</template>
使用方式:
<DataTable :columns="[{ prop: 'name', label: '名称' }]">
<template #name="{ row }">
<span style="color: red">{{ row.name }}</span>
</template>
</DataTable>
优势:
- 默认渲染走组件内逻辑
- 业务方可插入任意结构,无需修改组件源码
方法 2:组件内部暴露“扩展点 API”
// useTable.ts
export function useTable({ fetchFn, transformFn }) {
const loading = ref(false)
const data = ref([])
async function load() {
loading.value = true
const res = await fetchFn()
data.value = transformFn ? transformFn(res) : res
loading.value = false
}
onMounted(load)
return { data, loading, reload: load }
}
外部传入扩展逻辑:
const { data, loading } = useTable({
fetchFn: fetchUserList,
transformFn: (res) => res.map((i) => ({ ...i, label: i.name })),
})
组件变得更加灵活、组合性更强
可插拔组件设计:打造“系统级能力”
让业务开发者不改动代码,只配置/注入,即可使用组件新能力
示例 1:按钮 + 权限系统
<!-- PermissionButton.vue -->
<template>
<slot v-if="hasPermission"></slot>
</template>
<script setup>
import { usePermission } from '@/shared/hooks/usePermission'
const props = defineProps({ code: String })
const hasPermission = usePermission(props.code)
</script>
使用方式:
<PermissionButton code="user.delete">
<button @click="deleteUser">删除用户</button>
</PermissionButton>
组件“无侵入”支持权限逻辑,支持任意业务接入
示例 2:动态组件注册机制
// registerComponents.ts
import { app } from 'vue'
import ChartCard from './ChartCard.vue'
import TableCard from './TableCard.vue'
export const registerDynamicComponents = (app) => {
app.component('ChartCard', ChartCard)
app.component('TableCard', TableCard)
}
平台配置:
[
{ "type": "ChartCard", "data": { "title": "访客趋势" } },
{ "type": "TableCard", "data": { "columns": [...], "data": [...] } }
]
渲染引擎:
<component
v-for="item in configList"
:key="item.type"
:is="item.type"
v-bind="item.data"
/>
用户只需配置 JSON,即可渲染页面
组件设计规范建议表
项目 | 建议 |
---|---|
命名规范 | 统一 PascalCase,如 UserCard |
props 设计 | 支持 default、可组合、类型明确 |
插槽设计 | 命名清晰:#header 、#footer 、#cell-{column} |
事件命名 | 使用 update: 、onXXX 标准 |
代码拆分 | 分离逻辑 hooks、展示 UI、样式模块 |
性能优化 | v-memo 、defineOptions({ inheritAttrs: false }) |
实战组件封装案例建议
- 通用弹窗服务(useDialog + Modal 插槽)
- 表格系统封装(动态列配置 + 权限按钮 + 嵌套子表格)
- 上传系统(支持文件/图片/压缩包 + 自定义校验 + 后缀限制)
- 表单系统(JSON Schema 驱动 + 自定义校验规则 + 动态联动)
- 图表系统(ECharts 容器 + 响应式封装 + 动态配置渲染)