elpis全栈框架的理解

61 阅读18分钟

前言

背景

在前端开发场景中,我们常需搭建各类中后台系统,功能看似多样,核心却多为 CRUD 页面。这类重复性开发不仅效率低下,对个人技术能力的提升也毫无帮助。
更关键的是,多数中后台系统仅存在细微差异 —— 以电商系统为例,首次开发可能包含商品管理、订单管理、会员管理与优惠管理模块,而后续需要开发新的电商系统,它的需求或许仅新增一个分销管理模块,基础功能并无本质变化。
我们这时候,往往重新开发或对旧项目进行 “魔改”,既浪费大量时间,又缺乏技术突破的价值。尤其在 AI 技术飞速发展的当下,若长期局限于这类低技术含量的业务开发,个人竞争力必然会持续下降。


如何解决此问题呢?

image.png

elpis 全栈框架里 —— 领域模型 + BFF Serve+Dashboard 模板页 为核心,构建一套可复用、高灵活的解决方案,让中后台开发从 “重复CRUD” 转向 “配置化搭建”。

领域模型是整个方案的 “蓝图引擎”:基于 JSON Schema 定义系统核心结构,不仅能描述基础功能模块(如订单管理、商品管理),还支持通过 “基类 + 子类” 的层级设计拓展个性化需求。比如电商场景中,“订单管理 + 商品管理” 可作为通用基类,电商 A 需新增的 “优惠券管理”、电商 B 需补充的 “分销管理”,则可作为子类灵活挂载。

BFF Serve 扮演 “桥梁与驱动” 的角色:提供 SSR 服务,实例化项目,API接口服务

Dashboard 模板页是 “可视化呈现终端”:无需单独开发页面,只需接收 BFF 层传递的领域模型配置,就能自动渲染出符合业务需求的系统界面。

举个实际例子:搭建电商 A 系统时,我们只需在领域模型中配置 “基类(订单管理 + 商品管理)+ 子类(优惠券管理)”;启动系统后,BFF Serve 会读取这份配置并同步给 Dashboard 模板页,最终呈现的电商 A 系统,就自带 “订单管理 + 商品管理 + 优惠券管理” 全套功能。搭建电商 B 系统同理,仅需调整领域模型为 “基类 + 子类(分销管理)”,即可快速生成包含分销功能的电商系统 —— 全程无需重写代码,仅靠配置领域模型,就能实现中后台系统的快速搭建。

里程碑一 基于 nodejs 实现服务端内核引擎

elpis 全栈框架的第一步,便是以 Node.js 构建 BFF Serve 层 —— 对我们而言,Node.js 具备天然的技术适配性,上手门槛更低。

但传统 Node 开发中,团队常陷入 “目录结构不统一”“编码规范难落地”“中间件使用无规则” 的困境:路由该放在哪个目录?服务逻辑与配置管理如何拆分?这些问题往往消耗大量沟通成本。因此,elpis 框架基于 Koa 封装了 elpis-core 引擎,通过预设规范与自动化能力,把开发者从基础搭建中解放出来,只需专注业务开发。

一、规范目录与自动化加载

elpis-core 引擎以 “约定优于配置” 为原则,定义了清晰的项目目录结构,所有模块按功能归类,无需团队反复协商存放位置:

elpis
  |-- app/                # 核心业务目录(按约定存放业务模块)
  |   |-- controller/     # 控制器层:处理请求、调用服务、返回响应
  |   |-- extend/         # 扩展层:全局能力扩展(如工具函数、API 增强)
  |   |-- middleware/     # 中间件层:局部中间件(如接口权限校验)
  |   |-- public/         # 静态资源层:存放静态文件
  |   |-- router/         # 路由层:定义“请求路径→控制器”映射关系
  |   |-- router-schema/  # 路由校验层:用 JSON-Schema 定义接口参数规则
  |   |-- service/        # 服务层:封装数据处理、第三方接口调用(供控制器复用)
  |   |-- middlewares.js  # 全局中间件配置:注册全链路生效的中间件
  |-- config/             # 配置目录:区分本地/测试/生产环境的差异化配置
  |-- elpis-core/         # 引擎内核:提供自动加载、环境适配等核心能力
  |   |-- loader/         # 加载器集合:对应各模块的自动化加载逻辑
  |   |   |-- config.js   # 配置加载器:自动加载 config 目录下的环境配置
  |   |   |-- controller.js # 控制器加载器:自动加载 controller 模块并挂载
  |   |   |-- extend.js   # 扩展加载器:自动加载 extend 层的全局能力
  |   |   |-- middleware.js # 中间件加载器:自动加载 middleware 模块
  |   |   |-- router-schema.js # 路由校验加载器:自动加载接口参数校验规则
  |   |   |-- router.js   # 路由加载器:自动加载路由映射关系
  |   |   |-- service.js  # 服务加载器:自动加载 service 层的业务逻辑
  |   |-- env.js          # 环境判断工具:快速区分本地/测试/生产环境
  |   |-- index.js        # 引擎入口:整合所有加载器、初始化 Koa 实例
  |-- index.js            # 项目入口:一键启动 elpis 引擎

