本文围绕上一篇 基于 DSL 与领域模型的系统构建方案 5.2 中的组件拓展 描述动态组件拓展的具体实现。
动态组件的实现机制是一套通过配置+ 数据驱动 + 事件驱动来实现组件动态加载、渲染及交互的完整体系,其核心在于通过标准化的配置格式和灵活的事件机制,降低组件开发的重复工作,提升系统的可扩展性和可维护性。以下是对该机制的详细理解:
一、动态组件的核心设计理念
1. 配置驱动渲染
通过统一的配置文件(如 componentConfig)定义组件的结构、属性和行为,避免硬编码。例如,表单组件可通过 schema 解析数据并渲染表单项,实现 “一份数据渲染多种表单”(如搜索栏、新增 / 修改表单)。
2. 事件驱动交互
组件间通过事件通信,如点击按钮时抛出事件,由 schema-view 接收并触发对应组件的展示或数据更新。例如,“新增” 按钮抛出事件后,schema-view 调用表单组件并在提交后通知列表组件刷新数据。
3. 数据驱动开发
以数据为核心驱动组件逻辑,避免逻辑混乱。例如,从 dashboard 的 hook 开始处理数据,通过 Model 和 Schema 控制表单值和渲染结构。
二、动态组件的实现流程与机制
以新增表单组件create-form为例说明:
1. 组件配置与解析
-
配置目标: 通过组件配置和解析器,支持表单组件的动态生成。
-
配置结构:
schemaConfig: {
schema: {
type: 'object',
properties: {
product_name: {
...
createFormOption: {
comType: 'input',
default: '新课程'
}
},
price: {
createFormOption: {
comType: 'inputNumber'
}
},
inventory: {
...
createFormOption: {
comType: 'select',
enumList: [
{label: '100', value: 100},
{label: '1000', value: 1000},
{label: '10000', value: 10000}
]
}
}
},
required: ['product_name'] // 表单必填字段
},
componentConfig: {
createForm: {
title: '新增商品',
saveBtnText: '保存商品'
},
...
}
}
(1)componentConfig:用于配置所有的动态组件(如 createForm、componentA、componentB...),组件所拥有的字段(title、saveBtnText...)支持用户动态拓展;
(2)componentSchemaOption:createFormOption 用于解析数据渲染表单项,例如表单的字段类型、是否必填、默认值等,兼容 Element UI 原生属性。
- 配置解析:
// 取出 componentConfig
const { componentConfig } = schemaConfig;
// 构造动态组件数据
if(componentConfig && Object.keys(componentConfig).length > 0){
const dtoComponents = {};
for(const comName in componentConfig){
dtoComponents[comName] = {
schema: buildDtoSchema(configSchema, comName),
config: componentConfig[comName]
};
}
components.value = dtoComponents;
}
// 最终得到的数据结构为:
// components = {
// comA : { schema, config },
// comB : { schema, config },
// }
通过 schema(决定组件结构、组件内字段呈现方式)和 config(决定组件相关值)驱动渲染;
2. 组件加载机制
- 组件创建与管理:
新建components目录,用于存放各动态组件,如create-form、edit-form....,并将动态组件统一配置在 component-config.js 中。
-
事件触发加载:
动态组件需通过事件触发,而非直接展示(区别于搜索栏和表格的字段配置直接渲染)。
<template>
<el-row class="schema-view">
...
<table-panel ref="tablePanelRef" @operate="onTableOperate"></table-panel>
<component
:is="ComponentConfig[key]?.component"
v-for="(item,key) in components"
:key="key"
ref="comListRef"
@command="onComponentCommand"
></component>
</el-row>
</template>
...
import ComponentConfig from './components/component-config.js';
import { useSchema } from './hook/schema.js';
const {
api,
...
components
} = useSchema();
// table事件触发
const onTableOperate = ({btnConfig, rowData})=>{
const { eventKey } = btnConfig;
if(EventHandleMap[eventKey]){
EventHandleMap[eventKey]({btnConfig, rowData});
}
};
// table事件映射
const EventHandleMap = {
showComponent: showComponent
};
// 展示动态组件
function showComponent({ btnConfig, rowData }){
const { comName } = btnConfig.eventOption;
if(!comName) {
console.log('没配置组件名');
return;
};
const comRef = comListRef.value.find(item=>item.name === comName);
if(!comRef || typeof comRef.show !== 'function') {
console.log(`找不到组件:${comName}`);
return;
};
comRef.show(rowData);
};
核心逻辑:
(1)从 hook/schema.js 获取 API 和 components 配置,通过 provide/inject 实现组件间数据交互;
(2)通过 table-panel 组件的 onTableOperate 事件触发和 EventHandleMap 事件映射配合实现动态组件的渲染。
3. 事件映射与通信
-
事件驱动流程:
(1)按钮(如表格头部按钮、侧边栏按钮)配置事件(如
remove、showComponent);(2)
schema-view接收事件并根据eventKey调用对应方法。 如showComponent事件触发时,从eventOption获取组件名称,调用其show方法并传入数据rowData。 -
组件间协作:
schema与组件通过 “桥梁”(如search-panel、table-panel)连接,组件完成操作后返回事件通知其他组件(如列表刷新)。
三、动态组件的优势
-
优势:
- 减少重复开发:通过配置沉淀 80% 的重复工作,如表单渲染、事件处理逻辑;
- 灵活扩展:支持无限拓展组件(如增删改查、详情查看等),适配不同业务场景。
四、总结
动态组件的实现机制通过 “配置 + 事件 + 数据” 的设计,实现了组件的动态化、标准化和可扩展化,实现了改一改配置、调一调数据就能快速适配各业务场景,为复杂业务的前端开发提供了高效的解决方案。
引用:抖音“哲玄前端”,《全栈实践课》