动态组件库建设

动态组件扩展的核心价值:告别硬编码组件

传统开发中,每增加一个功能模块都需要:

  1. 硬编码组件引入:手动 import 并注册组件
  2. 维护成本激增:组件越多,代码越臃肿,修改牵一发动全身

而动态组件扩展设计通过 "配置驱动的组件管理" 彻底改变这一现状:

  • 配置即组件:通过 DSL 配置自动生成组件实例
  • 统一交互协议:所有组件遵循相同的生命周期和事件规范
  • 按需加载:只渲染配置中声明的组件,避免冗余

elpis 动态组件架构解析

第一层:组件注册中心(ComponentConfig)

component-config.js 作为组件注册中心,采用 "键值映射" 模式管理所有动态组件:

css 复制代码
// app/pages/dashboard/complex-view/schema-view/components/component-config.js
export const ComponentConfig = {
  createForm: {
    component: createForm,
  },
  editForm: {
    component: editForm,
  },
  detailPanel: {
    component: detailPanel,
  }
}

设计亮点

  1. 解耦组件与业务:组件实现与业务逻辑分离,通过 key 建立关联
  2. 扩展性极强:新增组件只需在 ComponentConfig 中注册,无需修改主逻辑

第二层:Schema 转换引擎(buildDtoSchema)

动态组件的核心难题是 "如何将通用 schema 转换为特定组件的配置" 。elpis 通过 buildDtoSchema 函数实现智能转换:

javascript 复制代码
// 核心转换逻辑
const buildDtoSchema = (_schema, comName) => {
  if (!_schema.properties) return {}
  const dtoSchema = {
    type: 'object',
    properties: {}
  }

  for (const key in _schema.properties) {
    const props = _schema.properties[key];
    // 关键:根据组件名提取对应配置
    if (props[`${comName}Option`]) {
      let dtoProps = {}
      // 提取非 Option 的通用属性
      for (const pKey in props) {
        if (pKey.indexOf('Option') < 0) {
          dtoProps[pKey] = props[pKey]
        }
      }
      // 合并组件特定配置
      dtoProps = Object.assign({}, dtoProps, {
        option: props[`${comName}Option`]
      })
      dtoSchema.properties[key] = dtoProps
    }
  }
  return dtoSchema
}

第三层:动态渲染与事件协调

schema-view.vue 中,通过 Vue 的动态组件机制实现组件的自动渲染和事件协调:

xml 复制代码
<template>
  <!-- 搜索面板 -->
  <search-panel @search="onSearch" />

  <!-- 数据表格 -->
  <table-panel @operate="onTableOperate" />

  <!-- 动态组件:核心实现 -->
  <component
    :is="ComponentConfig[key]?.component"
    v-for="(component, key) in components"
    :key="key"
    ref="comListRef"
    :component="component"
    @command="onComponentCommand"
  />
</template>

<script setup>
// 事件映射机制
const EventHandlerMap = {
  showComponent: showComponent,
}

// 根据 DSL 配置映射表格操作到组件显示
const onTableOperate = ({btnConfig, rowData}) => {
  const { eventKey } = btnConfig;
  if(EventHandlerMap[eventKey]) {
    EventHandlerMap[eventKey]({btnConfig, rowData})
  }
}

// 动态显示组件
function showComponent({btnConfig, rowData}) {
  const { comName } = btnConfig.eventOption;
  const component = comListRef.value.find(item => item.name === comName);
  if(component) {
    component.handleShow(rowData)
  }
}
</script>

设计精髓

  1. 数据传递标准化 :通过 component prop 传递 schema 和 config
  2. 事件冒泡机制:组件内部事件可向上传递,实现跨组件通信

具体组件实现:以 CreateForm 为例

动态组件需要遵循统一的接口规范,以 create-form.vue 为例:

xml 复制代码
<template>
  <el-drawer v-model="isShow" @close="handleHide">
    <template #header>
      <h3>{{ title }}</h3>
    </template>

    <!-- 核心:使用转换后的 schema 渲染表单 -->
    <schemaForm
      ref="schemaFormRef"
      :schema="components[name].schema"
    />

    <template #footer>
      <el-button @click="handleSave">{{ saveBtnText }}</el-button>
    </template>
  </el-drawer>
</template>

<script setup>
// 接收 DSL 数据
const { api, components } = inject('schemaViewData')
const name = ref('createForm')

// 统一接口实现
const handleShow = () => {
  const { config } = components.value[name.value]
  title.value = config.title
  saveBtnText.value = config.saveBtnText
  isShow.value = true
}

const handleSave = async () => {
  const formData = schemaFormRef.value.getFormData()
  await $curl({
    url: api.value,
    method: 'post',
    data: formData
  })
  // 通知父组件刷新数据
  emit('command', { event: 'loadTableData' })
}

// 暴露标准接口
defineExpose({
  name,
  handleShow,
  handleHide
})
</script>