其中,加载器(loader)  是自动化的核心:每个模块(如 controller、service、router)都对应专属加载器,加载器会主动遍历目标目录、解析文件内容,并将模块自动挂载到 Koa 实例上。以控制器加载为例,整个流程完全无需手动干预:

  1. 读取 elpis/app/controller/**/*.js 下所有文件;
  2. 提取文件名称与路径,如 app/controller/custom-module/custom-controller.js
  3. 截取路径核心部分,得到 custom-module/custom-controller
  4. 将路径中的 “-” 转为驼峰式命名,即 customModule.customController
  5. 自动挂载到内存 app 对象中

从启动到请求的全链路优化

固定启动顺序,避免模块依赖混乱

elpis-core 引擎定义了严格的模块启动顺序,确保各模块按依赖关系加载,避免 “未定义” 错误:

配置环境 → 加载配置 → 扩展加载 → 中间件加载 → 服务加载 → 控制器加载 → 全局中间件注册 → 路由模式加载 → 路由注册

从环境判断到路由生效,每一步都由引擎自动执行,开发者无需手动管理加载顺序。

环境与路由的精细化管理

  • 环境配置:通过 elpis-core/env.js 实现环境变量统一管理,可快速区分本地开发、测试、生产环境,配置文件按环境拆分后,加载器会自动匹配当前环境并加载对应配置;
  • 路由管理:结合 JSON-Schema 定义路由参数规则,不仅能自动校验请求参数合法性,还能让接口文档与代码逻辑保持一致,减少 “文档与实际接口不匹配” 的问题;
  • 请求响应链路:通过 “路由→控制器→服务→中间件” 的链路设计,实现请求处理的解耦:路由负责映射路径,控制器负责请求分发,服务负责业务逻辑,中间件负责通用能力(如权限校验、日志记录)。

可扩展的生态能力

elpis-core 引擎支持自定义插件与功能扩展:

  • 插件机制:可接入多个自定义插件,满足个性化业务需求(如日志插件、监控插件);
  • 功能扩展:通过 extend 层可扩展全局能力,如新增工具函数、增强 API 响应格式等,扩展内容由加载器自动生效,无需修改引擎核心代码。

里程碑二 基于 webpack5 完成工程化建设

作为 elpis 全栈框架的第二步,本阶段核心是为前端 Dashboard 模板页搭建工程化基础,通过拆分 webpack 配置实现 “环境差异化构建”,同时适配服务端渲染与 Vue3 技术栈需求。

一、配置拆分:按环境解耦,兼顾开发与生产

将 webpack 配置拆分为三类,通过 dev.js 和 prod.js 分别调用,实现不同环境的自动化构建:

  • webpack.base.js:基础配置(基类),定义入口、loader、插件、分包策略等通用逻辑,避免重复编码;
  • webpack.dev.js:开发环境配置,侧重热更新(HMR)、SourceMap 等提升开发效率的能力;
  • webpack.prod.js:生产环境配置,聚焦打包优化(多线程、资源压缩、缓存清理),保障线上性能。

二、核心配置实现:从入口到优化的全链路适配

1. 基础配置(webpack.base.js):适配服务端渲染的核心逻辑

(1)入口处理:动态识别多页面入口

