AI全栈实战 | 2.3-01 组件库实战:Element Plus 怎么搭出专业界面,过度封装为什么成维护负担

上篇回顾:2.2-02 对比了 Vue 可变数据与 React 不可变数据两种哲学。本篇讲 UI 组件库------后端转前端搭管理后台的「最后一公里」。你不需要从零写表单、表格、弹窗,用 Element Plus 或 Ant Design 能快速搭出专业界面。但「什么时候该封装业务组件、什么时候不该」是个重要的判断题。

一、开篇:从手写 CSS 到用组件库的转变

后端工程师第一次写管理后台,通常手写 HTML + CSS:

html 复制代码
<table>
  <tr><th>名称</th><th>操作</th></tr>
  <tr><td>Java</td><td><button>编辑</button></td></tr>
</table>

能用,但丑。加 CSS 美化要写半天,响应式、交互、校验全要手写。

用 Element Plus:

vue 复制代码
<el-table :data="tableData" stripe>
  <el-table-column prop="name" label="名称" />
  <el-table-column label="操作">
    <template #default="{ row }">
      <el-button @click="edit(row)">编辑</el-button>
    </template>
  </el-table-column>
</el-table>

自带样式、分页、排序、加载态。组件库的价值不是「好看」,而是「把常见的交互模式封装成可复用的积木」。

二、两大主流组件库

维度 Element Plus Ant Design
框架 Vue3 React(也有 Vue 版)
风格 简洁、中后台 专业、设计规范完善
生态 国内中后台主流 国际化、大厂出品
文档 中文友好 英文为主
适合 Vue 项目、国内业务 React 项目、国际化业务

选型建议:Vue 项目用 Element Plus,React 项目用 Ant Design。

三、按需引入:tree-shaking 的价值

3.1 全量引入的问题

javascript 复制代码
// 全量引入(不推荐)
import ElementPlus from 'element-plus';
import 'element-plus/dist/index.css';
app.use(ElementPlus);

打包体积 1MB+,大部分组件你没用到。

3.2 按需引入

javascript 复制代码
// Vite 配置自动按需引入
import AutoImport from 'unplugin-auto-import/vite';
import Components from 'unplugin-vue-components/vite';
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers';

export default {
  plugins: [
    AutoImport({ resolvers: [ElementPlusResolver()] }),
    Components({ resolvers: [ElementPlusResolver()] }),
  ]
};

模板里直接用 <el-button>,构建时自动只打包用到的组件。体积从 1MB 降到几十 KB。

四、三大高频场景的标准解法

4.1 表单校验

vue 复制代码
<template>
  <el-form :model="form" :rules="rules" ref="formRef">
    <el-form-item label="用户名" prop="username">
      <el-input v-model="form.username" />
    </el-form-item>
    <el-form-item label="邮箱" prop="email">
      <el-input v-model="form.email" />
    </el-form-item>
    <el-button @click="submit">提交</el-button>
  </el-form>
</template>

<script setup>
import { ref, reactive } from 'vue';

const formRef = ref();
const form = reactive({ username: '', email: '' });
const rules = {
  username: [
    { required: true, message: '请输入用户名', trigger: 'blur' },
    { min: 3, max: 20, message: '长度 3-20', trigger: 'blur' }
  ],
  email: [
    { required: true, message: '请输入邮箱', trigger: 'blur' },
    { type: 'email', message: '邮箱格式错误', trigger: 'blur' }
  ]
};

const submit = async () => {
  await formRef.value.validate();   // 校验通过才继续
  // 提交逻辑
};
</script>

4.2 表格分页

vue 复制代码
<template>
  <el-table :data="tableData" v-loading="loading">
    <el-table-column prop="name" label="名称" />
    <el-table-column prop="createdAt" label="创建时间" />
  </el-table>
  <el-pagination
    v-model:current-page="page.current"
    v-model:page-size="page.size"
    :total="page.total"
    @change="loadData"
  />
</template>

4.3 弹窗

vue 复制代码
<el-dialog v-model="visible" title="编辑">
  <el-form :model="form"><!-- 表单内容 --></el-form>
  <template #footer>
    <el-button @click="visible = false">取消</el-button>
    <el-button type="primary" @click="save">保存</el-button>
  </template>
</el-dialog>

五、主题定制与样式穿透

5.1 CSS 变量定制

css 复制代码
:root {
  --el-color-primary: #409eff;
  --el-color-success: #67c23a;
}

5.2 样式穿透(:deep)

组件库的内部元素有 scoped 样式,外部改不了。用 :deep:

vue 复制代码
<style scoped>
:deep(.el-table .cell) {
  font-size: 14px;
}
</style>

六、二次封装:什么时候该封、封什么

6.1 该封装的场景

  • 重复组合:同一个表格+搜索+分页组合用了 5 次以上
  • 业务规则:所有表单的邮箱字段都有相同的校验+提示
  • 统一风格:全系统的按钮尺寸/颜色要一致

6.2 不该封装的场景

  • 只用了 1~2 次:封装的代价比直接写还大
  • 需求不稳定:业务还在变,封装后改起来更麻烦
  • 过度抽象:把所有配置都做成 props,组件比原版还复杂

6.3 封装原则:开放封闭

vue 复制代码
<!-- 好的封装:固定业务部分,开放其余 -->
<template>
  <el-table :data="data" :columns="columns">
    <!-- 业务固定:分页、加载态内置 -->
    <!-- 业务可变:列配置、插槽透传 -->
  </el-table>
</template>

反面教材:把 el-table 的所有 props 都透传一遍,再加 20 个业务 props------这种封装比直接用 el-table 还复杂。

七、小结表

概念 价值 注意
按需引入 减小打包体积 用 unplugin 自动化
表单校验 减少手写校验逻辑 rules 写在 setup 里
表格分页 标准化列表交互 v-model 双向绑定页码
样式穿透 改组件库内部样式 用 :deep 不用 ::v-deep
二次封装 复用业务模式 不过度封装
相关推荐
对空六课1 小时前
动态按钮ID变化时的稳定埋点标识设计
前端·数据分析
福兮说1 小时前
URL 编码的七个坑:c++ 传到后端变空格、%25 套娃、截断 emoji 直接报错
开发语言·前端·javascript·node.js·url
吴声子夜歌1 小时前
Nginx应用与运维——Nginx Web服务应用实战(Python网站的搭建)
运维·前端·nginx
小的时候可菜了1 小时前
JS 异步的本质:从单线程到 thenable(一条进化链)
开发语言·前端·javascript
liangshanbo12151 小时前
Webpack vs Vite 底层原理高频追问的问题
前端·webpack·node.js
可乐鸡翅yeah_1 小时前
hls.js 配置 loadStartPosition,点播起始播放位置踩坑
开发语言·javascript·网络协议·ecmascript·m3u8在线
web打印社区2 小时前
Windows 网页静默打印设置步骤:客户端、防火墙与联调清单
开发语言·前端·javascript·chrome·pdf·ecmascript
恋猫de小郭2 小时前
KMP 又改了编译流程, Separate Compilation 禁止了 `commonMain` 的依赖穿透
android·前端·flutter
吴声子夜歌2 小时前
Nginx应用与运维——Nginx Web服务应用实战(静态文件服务器的搭建)
运维·前端·nginx