核心特性

  1. 配置驱动:组件的标题、按钮文案等都来自 DSL 配置
  2. Schema 复用:直接使用转换后的 schema 渲染表单,无需重复定义
  3. 事件通信:通过 emit 向上传递事件,实现数据联动

DSL 配置到组件的完整链路

让我们通过一个完整示例,展示从 DSL 配置到组件渲染的全流程:

1. DSL 配置定义

json 复制代码
{
  "schemaConfig": {
    "api": "/api/user",
    "schema": {
      "type": "object",
      "properties": {
        "userName": {
          "type": "string",
          "label": "用户名",
          "createFormOption": {
            "comType": "input",
            "required": true,
            "placeholder": "请输入用户名"
          }
        }
      }
    },
    "tableConfig": {
      "headerButtons": [
        {
          "label": "新增用户",
          "eventKey": "showComponent",
          "eventOption": { "comName": "createForm" }
        }
      ]
    },
    "componentConfig": {
      "createForm": {
        "title": "新增用户",
        "saveBtnText": "确认新增"
      }
    }
  }
}

2. Schema 转换处理

arduino 复制代码
// useSchema Hook 中的处理
const { componentConfig } = sConfig;
const dtoComponents = {}

for(const key in componentConfig){
  dtoComponents[key] = {
    // 转换为组件专用 schema
    schema: buildDtoSchema(configSchema, key),
    // 组件配置
    config: componentConfig[key]
  }
}
components.value = dtoComponents

3. 动态组件渲染

xml 复制代码
<!-- 自动渲染 createForm 组件 -->
<component
  :is="ComponentConfig['createForm']?.component"
  :component="{
    schema: { /* 转换后的 createForm schema */ },
    config: { title: '新增用户', saveBtnText: '确认新增' }
  }"
/>

扩展新组件:从配置到实现

假设需要新增一个 "批量导入" 组件,整个流程如下:

1. 创建组件实现

xml 复制代码
<!-- batch-import.vue -->
<template>
  <el-dialog v-model="isShow">
    <el-upload :action="uploadUrl">
      <el-button>选择文件</el-button>
    </el-upload>
  </el-dialog>
</template>

<script setup>
const name = ref('batchImport')

const handleShow = () => {
  isShow.value = true
}

defineExpose({ name, handleShow, handleHide })
</script>

2. 注册到 ComponentConfig

css 复制代码
// component-config.js
import batchImport from './batch-import/batch-import.vue'

export const ComponentConfig = {
  createForm: { component: createForm },
  editForm: { component: editForm },
  detailPanel: { component: detailPanel },
  // 新增注册
  batchImport: { component: batchImport }
}

3. 配置 DSL

css 复制代码
{
  "tableConfig": {
    "headerButtons": [
      {
        "label": "批量导入",
        "eventKey": "showComponent",
        "eventOption": { "comName": "batchImport" }
      }
    ]
  },
  "componentConfig": {
    "batchImport": {
      "title": "批量导入用户",
      "uploadUrl": "/api/user/batch-import"
    }
  }
}

4. 自动生效

无需修改主逻辑代码,新组件即可自动参与渲染和事件处理。这就是动态组件扩展设计的威力所在。

展望:AI 时代的动态组件

随着 AI 技术发展,动态组件扩展设计将迎来新的可能:

  1. 智能组件生成:AI 根据业务描述自动生成组件代码和配置
  2. 自适应布局:根据用户行为动态调整组件排列和交互方式
  3. 个性化定制:为不同用户角色生成专属的组件组合

动态组件扩展设计不仅是技术架构的创新,更是开发思维的转变 ------ 从 "写代码实现功能" 到 "设计机制解决问题"。在 DSL 驱动的低代码时代,这种设计理念将成为构建可扩展、易维护系统的核心基石。

出处:《哲玄课堂-大前端全栈实践》

相关推荐
懒大王95272 小时前
uni-app + Vue3 开发展示 echarts 图表
前端·uni-app·echarts
yinuo2 小时前
Uni-App跨端实战:微信小程序WebView与H5通信全流程解析(01)
前端
xkroy2 小时前
ajax
前端·javascript·ajax
Yvonne爱编码2 小时前
AJAX入门-URL、参数查询、案例查询
前端·javascript·ajax
闲人编程3 小时前
前端形态与样式风格:从古典到现代的视觉语言演进
前端·css·状态模式·组件·js·风格·响应式
JudithHuang3 小时前
Mac版微信开发者工具登录二维码不显示问题解决方案
前端
Swift社区3 小时前
如何解决 Vue2 前端项目为何无法访问本地资源(chunk.js 加载一直 pending/转圈)
开发语言·前端·javascript
清风细雨_林木木3 小时前
Vue加载资源‘如图片’的“直接引入“方式和“request()“的区别
前端·javascript·vue.js