基于配置驱动的前端DSL架构实践

317 阅读6分钟

引用参考:抖音"哲玄前端"《大前端全栈实践》

概述

基于领域模型概念设计了一套配置驱动的前端架构模式,通过JSON Schema(核心)配置文件驱动页面生成,实现了高度可配置化的管理系统。该架构采用模型-项目继承设计,支持多项目、多领域的复杂业务场景,有效解决了传统前端开发中的70-80%的重复性工作和维护难题。

一、核心设计理念

1.1 配置驱动开发

通过一套标准化的配置数据即可驱动生成整个站点的页面组件,将"手写代码"转变为"配置生成",大幅提升开发效率。

1.2 模型继承机制

基础模型配置可被多个项目继承和扩展(面向对象思维),支持内容沉淀的同时保持定制化灵活性,遵循DRY原则。

1.3 组件化架构

基于Vue 3的响应式组件化架构,支持动态组件渲染和组合,确保良好的可维护性和扩展性。

1.4 领域分离

按业务领域组织模型配置,不同领域可派生出对应的项目类型,保持业务逻辑的清晰分离。

二、解决的核心痛点

2.1 重复性CRUD开发

传统痛点:每个业务模块都需要重复编写表格、搜索、增删改查的代码解决方案:通过schema配置自动生成完整的CRUD界面

// 一个简单的schema配置即可生成完整的用户管理界面
schemaConfig: {
  api: '/api/users',
  schema: {
    type: 'object',
    properties: {
      username: {
        type: 'string',
        label: '用户名',
        tableOption: { width: 150 },
        searchOption: { comType: 'input' }
      }
    }
  }
}

2.2 组件复用困难

传统痛点:不同页面的表格、搜索组件功能差异大,难以复用解决方案:通过tableOption、searchOption等标准化配置实现组件的灵活定制

2.3 状态管理复杂

传统痛点:搜索条件、分页状态、选中状态需要手动管理解决方案:自动处理URL参数与搜索默认值的映射,实现状态持久化

三、架构设计

3.1 整体架构流程

graph TD
    A[Model配置] --> B[Project继承]
    B --> C[BFF层处理]
    C --> D[Dashboard模板引擎]
    D --> E[页面组件渲染]
    E --> F[菜单系统]
    E --> G[路由管理]

四、核心技术实现

4.1 配置继承机制

项目的核心创新在于实现了灵活的配置继承机制,通过model/index.js处理三种操作:

model/
├── business/          # 电商领域
│   ├── model.js       # 基础模型配置
│   └── project/       # 具体项目配置
│       ├── pdd.js     # 拼多多项目
│       ├── jd.js      # 京东项目
│       └── taobao.js  # 淘宝项目
├── course/            # 课程领域
│   ├── model.js       # 基础模型配置
│   └── project/       # 具体项目配置
│       ├── bilibili.js # B站项目
│       └── douyin.js   # 抖音项目
└── index.js           # 模型加载器

实际案例:

// 配置继承处理逻辑
const projectExtendModel = (model, project) => {
  return _.mergeWith({}, model, project, (modelValue, projValue) => {
    if (Array.isArray(modelValue) && Array.isArray(projValue)) {
      // 处理修改(重载): project有的键值,model也有
      // 处理新增(拓展): project有的键值,model没有  
      // 处理保留(继承): model有的键值,project没有
      return mergeArraysByKey(modelValue, projValue)
    }
  })
}

4.2 动态组件渲染

Schema配置解析

通过schema.js实现配置数据的智能解析和转换:

// 基础模型 (model/business/model.js)
{
  menu: [{
    key: 'product',
    name: '商品管理',
    schemaConfig: { /* 基础配置 */ }
  }]
}

// 项目配置 (model/business/project/pdd.js)  
{
  name: '拼多多',
  menu: [{
    key: 'product',
    name: '拼多多商品管理',  // 修改:覆盖名称
  }, {
    key: 'client',           // 新增:添加客户管理
    name: '拼多多客户管理'
  }]
}

SchemaOption的清洗和过滤

// 配置分离和转换
const buildDtoSchema = (_schema, comName) => {
  const dtoSchema = { type: 'object', properties: {} }
  
  for (const key in _schema.properties) {
    const props = _schema.properties[key]
    if (props[`${comName}Option`]) {
      // 分离基础属性和组件配置
      let dtoProps = { option: props[`${comName}Option`] }
      dtoSchema.properties[key] = dtoProps
    }
  }
  return dtoSchema
}

4.3 路由和视图管理

视图组件架构

  • header-view: 头部菜单渲染,支持项目切换和多级菜单
  • sider-view: 侧边栏复合视图,支持嵌套路由和默认选中
  • schema-view: 配置驱动的CRUD页面核心组件
  • iframe-view: 外部页面嵌入支持

4.4 Schema配置解析与数据流转

