前言:在使用vuejs的过程中,我们几乎每天都写组件。但是在业务开发的过程中,我们会发现即使是不同功能的组件,里面也会包含很多的相似代码。我们会将大部分精力投入到相似代码之中,这样会大幅度降低我们的开发效率。这个时候,将这些具备相同功能的代码独立封装成一个组件可以减少开发时间,也可以降低后续维护的难度。
为什么需要封装组件
组件封装的本质,是控制复杂度。试想一下,一个页面具备多个含有loading的按钮,多个表单校验,多个弹窗,这是否会导致重复代码量的提升?是否会导致这个组件的代码行数的增多?这会导致我们在编写和维护变得困难。
<button class="btn-primary" @click="submit" :disabled="loading">
{{ loading ? '提交中...' : '提交' }}
</button>
当这种结构在多个页面出现时,一旦样式或交互规则改变,你需要修改所有地方。 这时,“复制”不是解决方案,“抽象”才是。
如何封装一个“好”的组件
在我的工作实践中,我总结出了以下几点。
- 单一职责:一个组件应该专注于一件事情,不能让一个组件既负责UI展示,又负责复杂业务计算。组件应该保持功能的纯粹。
- 高内聚,低耦合:组件应该保持自身的独立性,尽可能地把组件应该处理交给组件自身处理。组件内部逻辑自洽,对外只暴露必要接口。
- 保持单一数据流:组件内部使用pros接收父组件输入,使用emit向父组件输出数据。子组件要严格保有父组件传入的数据内容,不能直接修改,而是通过事件通知父组件自身修改。
- 可配置:通过 props 控制:类型、状态、行为。而不是把逻辑写在内部无法扩展。
- 可组合:可以使用slots让组件更加灵活。
从混乱到抽象:一个按钮组件的演进
我们通过一个简单例子,看看封装是如何一步步完成的。
第一步:页面中重复代码
<button
class="btn-primary"
:disabled="loading"
@click="submit"
>
{{ loading ? '提交中...' : '提交' }}
</button>
第二步:抽离基础组件
创建 BaseButton.vue:
<template>
<button
:class="['btn', `btn-${type}`]"
:disabled="disabled || loading"
@click="handleClick"
>
<slot />
</button>
</template>
<script setup>
const props = defineProps({
type: {
type: String,
default: 'primary'
},
disabled: Boolean,
loading: Boolean
})
const emit = defineEmits(['click'])
function handleClick(e) {
if (!props.loading && !props.disabled) {
emit('click', e)
}
}
</script>
第三步:外部使用
<BaseButton
type="primary"
:loading="loading"
@click="submit"
>
提交
</BaseButton>
封装的过程,其实就是:
不断识别重复 → 提取抽象 → 明确边界 → 提供接口。
组件类型的区分:不要过度抽象
并不是所有组件都应该做成“高度通用”。
我们可以大致分为三类:
1️⃣ 基础组件
例如:
- Button
- Input
- Modal
高复用、高抽象。
2️⃣ 业务组件
例如:
- 订单卡片
- 用户信息模块
与业务强绑定,不必追求极致通用。
3️⃣ 容器组件
负责:
- 数据请求
- 状态管理
- 业务组合
尽量不承担具体 UI 展示。
抽象过度同样是一种设计错误。
如果一个组件的 props 多到难以理解,那说明它可能承担了过多职责。
常见错误与反模式
在实际开发中,常见的问题包括:
- props 数量过多
- 子组件直接修改父组件数据
- 组件过度拆分导致维护成本增加
- 在 template 中堆积复杂逻辑
- 滥用 provide / inject
组件封装不是“拆得越碎越好”,而是“职责越清晰越好”。
总结:封装的核心是边界
Vue3 提供了更强的表达能力,但 API 只是工具。
真正决定组件质量的,是对“边界”的理解。
一个优秀的组件应该:
- 内部逻辑封闭
- 对外接口清晰
- 数据流向明确
- 结构可组合
- 行为可配置
从混乱到优雅,并不是一蹴而就。
它来自于:
- 对重复代码的敏感
- 对复杂度的警惕
- 对抽象层级的把握
这,才是组件封装真正的意义。
但是在工作中,为了提升效率,大部分情况是对已有基础组件进行二次封装,要避免重复造轮子。