区别于普通前端项目的固定入口,框架通过 glob 匹配 app/pages/**/entry.*.js(如 entry.dashboard.js),自动生成入口配置与 HTML 模板,适配服务端渲染场景(用户访问 http://ip:port/view/xxxx 时,服务端返回主页面,其他页面由 Vue Router 渲染):

js

// 1. 匹配所有 entry.xx.js 入口文件
const elpisEntryList = path.resolve(__dirname, "../../pages/**/entry.*.js");
// 2. 构造入口与 HTML 模板
glob.sync(elpisEntryList).forEach(file => {
  const entryName = path.basename(file, ".js");
  // 入口配置:key 为入口名,value 为文件路径
  elpisPageEntries[entryName] = file;
  // HTML 模板:指定输出路径、模板文件与注入的代码块
  elpisHtmlWebpackPluginList.push(new HtmlWebpackPlugin({
    filename: path.resolve(process.cwd(), "./app/public/dist/", `.tpl`),
    template: path.resolve(__dirname, "../../view/entry.tpl"),
    chunks: [entryName]
  }));
});
// 最终入口配置
entry: elpisPageEntries,

(2)loader 配置:适配 Vue3 + Less 技术栈

针对 Vue 单文件组件、ES6 语法、样式与静态资源,配置专属 loader,确保代码正常解析:

rules: [
  { test: /.vue$/, use: "vue-loader" }, // 解析 Vue 文件
  { test: /.js$/, include: [pages目录], use: "babel-loader" }, // 转译 ES6
  { test: /.(png|jpg|gif)$/, use: { loader: "url-loader", options: { limit: 300 } } }, // 小图转 base64
  { test: /.css$/, use: ["style-loader", "css-loader"] }, // 解析 CSS
  { test: /.less$/, use: ["style-loader", "css-loader", "less-loader"] }, // 解析 Less
  { test: /.(eot|woff|woff2|svg|ttf)$/, use: "file-loader" } // 解析字体
]

(3)插件与优化:保障基础功能与性能

  • 插件:配置 VueLoaderPlugin(解析 Vue 组件)、DefinePlugin(定义 Vue 全局常量,如禁用生产环境调试工具),并注入动态生成的 HTML 模板;

  • 分包策略:通过 splitChunks 将代码分为三类,利用浏览器缓存减少重复加载:

    • vendor:第三方依赖(如 Vue、Vue Router),改动频率低;
    • common:业务公共组件(如 common/widgets 目录),被引用 ≥2 次即抽取;
    • entry.{page}:页面私有业务代码,改动频率高。
2. 开发环境配置(webpack.dev.js):聚焦开发效率

核心实现三项能力:

  • HMR 热更新:在入口中注入 HMR 客户端,结合 HotModuleReplacementPlugin,实现代码改动后局部更新,无需刷新页面;
  • SourceMap:配置 eval-cheap-module-source-map,快速定位错误且保障构建速度;
  • 输出规范:指定开发环境输出目录(./app/public/dist/dev/),统一资源路径格式。
3. 生产环境配置(webpack.prod.js):聚焦线上性能

通过多插件组合实现优化:

  • 多线程打包:使用 HappyPack 开启多线程(线程数 = CPU 核心数),并行处理 JS 与 CSS,提升打包速度;
  • 资源压缩TerserWebpackPlugin 压缩 JS(删除 console.log)、CSSMinimizerPlugin 压缩 CSS,减少资源体积;
  • 缓存清理CleanWebpackPlugin 打包前清空 public/dist 目录,避免旧资源残留;
  • CSS 提取MiniCssExtractPlugin 将 CSS 从 JS 中提取为单独文件,支持缓存复用。

三、页面入口逻辑:衔接 Vue 应用初始化

框架约定 app/pages 目录下的核心文件,实现 Vue 应用的自动化挂载:

  • boot.js:类比普通 Vue 项目的 main.js,负责初始化 Vue 实例;
  • entry.dashboard.js(如 app/pages/dashboard/ 下):聚合路由配置与待挂载组件,调用 boot(dashboard, { routes }) 完成应用启动。

里程碑三 基于 vue3 完成领域模型架构建设

作为 elpis 全栈框架解决前端 CRUD 重复开发的核心环节,本阶段聚焦 “用配置驱动页面”—— 基于领域模型 DSL 与 JSON Schema 定义页面结构,让中后台系统从 “手动编码开发” 转向 “配置化渲染”,彻底摆脱重复编写 CRUD 页面的低效模式。

一、核心选择:为何用 JSON Schema 承载领域模型?

选择 JSON Schema 作为领域模型的配置格式,核心源于其三大优势:

  1. 领域适配性强:作为特定领域的标准 JSON 格式,可精准描述中后台页面的 “字段属性、交互逻辑、组件关联”,比如一个 “商品价格” 字段,能同时定义其数据类型(number)、校验规则(30≤价格≤1000)、表格展示格式(保留 2 位小数)、表单组件类型(数字输入框);
  2. 校验能力完善:可结合 AJV(Another JSON Schema Validator)工具,自动校验前端输入数据与后端返回数据的合法性,减少手动编写校验逻辑的工作量;
  3. 扩展性灵活:支持嵌套配置、条件渲染等复杂场景,既能覆盖基础 CRUD 页面,也能适配带个性化交互的定制页面,无需修改框架核心逻辑。

二、领域模型架构:页面的 “配置化蓝图”

elpis 框架通过 JSON Schema 将中后台系统拆解为 “导航菜单 + 动态页面” 两大核心板块,每个板块的功能与交互均由配置定义,无需编写重复代码:

1. 整体架构拆解

  • 导航菜单:定义系统的模块结构,支持多级菜单(如 “商品管理”“订单管理”),并关联对应的页面类型(Schema 渲染页 /iframe 嵌入页 / 自定义页);

  • 动态页面:分为三类核心页面,覆盖中后台 90% 以上的场景:

    • schema-view:核心 CRUD 页面,由 “搜索栏(search-bar)+ 表格(table)” 组成,表格关联 “新增 / 编辑 / 详情 / 删除” 等弹窗交互;
    • iframe:嵌入外部页面(如第三方数据报表、旧系统页面),仅需配置嵌入路径;
    • custom:用户自定义页面,支持关联自定义 Vue 组件路径,满足特殊交互需求。

2. 核心配置示例(以电商系统 “商品管理” 为例)

通过一份 JSON 配置,可完整定义 “商品管理” 模块的菜单、表格字段、搜索组件、表单交互,示例如下:

js

module.exports = {
  model: 'dashboard',       // 模型标识,关联系统整体配置
  name: '电商系统',         // 系统名称,用于页面标题展示
  menu: [                   // 导航菜单配置
    {
      key: 'product',       // 菜单唯一标识
      name: '商品管理',     // 菜单显示名称
      menuType: 'module',   // 菜单类型(模块级)
      moduleType: 'schema', // 页面类型(Schema 渲染页)
      schemaConfig: {       // Schema 页面核心配置
        api: '/api/proj/product', // CRUD 接口地址(增删改查统一关联)
        schema: {           // 字段属性定义(覆盖表格、表单、搜索栏)
          type: 'object',
          properties: {
            // 商品ID:表格展示、编辑时禁用输入
            product_id: {
              type: 'string',
              label: '商品ID',
              tableOption: { width: 300, 'show-overflow-tooltip': true },
              editFormOption: { comType: 'input', disabled: true }
            },
            // 商品名称:支持动态下拉搜索、表单输入校验(3-20字)
            product_name: {
              type: 'string',
              label: '商品名称',
              maxLength: 20,
              minLength: 3,
              searchOption: { comType: 'dynamicSelect', api: '/api/proj/product_enum/list' },
              createFormOption: { comType: 'input', default: '哲玄新课程' }
            },
            // 价格:数值范围校验(30-1000)、表格保留2位小数、下拉搜索
            price: {
              type: 'number',
              label: '价格',
              maximum: 1000,
              minimum: 30,
              tableOption: { width: 200, toFixed: 2 },
              searchOption: { comType: 'select', enumList: [{ label: '¥39.0', value: 39.0 }, ...] },
              createFormOption: { comType: 'inputNumber' }
            },
            // 其他字段(库存、创建时间):略...
          },
          required: ['product_name'] // 表单必填字段
        },
        tableConfig: {       // 表格交互配置(按钮、操作逻辑)
          headerButtons: [{  // 表格顶部按钮(新增商品)
            label: '添加商品',
            eventKey: 'showComponent', // 触发事件(显示弹窗)
            eventOption: { comName: 'createForm' } // 弹窗类型(新增表单)
          }],
          rowButtons: [      // 表格行操作按钮(详情、修改、删除)
            { label: '详情', eventKey: 'showComponent', eventOption: { comName: 'detailPanel' } },
            { label: '修改', eventKey: 'showComponent', eventOption: { comName: 'editForm' } },
            { label: '删除', eventKey: 'remove', eventOption: { params: { product_id: 'schema::product_id' } } }
          ]
        },
        componentConfig: {   // 弹窗组件配置(标题、按钮文本)
          createForm: { title: '添加商品', saveBtnText: '保存' },
          editForm: { mainKey: 'product_id', title: '编辑商品' },
          detailPanel: { mainKey: 'product_id', title: '商品详情' }
        }
      }
    },
    // 其他菜单(订单管理-自定义页、客户管理-自定义页):略...
  ]
}

三、配置渲染流程:从 “配置” 到 “页面” 的全链路实现

前端基于 Vue3 实现配置的自动化渲染,核心分为 “配置获取” 与 “页面渲染” 两步,全程无需手动编写页面代码:

1. 第一步:配置获取(衔接 BFF Serve 层)

领域模型配置由 BFF Serve 层统一管理,前端通过接口获取配置并缓存,确保多页面共享数据:

  1. 接口请求:在系统入口(如 dashboard.vue)调用 BFF 层的 /api/project 接口,传入项目标识(proj_key),获取包含 “菜单、页面、字段” 的完整配置;
  2. 状态缓存:将配置存入 Vuex/Pinia 状态管理(如 menuStore 存储菜单配置、projectStore 存储项目信息),避免重复请求;
  3. 配置分发:路由切换时,从状态管理中读取当前菜单对应的配置,传递给动态渲染组件。

2. 第二步:页面渲染(分场景适配)

根据配置中的 moduleType(页面类型),前端自动匹配渲染逻辑,实现 “一套代码适配多场景”:

(1)schema-view 渲染(核心 CRUD 页面)

通过自定义 Hook useSchema 解析配置,将 JSON Schema 拆分为 “表格配置、搜索配置、组件配置”,再结合 Vue 动态组件渲染页面:

  • 配置解析useSchema 钩子接收 schemaConfig,提取 api(接口地址)、tableSchema(表格字段)、searchSchema(搜索字段)、components(弹窗组件);

  • 组件渲染

    • 搜索栏:根据 searchSchema 动态生成组件(输入框、下拉框、日期范围选择器),并绑定校验规则;
    • 表格:基于 tableSchema 渲染列,结合 tableConfig 渲染顶部 / 行操作按钮,点击按钮时触发 eventKey 对应的逻辑(如显示弹窗、调用删除接口);
    • 弹窗:根据 componentConfig 渲染 “新增 / 编辑 / 详情” 弹窗,自动关联表单字段与校验规则。

(2)iframe 渲染(嵌入外部页面)

  1. 从菜单配置中读取 iframe 类型对应的嵌入路径(如 customConfig.path);
  2. 在 Vue 模板中使用 <iframe> 标签,动态绑定 src 属性为嵌入路径,实现外部页面的无缝嵌入。

(3)custom 渲染(自定义页面)

  1. 配置中指定自定义页面的 Vue 组件路径(如 customConfig.path);
  2. 通过 Vue3 的异步加载组件,在路由对应的页面中渲染,满足个性化交互需求(如复杂表单、数据可视化)。

3. 核心渲染代码示例(dashboard.vue 入口)

<template>
  <el-config-provider :locale="zhCn">
    <!-- 头部导航(渲染菜单) -->
    <header-view :proj-name="projName" @menu-select="onMenuSelect">
      <!-- 动态路由出口(渲染对应页面) -->
      <template #main-content>
        <router-view></router-view>
      </template>
    </header-view>
  </el-config-provider>
</template>

<script setup>
import { ref, onMounted } from "vue";
import { useRouter, useRoute } from "vue-router";
import { useMenuStore } from "$elpisStore/menu"; // 菜单状态管理
import { useProjectStore } from "$elpisStore/project"; // 项目状态管理
import $curl from "$elpisCommon/curl.js"; // 接口请求工具

const router = useRouter();
const route = useRoute();
const menuStore = useMenuStore();
const projectStore = useProjectStore();
const projName = ref("");

// 初始化:获取配置并缓存
onMounted(async () => {
  await getProjectConfig();
});

// 调用 BFF 接口获取领域模型配置
async function getProjectConfig() {
  const res = await $curl({
    method: "get",
    url: "/api/project",
    query: { proj_key: route.query.proj_key } // 项目标识
  });
  if (res?.success && res.data) {
    const { name, menu } = res.data;
    projName.value = name;
    menuStore.setMenuList(menu); // 缓存菜单配置
  }
}

// 菜单点击:切换路由,匹配对应页面
const onMenuSelect = (menuItem) => {
  const { key, moduleType, customConfig } = menuItem;
  // 路由路径映射(根据页面类型匹配)
  const pathMap = {
    schema: "/schema",    // Schema 渲染页路径
    iframe: "/iframe",    // iframe 嵌入页路径
    custom: customConfig?.path // 自定义页路径
  };
  // 跳转路由(携带项目标识、菜单标识)
  router.push({
    path: `/view/dashboard`,
    query: { key, proj_key: route.query.proj_key }
  });
};
</script>

里程碑四 基于 vue3 完成动态组件库建设

上一阶段通过 DSL 配置实现了 schema-view 页面自动化渲染,本阶段核心是构建配置驱动的动态组件库,用 JsonSchema 统一描述组件结构与逻辑

一、解决三大关键问题

  1. 配置体系:用一份 jsonSchema 覆盖 “组件是什么、何时触发、包含什么、如何交互”;
  2. 渲染通信:实现三层组件(容器→中间件→控件)解耦与跨层级联动。

二、配置体系:四层结构定义组件全生命周期

基于 “单一数据源” 思想,在领域模型基础上扩展配置,确保框架可自动识别逻辑:

  1. 组件元信息(componentConfig):定义 “是什么”

描述组件基础属性,如标题、按钮文本、主键(用于数据回显):

schemaConfig: {
  componentConfig: {
    createForm: { title: '添加商品', saveBtnText: '保存' },
    editForm: { mainKey: 'product_id', title: '编辑商品' },
    detailPanel: { mainKey: 'product_id', title: '商品详情' }
  }
}
  1. 触发规则(tableConfig):定义触发

在表格按钮中配置事件,通过 eventKey 关联组件,统一交互逻辑:

schemaConfig: {
  tableConfig: {
    headerButtons: [{ // 表头按钮→新增组件
      label: '添加商品',
      eventKey: 'showComponent', 
      eventOption: { comName: 'createForm' }
    }],
    rowButtons: [ // 行按钮→详情/编辑/删除
      { label: '详情', eventKey: 'showComponent', eventOption: { comName: 'detailPanel' } },
      { label: '删除', eventKey: 'remove', eventOption: { params: { product_id: 'schema::product_id' } } }
    ]
  }
}
  1. 字段属性(properties):定义 “包含什么”

为字段配置不同组件中的表现(控件类型、校验规则),通过 xxxOption 区分场景:

schemaConfig: {
  properties: {
    product_name: {
      type: 'string',
      label: '商品名称',
      maxLength: 20, // 全局校验
      searchOption: { comType: 'dynamicSelect', api: '/api/proj/enum' }, // 搜索框
      createFormOption: { comType: 'input', default: '哲玄新课程' } // 新增表单
    },
    price: {
      type: 'number',
      minimum: 30,
      tableOption: { width: 200, toFixed: 2 }, // 表格显示
      createFormOption: { comType: 'inputNumber' } // 表单控件
    }
  }
}
  1. API 交互:定义 “如何通信” 遵循 REST 规范,基础 API 统一配置,框架自动映射 HTTP 方法(新增→POST、删除→DELETE),动态参数用 schema::xxx 取表格行数据。

三、分层渲染:三层结构解耦加载 按职责拆分组件,用 Vue 动态组件实现自动化渲染,便于扩展:

1. 目录结构

plaintext

src/
├── pages/schema-view/       # 顶层容器:管理状态与事件
│   ├── schema-view.vue
│   └── components/          # 中间层:预设组件(新增/编辑/详情)
│       ├── create-form/
│       └── component-config.js # 组件映射(comName→组件路径)
└── widgets/schema-form/     # 底层:字段控件(输入框/下拉框)
    ├── schema-form.vue      # 控件容器
    └── components/          # 具体控件

2. 核心渲染逻辑(schema-view.vue)

vue

<template>
  <!-- 表格+搜索栏(复用前序实现) -->
  <el-table>...</el-table>

  <!-- 动态渲染中间层组件 -->
  <component
    v-for="(com, comName) in components"
    :key="comName"
    :is="ComponentMap[comName]"
    :schema="com.schema"
    :api="api"
    @command="handleComCommand" <!-- 监听组件事件如保存成功-->
  />
</template>

<script setup>
import { useSchema } from "@/hooks/schema.js";
import ComponentMap from "./components/component-config.js";

const { api, components } = useSchema();
// 处理组件事件(如保存后刷新表格)
const handleComCommand = ({ event }) => {
  if (event === "saveSuccess") emit("refreshTable");
};
</script>

四、通信机制:高效跨层级联动

  1. 数据向下:顶层用 provide 提供 API、配置,中间层 / 底层用 inject 直接获取,避免 props 层层传递;
  2. 事件向上:中间层通过 emit 触发事件(如 saveSuccess),顶层监听后执行逻辑(如刷新表格);
  3. 组件调用:顶层用 ref 获取中间层组件实例,主动调用方法(如编辑时回显数据)。

里程碑五 完成 elpis-npm 包分离并发布

本阶段核心目标是实现 “核心 - 业务” 彻底解耦:将框架核心能力(elpis-core、webpack 工程化、基础组件)封装为elpis npm 包,业务侧仅需安装该包,按约定目录开发即可,无需关注底层配置,真正实现 “聚焦业务、核心托管”。

elpis-core:自动扫描业务代码,实现 “业务模块零配置挂载”

为各加载器(controller、service 等)新增业务层扫描逻辑,自动识别业务目录下的模块并挂载到框架实例,无需手动注册。
控制器加载器为例:

// elpis-core/loader/controllerLoader.js
const path = require('path');
const glob = require('glob');

function loadController(app) {
  // 1. 加载框架核心控制器(原有逻辑)
  const coreControllerPath = path.resolve(__dirname, '../app/controller');
  glob.sync(`${coreControllerPath}/**/*.js`).forEach(file => handlerFile(file, app));

  // 2. 新增:扫描业务层控制器(约定业务路径 app/controller)
  const businessControllerPath = path.resolve(app.businessPath, './controller');
  const businessFileList = glob.sync(`${businessControllerPath}/**/*.js`);
  businessFileList.forEach(file => handlerFile(file, app));
  console.log(`-- [elpis] loaded ${businessFileList.length} business controllers --`);
}

// 统一挂载逻辑:核心与业务模块共用
function handlerFile(file, app) {
  const controller = require(file);
  const moduleName = path.dirname(file).split(path.sep).pop();
  if (!app.controller[moduleName]) app.controller[moduleName] = {};
  app.controller[moduleName][path.basename(file, '.js')] = controller;
}

module.exports = loadController;

同时改造elpis-core/index.js,新增业务全局中间件注册

// elpis-core/index.js
function start(options = {}) {
  const app = new Koa();
  app.businessPath = options.businessPath || process.cwd() + '/app'; // 约定业务路径

  // 注册业务全局中间件(约定业务路径 middleware.js)
  try {
    require(`${app.businessPath}/middleware.js`)(app);
    console.log(`-- [elpis] loaded business global middleware --`);
  } catch (error) {
    console.log(`[elpis] no business middleware, skip`);
  }

  // 加载核心+业务模块(controller、service等)
  loadController(app);
  loadService(app);
  app.listen(options.port || 3000);
  return app;
}

module.exports = { start };

2. webpack 配置:自动扫描业务页面,实现 “业务入口零配置打包”

webpack.base.js/dev.js/prod.js改造,新增业务层入口扫描,自动将业务页面纳入打包流程:

// webpack/base.js
const path = require('path');
const glob = require('glob');
const HtmlWebpackPlugin = require('html-webpack-plugin');

// 1. 初始化业务入口与HTML插件容器
const businessEntries = {};
const businessHtmlPlugins = [];

// 2. 扫描业务层入口(约定 app/pages/**/entry.*.js)
const businessEntryList = path.resolve(process.cwd(), './app/pages/**/entry.*.js');
glob.sync(businessEntryList).forEach(file => {
  const entryName = path.basename(file, '.js').replace('entry.', '');
  // 配置入口
  businessEntries[entryName] = file;
  // 配置HTML模板
  businessHtmlPlugins.push(new HtmlWebpackPlugin({
    filename: `${entryName}.html`,
    template: path.resolve(__dirname, '../template/entry.tpl'),
    chunks: [entryName]
  }));
});

// 3. 合并核心与业务配置(最终导出)
module.exports = {
  entry: { ...coreEntries, ...businessEntries }, // 合并入口
  plugins: [...corePlugins, ...businessHtmlPlugins], // 合并插件
  // 其他基础配置(loader、resolve等不变)
};

3. 框架入口 index.js:封装核心 API,对外暴露标准化能力

将框架核心能力(服务启动、前端构建)封装为函数,对外暴露清晰 API,业务侧直接调用:

// index.js(elpis包入口)
const ElpisCore = require("./elpis-core");
const FEBuildDev = require('./webpack/dev.js');
const FEBuildProd = require('./webpack/prod.js');

module.exports = {
  // 1. 暴露基础类(供业务继承,减少重复编码)
  Controller: { Base: require('./app/controller/base.js') },
  Service: { Base: require('./app/service/base.js') },

  // 2. 前端构建:支持开发/生产环境
  frontendBuild(paramsEnv) {
    const env = paramsEnv.trim();
    env === 'local' ? FEBuildDev() : FEBuildProd();
  },

  // 3. BFF服务启动:接收业务配置,返回实例
  serverStart(options = {}) {
    const app = ElpisCore.start(options);
    console.log(`-- [elpis] server started at port ${options.port || 3000} --`);
    return app;
  }
};

4. entry.dashboard.js:支持业务路由拓展,零配置注入

新增业务路由拓展机制,业务侧按约定暴露配置函数即可自动注入路由:

// app/pages/dashboard/entry.dashboard.js
import { createRouter, createWebHistory } from 'vue-router';

// 1. 框架核心路由(原有逻辑)
const routes = [/* 核心页面路由 */];
const siderRoutes = [/* 核心菜单路由 */];

// 2. 新增:业务路由拓展(约定业务暴露 businessDashboardRouterConfig)
if (typeof businessDashboardRouterConfig === 'function') {
  businessDashboardRouterConfig({ routes, siderRoutes });
  console.log(`-- [elpis] extended business routes --`);
}

// 3. 创建路由实例并导出
const router = createRouter({ history: createWebHistory(), routes });
export { router, siderRoutes };

5. 组件配置文件:支持业务组件拓展,合并导出

改造schema-view/components/component-config.jswidgets/**/xx-item-config.js,新增业务组件导入,合并核心与业务组件:

// schema-view/components/component-config.js
// 1. 框架核心组件(原有逻辑)
import CreateForm from './create-form';
const ComponentConfig = { createForm };

// 2. 新增:导入业务组件(约定业务路径 $businessComponentConfig)
import businessComponentConfig from '$businessComponentConfig';

// 3. 合并导出(业务组件覆盖核心组件,支持定制)
export default { ...ComponentConfig, ...businessComponentConfig };

二、业务侧使用:elpis-demo 项目零配置开发

业务侧只需创建elpis-demo项目,按以下步骤即可快速开发:

  1. 安装 elpis 包npm install elpis --save(或npm link本地调试);

  2. 按约定目录开发:业务代码放在app目录下(如app/controllerapp/pages),无需配置 webpack/elpis-core;

三、包发布:npm 流程

  1. 本地调试:通过npm linkelpis包链接到elpis-demo,验证功能;
  2. 登录 npmnpm login(输入账号密码);
  3. 发布包npm publish(确保package.jsonname唯一,版本号正确)。

迭代发展方向

深度融合 AI

AI 需求转配置:基于框架开发指南构建专属 AI 提示词(Prompt),开发者只需输入自然语言需求(如 “新增电商分销管理模块,包含分销员列表、佣金统计、订单关联功能”),AI 即可自动生成符合 elpis 规范的领域模型配置(字段定义、组件交互、API 映射),无需手动编写 jsonSchema 配置;

AI Agent:打造 “开发助手”: 开发 elpis 专属 AI Agent,集成框架所有核心能力与规范,支持多维度交互:

  • 配置生成:根据开发者需求描述,生成 schema-view 页面配置、业务路由配置、组件拓展配置等,直接适配 elpis 框架的动态渲染逻辑;
  • 问题解答:开发者遇到配置疑问(如 “如何自定义表格行操作按钮”“业务组件如何挂载到框架”)时,AI Agent 可基于框架文档精准回复,并提供代码示例;
  • 能力拓展:支持开发者提出组件拓展需求(如 “开发一个带时间范围选择的搜索控件”),AI Agent 自动生成符合 elpis 组件规范的代码,并说明挂载方式,确保不偏离框架生态。