从列表页到规则引擎:一个组件封装过程中的前端认知进阶

你是不是也写过几十个中后台页面:

  • 搜索表单重复粘贴?
  • 状态字段手动判断?
  • 时间字段反复 format?
  • 页面跳转状态丢失?

看起来你"很会写",但其实你只是写得多。

这次我们来讲点硬的,不止封装功能,而是把一个列表页写成系统、抽成规则

🚩 一个最普通的中后台列表页

你熟得不能再熟的结构:

  • 搜索 + 表单
  • Tab 状态切换
  • 表格展示 + 分页
  • 编辑跳转 or 弹窗操作

以前我们怎么写?👇

vue 复制代码
<el-form>
  <el-input v-model="query.name" />
  <el-date-picker v-model="query.date" />
  <el-button @click="fetchData">搜索</el-button>
</el-form>

<el-table :data="tableData" />

写是能写,但每次都得重复处理这些:

  • query 对象手动构造
  • 表单字段手动初始化
  • 跳转回来状态丢了
  • loading、分页拼来拼去

🎯 我要的不是组件,而是规则系统

我希望的列表页只需要这样 👇

vue 复制代码
<SmartListView
  :form-rule="rule"
  :table-setup="tableSetup"
  :fetch-setup="fetchSetup"
  :tabs-static-list="tabsArr"
/>

剩下的交给组件:

✅ 表单结构自动渲染

✅ 默认值 + 时间字段自动处理

✅ 查询 / 分页 / 缓存集成

✅ Tab 切换自动记忆

✅ 自定义 slot 灵活扩展

🧱 设计重点拆解

1️⃣ 字段 schema + formModel 解耦

js 复制代码
formSetup = [
  {
    type: 'datePicker',
    field: 'updateDate',
    title: '时间范围',
    value: [new Date(), moment().add(30, 'days').toDate()],
    props: { type: 'range' }
  }
]

🔍 schema 控结构,formModel 控值

🧠 这就是典型的配置驱动,字段即规则。

2️⃣ fetchSetup 接管一切行为

js 复制代码
fetchSetup = {
  url: '/api/list',
  tabs: {
    tabFilterKey: 'status',
    formDefaults: {
      updateDate: [new Date(), moment().add(30, 'days').toDate()]
    }
  },
  timeAdapter: {
    updateDate: ['startTime', 'endTime']
  }
}

你不需要再 format时间字段,也不需要自己转换字段名了。

3️⃣ Tab 切换自动触发查询 + 状态缓存

  • 每个 Tab 对应一个搜索状态
  • 切换时恢复上一次状态
  • 再次挂载保留分页信息

这不就是"列表状态保持"的理想体验?

4️⃣ 搜索 + 清空统一封装

js 复制代码
@submit → getFormData() → processTimeAdapter() → fetch()
@clear  → resetFields + clearStorage

再也不写 search()、reset()、page = 1,这些都应该在组件内搞定!

🧨 实战踩坑回顾(是真的踩了)

setValue 后值又被清空?

👉 是 v-model 自动同步值导致的,得放在 this.$nextTick 后。

❌ formDefaults 被污染?

👉 记得 deepClone,Vue 响应式会污染原始值!

❌ tabsArr 是接口来的,但子组件不更新?

👉 用 :key="smartListKey" 让组件重新挂载。

🧠 软件工程视角的组件设计

🧩 设计模式:

模式 体现
策略模式 不同字段转换策略
模板方法 SmartListView 结构统一,slot 灵活插入
观察者模式 watch formRule 响应变更

🧱 工程思想更重要:

  • 字段结构统一
  • 状态行为集中管理
  • 页面逻辑降到最低

🧬 真正的统一方案:我把一页内容,封成了一份 schema

经过一轮又一轮封装、踩坑、重构,我最终把所有列表页的结构 ------ 表单、表格、请求、tabs ------ 都抽象成了一个 pageSchema 配置对象。

从此,页面不再是写出来的,而是「定义出来的」。

ini 复制代码
<SmartListView :schema="pageSchema" />

一行组件调用,搞定整个列表页。

✅ 我实现了这些功能:

  • form.fields → 自动渲染搜索表单 + 默认值填充
  • table.columns → 自动生成表格结构 + 权限 + 字典映射
  • fetch.url → 统一封装查询逻辑 + 时间字段转换 + loading 管理
  • tabs.options → 动态状态 Tab + 状态缓存 + 自动切换请求
  • schema.transform → 自动把字段从组件值映射到接口字段(如日期 range)

再复杂的业务,只需要维护一份 schema 配置。

🔥 现在新增一个列表页只需要:

css 复制代码
export const logPageSchema = {
  form: {{ fields: [...] }},
  table: {{ columns: [...] }},
  fetch: {{ url: '/api/logs' }},
};

以前你要写 300 行 Vue,现在你只需要写 30 行配置。

💬 配置是抽象能力

很多人一看到 schema 就说:

"太麻烦了,还不如直接写。"

但你要知道:

写代码是为了抽象,抽象是为了规模化。

v-model 和写 schema,不是谁轻松谁牛逼,而是:

谁能写出 更多人复用未来还能自演化 的页面,谁就赢了。

🎯 思考:你以为你在写页面,其实你在构建规则系统

封装 SmartListView 的过程,不是把逻辑揉进组件,而是把行为抽象成规则,把重复变成结构。

你在做的,不只是「减少代码量」,而是:

  • 让表单字段具备声明式规则性
  • 让页面状态具备可还原能力
  • 让组件逻辑具备平台通用性

SmartListView 渲染实现结构图

真正的高级工程感,是这样的:

  • 字段有定义中心
  • 状态有恢复机制
  • 表单有 schema 驱动
  • 页面不再"实现功能",而是"组合能力"

到这一步,你不再是"封装组件的人",而是:

在定义规则的人

在推动系统的人

在构建平台能力的人

总结一句话:页面≠代码堆砌,字段≠值输入,表单≠UI控件。

真正的前端能力,是让规则生成页面,而不是手写页面。

如果你也在封装列表页、踩坑表单 schema、搞不定状态回填------欢迎评论区分享,我们一起把组件写成系统,写成能力!

🏁 结语:页面 ≠ 技术,规则才是资产

页面能跑只是初级

页面清晰是中级

页面可控是高级

真正的高级前端,不写页面,而是定义页面怎么写。

欢迎留言交流 👉 你最近写的列表页,还在重复写啥?

相关推荐
艾恩小灰灰15 分钟前
深入理解CSS中的`transform-origin`属性
前端·javascript·css·html·web开发·origin·transform
ohMyGod_12337 分钟前
Vue如何获取Dom
前端·javascript·vue.js
蓉妹妹42 分钟前
React项目添加react-quill富文本编辑器,遇到的问题,比如hr标签丢失
前端·react.js·前端框架
码客前端1 小时前
css图片设为灰色
前端·javascript·css
艾恩小灰灰1 小时前
CSS中的`transform-style`属性:3D变换的秘密武器
前端·css·3d·css3·html5·web开发·transform-style
Captaincc1 小时前
AI coding的隐藏王者,悄悄融了2亿美金
前端·后端·ai编程
天天扭码1 小时前
一分钟解决一道算法题——矩阵置零
前端·算法·面试
抹茶san1 小时前
el-tabs频繁切换tab引发的数据渲染混淆
前端·vue.js·element
Captaincc1 小时前
关于MCP最值得看的一篇:MCP创造者聊MCP的起源、架构优势和未来
前端·mcp
小小小小宇1 小时前
记录老项目Vue 2使用VueUse
前端