graph TD
    A["原始Schema配置"] --> B["useSchema Hook"]
    B --> C["buildDtoSchema处理"]
    C --> D["tableSchema分离"]
    C --> E["searchSchema分离"]
    D --> F["Schema-Table组件"]
    E --> G["Schema-Search-Bar组件"]
    G --> H["动态组件渲染"]
    H --> I["SearchItemConfig映射"]
    I --> J["具体搜索组件<br/>(input/select/dynamicSelect)"]
    F --> K["表格列动态生成"]
    K --> L["数据格式化处理"]
    L --> M["分页和操作按钮"]

动态组件注册机制

// search-item-config.js - 组件配置映射
const SearchItemConfig = {
  input: { component: input },
  select: { component: select },
  dynamicSelect: { component: dynamicSelect },
  dateRange: { component: dateRange }
}

// 动态组件渲染 - schema-search-bar.vue
<component
  :is="SearchItemConfig[schemaItem.option?.comType]?.component"
  :schema-key="key"
  :schema="schemaItem"
  @loaded="handleChildLoaded"
/>

4.5 数据请求与状态管理流程

sequenceDiagram
    participant U as 用户操作
    participant S as Schema-Search-Bar
    participant SP as Search-Panel
    participant SV as Schema-View
    participant ST as Schema-Table
    participant API as 后端API
    
    U->>S: 输入搜索条件
    S->>SP: emit('search', searchValObj)
    SP->>SV: emit('search', searchValObj)
    SV->>ST: 更新apiParams
    ST->>API: GET /api/resource/list?params
    API-->>ST: 返回分页数据
    ST->>ST: buildTableData处理
    ST-->>U: 渲染表格数据

4.6 配置继承详细机制

graph TD
    A["基础model配置<br/>business/model.js"] --> B["项目配置<br/>business/project/pdd.js"]
    B --> C["projectExtendModel函数"]
    C --> D{"配置项Key匹配?"}
    D -->|是| E["修改/重载<br/>使用project配置覆盖"]
    D -->|否| F{"仅在project中存在?"}
    F -->|是| G["新增/扩展<br/>添加新的配置项"]
    F -->|否| H["保留/继承<br/>保持model原有配置"]
    E --> I["合并后的最终配置"]
    G --> I
    H --> I
    I --> J["传递给dashboard渲染"]

五、Schema配置标准

5.1 完整的配置案例对比

基础模型配置示例:

// model/business/model.js
module.exports = {
  name: '电商系统',
  menu: [{
    key: 'product',
    name: '商品管理',
    moduleType: 'schema',
    schemaConfig: {
      api: '/api/proj/product',
      schema: {
        type: 'object',
        properties: {
          product_name: {
            type: 'string',
            label: '商品名称',
            tableOption: { width: 200 },
            searchOption: {
              comType: 'dynamicSelect',
              api: '/api/proj/product_enum/list'
            }
          },
          price: {
            type: 'number',
            label: '价格',
            tableOption: { width: 200 },
            searchOption: {
              comType: 'select',
              enumList: [
                { label: '全部', value: -999 },
                { label: '¥39.9', value: 39.9 }
              ]
            }
          }
        }
      }
    }
  }]
}

项目继承配置示例:

// model/business/model.js
module.exports = {
  name: '电商系统',
  menu: [{
    key: 'product',
    name: '商品管理',
    moduleType: 'schema',
    schemaConfig: {
      api: '/api/proj/product',
      schema: {
        type: 'object',
        properties: {
          product_name: {
            type: 'string',
            label: '商品名称',
            tableOption: { width: 200 },
            searchOption: {
              comType: 'dynamicSelect',
              api: '/api/proj/product_enum/list'
            }
          },
          price: {
            type: 'number',
            label: '价格',
            tableOption: { width: 200 },
            searchOption: {
              comType: 'select',
              enumList: [
                { label: '全部', value: -999 },
                { label: '¥39.9', value: 39.9 }
              ]
            }
          }
        }
      }
    }
  }]
}

5.2 组件渲染架构图

graph TD
    A["dashboard主容器"] --> B["Header-View<br/>头部菜单"]
    A --> C["Main-Content<br/>主内容区域"]
    
    B --> D["项目切换<br/>ProjectStore"]
    B --> E["菜单渲染<br/>MenuStore"]
    
    C --> F["Schema-View<br/>配置驱动页面"]
    C --> G["Sider-View<br/>侧边栏复合视图"]
    C --> H["Iframe-View<br/>外部页面嵌入"]
    C --> I["Custom-View<br/>自定义页面"]
    
    F --> J["Search-Panel<br/>搜索面板"]
    F --> K["Table-Panel<br/>表格面板"]
    
    J --> L["Schema-Search-Bar<br/>动态搜索栏"]
    K --> M["Schema-Table<br/>动态表格"]
    
    L --> N["Input组件"]
    L --> O["Select组件"]
    L --> P["DynamicSelect组件"]
    L --> Q["DateRange组件"]
    L --> Z["...其他扩展组件"]
    
    M --> R["动态列渲染"]
    M --> S["操作按钮"]
    M --> T["分页组件"]

六、核心组件深度解析

6.1 Schema-Table组件技术实现

动态列渲染机制:

