用了三年 Vue,我终于理解为什么“组件设计”才是重灾区

一开始写 Vue 的时候,谁不是觉得:"哇,组件好优雅!"三年后再回头一看,组件目录像垃圾堆,维护一处改三处,props 乱飞、事件满天飞,复用全靠 copy paste。于是我终于明白 ------ 组件设计,才是 Vue 项目的重灾区


1. 抽组件 ≠ 拆文件夹

很多初学 Vue 的人对"组件化"的理解就是:"页面上出现重复的 UI?好,抽个组件。"

于是你会看到这样的组件:

html 复制代码
<!-- TextInput.vue -->
<template>
  <input :value="value" @input="$emit('update:value', $event.target.value)" />
</template>

接着你又遇到需要加图标的输入框,于是复制一份:

html 复制代码
<!-- IconTextInput.vue -->
<template>
  <div class="icon-text-input">
    <i class="icon" :class="icon" />
    <input :value="value" @input="$emit('update:value', $event.target.value)" />
  </div>
</template>

再后来你需要加验证、loading、tooltip......结果就变成了:

  • TextInput.vue
  • IconTextInput.vue
  • ValidatableInput.vue
  • LoadingInput.vue
  • FormInput.vue

组件爆炸式增长,但每一个都只是"刚好凑合",共用不了。


2. 抽象失控:为了复用而复用,结果没人敢用

比如下面这个场景:

你封装了一个超级复杂的表格组件:

html 复制代码
<CustomTable
  :columns="columns"
  :data="tableData"
  :show-expand="true"
  :enable-pagination="true"
  :custom-actions="['edit', 'delete']"
/>

你美其名曰"通用组件",但别人拿去一用就发现:

  • 某个页面只要展示,不要操作按钮,配置了也没法删;
  • 有个页面需要自定义排序逻辑,你这边死写死;
  • 另一个页面用 element-plus 的样式,这边你自绘一套 UI;
  • 报错时控制台输出一大堆 warning,根本不知道哪来的。

最后大家的做法就是 ------ 不用你这套"通用组件",自己抄一份改改


3. 数据向下流、事件向上传:你真的理解 props 和 emit 吗?

Vue 的单向数据流原则说得很清楚:

父组件通过 props 向下传数据,子组件通过 emit 通知父组件。

但现实是:

  • props 传了 7 层,页面逻辑根本看不懂数据哪来的;
  • 子组件 emit 了两个 event,父组件又传回了回调函数;
  • 有时候干脆直接用 inject/providerefeventBus 偷偷打通通信。

举个例子:

html 复制代码
<!-- 祖父组件 -->
<template>
  <PageWrapper>
    <ChildComponent :formData="form" @submit="handleSubmit" />
  </PageWrapper>
</template>

<!-- 子组件 -->
<template>
  <Form :model="formData" />
  <button @click="$emit('submit', formData)">提交</button>
</template>

看上去还好?但当 ChildComponent 再包一层 FormWrapper、再嵌套 InputList,你就发现:

  • formData 根本不知道是哪个组件控制的
  • submit 被多层包装、debounce、防抖、节流、劫持
  • 你改一个按钮逻辑,要翻 4 个文件

4. 技术债爆炸的罪魁祸首:不敢删、不敢动

组件目录看似整齐,但大部分组件都有如下特征:

  • 有 10 个 props,3 个事件,但没人知道谁在用;
  • 注释写着"用于 A 页面",实际上 B、C、D 页面也在引用;
  • 一个小改动能引发"蝴蝶效应",整个系统发疯。

于是你只能选择 ------ 拷贝再新建一个组件,给它加个 V2 后缀,然后老的你也不敢删。

项目后期的结构大概就是:

css 复制代码
components/
├── Input.vue
├── InputV2.vue
├── InputWithTooltip.vue
├── InputWithValidation.vue
├── InputWithValidationV2.vue
└── ...

"为了让别人能维护我的代码,我决定不动它。"


5. 组件设计的核心,其实是抽象能力

我用三年才悟到一个道理:

Vue 组件设计的难点,不是语法、也不是封装,而是你有没有抽象问题的能力

举个例子:

你需要设计一个"搜索区域"组件,包含输入框 + 日期范围 + 搜索按钮。

新手写法:

html 复制代码
<SearchHeader
  :keyword="keyword"
  :startDate="start"
  :endDate="end"
  @search="handleSearch"
/>

页面需求一改,换成了下拉框 + 单选框怎么办?又封一个组件?

更好的设计是 ------ 提供slots 插槽 + 作用域插槽

html 复制代码
<!-- SearchHeader.vue -->
<template>
  <div class="search-header">
    <slot name="form" />
    <button @click="$emit('search')">搜索</button>
  </div>
</template>

<!-- 使用 -->
<SearchHeader @search="search">
  <template #form>
    <el-input v-model="keyword" placeholder="请输入关键词" />
    <el-date-picker v-model="range" type="daterange" />
  </template>
</SearchHeader>

把结构交给组件,把行为交给页面。组件不掌控一切,而是协作。


6. 那么组件怎么设计才对?

我总结出 3 条简单但有效的建议:

✅ 1. 明确组件职责:UI?交互?逻辑?

  • UI 组件只关心展示,比如按钮、标签、卡片;
  • 交互组件只封装用户操作,比如输入框、选择器;
  • 逻辑组件封装业务规则,比如筛选区、分页器。

别让一个组件又画 UI 又写逻辑还请求接口。


✅ 2. 精简 props 和 emit,只暴露"必需"的接口

  • 一个组件 props 超过 6 个,要小心;
  • 如果事件名不具备业务语义(比如 click),考虑抽象;
  • 不要用 ref 操作子组件的内部逻辑,那是反模式。

✅ 3. 使用 slots 替代"高度定制的 props 方案"

如果你发现你组件 props 变成这样:

html 复制代码
<SuperButton
  :label="'提交'"
  :icon="'plus'"
  :iconPosition="'left'"
  :styleType="'primary'"
  :loading="true"
/>

那它该用 slot 了:

html 复制代码
<SuperButton>
  <template #icon><PlusIcon /></template>
  提交
</SuperButton>

🙂

三年前我以为组件化是 Vue 最简单的部分,三年后我才意识到,它是最深、最难、最容易出坑的部分。

如果你也踩过以下这些坑:

  • 组件复用越写越复杂,别人都不敢用;
  • props 和事件像迷宫一样,维护成本极高;
  • UI 和逻辑耦合,改一点动全身;
  • 项目后期组件膨胀、技术债堆积如山;

别再让组件成为项目的"技术债"。你们也有遇到吗?

📌 你可以继续看我的系列文章

相关推荐
崔庆才丨静觅5 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60615 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了6 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅6 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅6 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅6 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment6 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅7 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊7 小时前
jwt介绍
前端
爱敲代码的小鱼7 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax