Skip to content
On this page

组件驱动架构

Grow Admin 通过组件驱动桥接层实现业务代码与具体 UI 组件库的解耦。业务模块只使用 Grow* 契约组件,组件库切换在宿主应用中统一配置。

设计理念:为什么要组件驱动?

传统做法的问题

vue
<!-- 业务代码直接绑定 Element Plus -->
<el-button type="primary">提交</el-button>

一旦遇到以下情况,成本陡增:

  • 公司设计规范要求换 Naive UI / Ant Design Vue
  • 不同子公司使用不同组件库,但共用同一套业务模块
  • 升级 major 版本时 API 大面积变更

全局搜索替换不仅费时,还容易漏改样式、指令、命令式 API(ElMessage 等)。

桥接层解决什么?

Grow Admin 在业务与 UI 库之间插入稳定契约层

业务 ──认识──▶ GrowButton ──映射──▶ ElButton / NButton / AButton
              (不变)              (可替换)
原则含义
只抽象共有能力84 个三库都有的组件,不虚构统一 API
切换在宿主业务包不安装、不感知具体 UI 库
局部可覆盖ComponentDriverProvider 应对特殊子树

带来的好处

  • 业务模块可发布为纯净 npm 包(peer 依赖 @grow-admin-rock/components
  • 切换 UI 库 ≈ 改宿主两处配置projectSetting + vite preset
  • 契约清单 = 兼容承诺,超出 84 个的由业务自行封装,不污染框架

为什么不做一个「超级 UI 组件库」?

大而全的二次封装往往:

  • Props 与原生库差异大,文档难维护
  • 新库特性滞后,成为瓶颈
  • 业务同学既要学 Grow API 又要学底层库

驱动映射重新发明组件更轻:Grow 组件本身是薄包装,主要做名称统一与驱动查找,行为尽量透传底层库。

三层结构

业务代码 / 业务模块(apps-login 等)

Grow* 契约组件(@grow-admin-rock/components)

组件驱动桥接层(@grow-admin-rock/component-driver)

具体驱动包(element-plus / naive-ui / ant-design-vue)

设计原则

  1. 业务模块只使用 Grow* 契约组件,禁止直接 import element-plus / naive-ui / ant-design-vue
  2. 组件库切换在宿主应用统一配置,业务模块无需关心底层实现
  3. 支持全局一种组件库 + 局部子树覆盖另一种(通过 ComponentDriverProvider

运行时 vs 构建时:为何要两处配置?

配置控制什么若不一致会怎样
projectSetting.componentLibrary运行时 Grow 组件映射到哪个驱动组件能渲染但行为怪异
vite.config preset构建时三方组件自动导入、样式预处理样式缺失、类型报错

运行时决定「画什么」,构建时决定「怎么打包」——双轨一致才能既切换自由又构建正确。

契约组件(Grow*)

@grow-admin-rock/components 定义了 RockComponent 枚举,涵盖三库共有的 84 个组件

typescript
export enum RockComponent {
  Button = 'Button',
  Input = 'Input',
  Select = 'Select',
  Form = 'Form',
  // ... 共 84 个(不含 Table 等三库非共有组件)
}

契约组件以 Grow 前缀全局注册,可直接在模板中使用:

vue
<template>
  <GrowButton type="primary">提交</GrowButton>
  <GrowInput v-model="value" placeholder="请输入" />
  <GrowSelect v-model="selected" :options="options" />
</template>

框架内置、无驱动映射的组件

组件说明
GrowIconify框架自实现 Iconify 封装,全局注册,无三库驱动映射

参考项目封装(不纳入契约层)

组件说明
GrowTable基于 vxe-table 的表格封装
GrowCubeTable基于 @antv/s2 的多维表格
GrowLocalePicker自定义语言切换器

若业务需要类似能力,请在 DesignCornerstone 中自行实现。

驱动加载流程

projectSetting.componentLibrary

sample/src/init-components-driver.ts   ← 动态 import 对应驱动包

driver.builder().enableAll()           ← 注册全部组件映射

AppContext.DriverComponentDictionary

componentsLib.onSetup → registerGrowComponent()  ← 全局注册 Grow* 组件

init-components-driver.ts 根据 projectSetting.componentLibrary 动态加载驱动:

typescript
const driverFactories = {
  [ComponentLibraryType.ElementPlus]: async () => {
    const { EPComponentDriver } = await import('@grow-admin-rock/component-driver-element-plus');
    // 绑定 Message / Notification / Dialog
    return () => EPComponentDriver.builder().enableAll();
  },
  // NaiveUI、AntDesignVue 同理...
};

驱动包对照

组件库驱动包映射组件数
Element Plus(默认)@grow-admin-rock/component-driver-element-plus84
Naive UI@grow-admin-rock/component-driver-naive84
Ant Design Vue@grow-admin-rock/component-driver-antdv84

三个驱动包已对齐全部共有契约组件,切换 componentLibrary 即可在同一套业务代码下切换 UI 库。

在 script 中获取驱动组件

typescript
import { useDriverComponent, RockComponent } from '@grow-admin-rock/components';

const Button = useDriverComponent(RockComponent.Button);

Builder API

驱动包提供 Builder 模式,可按需启用组件:

typescript
import { EPComponentDriver } from '@grow-admin-rock/component-driver-element-plus';

const driver = EPComponentDriver.builder()
  .enableButton()
  .enableInput()
  .enableDatePicker()
  .finish();

enableAll() 启用全部 84 个组件映射。

局部覆盖

全局默认一种 UI 库;个别页面需要另一种时,用 ComponentDriverProvider 包裹子树,并传入对应驱动包的 builder().enableXxx().finish()

vue
<script setup>
import { ComponentDriverProvider } from '@grow-admin-rock/component-driver';
import { NaiveComponentDriver } from '@grow-admin-rock/component-driver-naive';

const driver = NaiveComponentDriver.builder()
  .enableButton()
  .enableInput()
  .finish();
</script>

<template>
  <ComponentDriverProvider :driver="driver">
    <!-- 此区域内的 Grow* 组件走 Naive UI 驱动 -->
    <GrowButton>局部 Naive 按钮</GrowButton>
  </ComponentDriverProvider>
</template>

按需启用示例(Element Plus 只开按钮和日期选择器):

typescript
EPComponentDriver.builder()
  .enableButton()
  .enableDatePicker()
  .finish();

完整说明、三库 Builder 示例与注意事项见 局部覆盖组件库

开发规范

✅ 推荐❌ 禁止
模板中使用 <GrowButton> 等契约组件直接 import { ElButton } from 'element-plus'
通过 projectSetting.componentLibrary 切换库在业务模块内安装/切换驱动
特殊场景使用 ComponentDriverProvider绕过桥接层直接使用三方组件

完整规范见 开发规范

下一步