// schema-table.vue核心渲染逻辑
<template v-for="(schemaItem, key) in schema.properties">
  <el-table-column
    v-if="schemaItem.option.visible !== false"
    :key="key"
    :prop="key"
    :label="schemaItem.label"
    v-bind="schemaItem.option"  // 动态绑定所有配置
  />
</template>

数据预处理机制:

const buildTableData = (listData) => {
  return listData.map((rowData) => {
    for (const dKey in rowData) {
      const schemaItem = schema.value.properties[dKey]
      
      // 处理数字精度格式化
      if (schemaItem?.option?.toFixed) {
        rowData[dKey] = rowData[dKey].toFixed(schemaItem.option.toFixed)
      }
    }
    return rowData
  })
}

6.2 Schema-Search-Bar组件生命周期

sequenceDiagram
    participant SSB as Schema-Search-Bar
    participant SC as 子搜索组件
    participant SIC as SearchItemConfig
    
    SSB->>SIC: 根据comType获取组件
    SIC-->>SSB: 返回对应组件类
    SSB->>SC: 动态创建组件实例
    SC-->>SSB: emit('loaded')
    SSB->>SSB: childComLoadedCount++
    Note over SSB: 所有子组件加载完成后
    SSB->>SSB: emit('load', getValue())
    
    Note over SSB,SC: 用户操作阶段
    SSB->>SC: 用户点击搜索
    SC-->>SSB: getValue()获取当前值
    SSB->>SSB: emit('search', searchObj)

6.3 URL状态管理与持久化

状态注入机制:

// schema.js中的URL参数处理
const dtoSearchSchema = buildDtoSchema(configSchema, 'search')
for (const key in dtoSearchSchema.properties) {
  if (route.query[key] !== undefined) {
    // 从URL参数注入默认值,实现状态持久化
    dtoSearchSchema.properties[key].option.default = route.query[key]
  }
}

路由状态同步流程:

graph LR
    A["用户访问URL<br/>?proj_key=pdd&key=product&product_name=大前端实践课"] --> B["Route Query解析"]
    B --> C["Schema Hook处理"]
    C --> D["注入default值到Schema"]
    D --> E["渲染搜索组件默认值"]
    E --> F["自动执行搜索"]
    F --> G["显示对应结果"]
    
    H["用户修改搜索条件"] --> I["更新URL参数"]
    I --> J["状态持久化"]
    J --> K["刷新页面保持状态"]

七、架构优势与性能优化

7.1 组件缓存与性能优化

动态组件复用机制:

// SearchItemConfig组件单例模式
const SearchItemConfig = {
  input: { component: input },      // 组件实例复用
  select: { component: select },
  // 避免重复创建组件实例,提升渲染性能
}

按需加载与路由分割:

// entry.dashboard.js中的路由懒加载
routes.push({
  path: '/view/dashboard/schema',
  component: () => import('./complex-view/schema-view/schema-view.vue')
})

7.3 扩展性设计模式

插件化组件注册:

// 支持新组件类型的动态扩展
const SearchItemConfig = {
  // 原有组件
  input: { component: input },
  
  // 如果后续新增组件类型
  richText: { component: richTextEditor },
  imageUpload: { component: imageUploader },
  // 无需修改核心代码,只需扩展配置
}

八、实践案例与效果展示

8.1 多项目配置继承实例

以拼多多项目为例,展示完整的配置继承效果:

graph TD
    A["Business基础模型"] --> B["商品管理<br/>基础功能"]
    A --> C["订单管理<br/>基础功能"] 
    A --> D["客户管理<br/>基础功能"]
    
    E["拼多多项目配置"] --> F["商品管理(拼多多)<br/>继承+定制"]
    E --> G["客户管理(拼多多)<br/>新增功能"]
    E --> H["数据分析(拼多多)<br/>新增复合视图"]
    E --> I["信息查询<br/>新增iframe集成"]
    
    B --> F
    D --> G
    
    F --> J["最终页面:<br/>拼多多商品管理"]
    G --> K["最终页面:<br/>拼多多客户CRM"]
    H --> L["最终页面:<br/>数据分析仪表板"]
    I --> M["最终页面:<br/>百度搜索集成"]

8.2 实际配置效果对比

一份配置,多端复用:

// 同一份商品配置,在不同项目中的表现
// 淘宝项目:展示"淘宝商品管理"
// 京东项目:展示"京东商品管理" 
// 拼多多项目:展示"拼多多商品管理" + 额外的客户管理功能

总结

这套基于配置驱动的DSL架构通过以下核心实现:

  1. 配置即代码:将70-80%的重复性开发工作转化为配置编写
  1. 模型继承:基于面向对象思维的配置复用机制
  1. 组件化:高度解耦的动态组件渲染系统
  1. 标准化:基于JSON Schema的标准配置规范

需要注意的是,项目中许多关于设计的地方,可以多多反复观看,细细回味,如果当时有困惑,可以观看完整的视频后,回想设计的理念和思维,这个设计的思想值得用心去学习,如果当时不理解也没关系,总会有一刻融会贯通的感觉,流水不争先,争滔滔不绝,望大家一起加油。