前言
Elpis,古希腊希望女神, 寓意为前端开发乃至程序员岗位的希望
Elpis 是一个企业级全栈应用框架,采用前后端一体化设计,通过配置驱动的方式快速构建企业级应用。项目分为五个核心阶段:核心框架搭建、前端工程化、DSL 设计、动态组件库建设和 NPM 包发布。
npm 地址: www.npmjs.com/package/@mo…
npm i @moranliunian/elpis
特别鸣谢:哲玄前端(全栈)
核心特性
- 模块化加载器:通过
elpis-core/loader/目录下的各种 loader 实现模块的自动加载 - 约定优于配置:基于目录结构的自动发现和注册机制
- 插件化架构:支持业务模块的扩展和自定义
Koa 洋葱圈模型工作原理
Koa的洋葱圈模型指的是中间件的执行方式:请求从外层中间件开始处理,逐步向内传递,到达核心处理逻辑后,再逐层向外返回响应。
- Middleware(中间件):处理HTTP请求的各个环节,如日志记录、身份验证、数据解析等。
- Router(路由):根据URL路径将请求分发到对应的Controller。
- Controller(控制器):处理请求参数,调用Service层,组织响应数据。
- Service(服务):包含业务逻辑,处理数据操作,与数据库交互。
- Config Loader(配置加载器):加载和管理应用程序的配置信息。
- 这种分层架构使代码更加模块化,易于维护和测试,每层都有明确的职责。
启动流程
// elpis-core/index.js 核心启动逻辑
start(options = {}) {
const app = new Koa();
// 1. 加载中间件
middlewareLoader(app);
// 2. 加载路由配置
routerSchemaLoader(app);
// 3. 加载控制器
controllerLoader(app);
// 4. 加载服务层
serviceLoader(app);
// 5. 加载配置
configLoader(app);
// 6. 加载扩展
extendLoader(app);
// 7. 注册路由
routerLoader(app);
}
I. elpis-core 内核
graph TD
A[Elpis Core] --> B[Loader 模块]
B --> C[Middleware Loader]
B --> D[Router Loader]
B --> E[Controller Loader]
B --> F[Service Loader]
B --> G[Config Loader]
B --> H[Extend Loader]
A --> I[Koa 应用实例]
I --> J[中间件注册]
I --> K[路由注册]
I --> L[服务启动]
目录结构
elpis-core/
├── index.js # 核心启动入口
├── env.js # 环境配置管理
└── loader/ # 模块加载器
├── config.js # 配置加载器
├── controller.js # 控制器加载器
├── extend.js # 扩展加载器
├── middleware.js # 中间件加载器
├── router-schema.js # 路由配置加载器
├── router.js # 路由加载器
└── service.js # 服务层加载器
II. 前端工程化模块,webpack 配置
graph LR
A[Webpack 配置] --> B[基础配置]
A --> C[开发配置]
A --> D[生产配置]
B --> E[入口配置]
B --> F[模块解析]
B --> G[插件配置]
B --> H[优化策略]
E --> I[动态入口发现]
F --> J[Vue Loader]
F --> K[Babel Loader]
G --> L[HtmlWebpackPlugin]
H --> M[代码分割]
H --> N[Tree Shaking]
核心特性
- 动态入口发现:自动扫描
app/pages/**/entry.*.js文件作为入口 - 多页面应用支持:支持多个独立页面的构建
- 智能代码分割:vendor、common、entry 三层分割策略
- 业务扩展支持:通过 alias 配置支持业务模块扩展
关键配置
// 动态入口配置
const elpisEntryList = path.resolve(__dirname, '../../pages/**/entry.*.js');
glob.sync(elpisEntryList).forEach((file) => {
handleFile(file, elpisPageEntries, elpisHtmlWebpackPluginList);
});
// 代码分割策略
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendor'
},
common: {
test: /[\\/]common|widgets[\\/]/,
name: 'common'
}
}
}
目录结构
app/webpack/
├── dev.js # 开发环境启动
├── prod.js # 生产环境构建
├── config/ # 配置文件
│ ├── webpack.base.js # 基础配置
│ ├── webpack.dev.js # 开发配置
│ └── webpack.prod.js # 生产配置
└── libs/ # 工具库
└── blank.js # 空白模块
III. DSL 设计和实现 schemaview
graph TD
A[Schema 配置] --> B[Schema Parser]
B --> C[Table Schema]
B --> D[Search Schema]
B --> E[Component Schema]
C --> F[Table Panel]
D --> G[Search Panel]
E --> H[Dynamic Components]
F --> I[数据展示]
G --> J[条件搜索]
H --> K[表单操作]
H --> L[详情展示]
DSL 核心概念
- 统一 Schema 定义:一个 Schema 配置支持多种视图(table、search、form)
- Option 扩展机制:通过
tableOption、searchOption、createFormOption等扩展字段行为 - 动态组件系统:基于配置的组件动态渲染
“那什么是 JSON Schema 呢?”
JSON Schema 本身是一个 JSON 对象,它是一份强大的声明式合约,用来描述和验证另一种 JSON 数据的结构和内容。
你可以把它理解为:
- 数据的蓝图或元数据:它定义了 JSON 数据应该长什么样。
- 数据的契约:它规定了数据必须遵守的规则。
- 数据的验证工具:你可以用程序(验证器)根据 Schema 来检查任何 JSON 数据是否符合要求。
它的核心作用是 验证(Validation)。在数据传输(如 API 请求/响应)或数据存储时,确保 JSON 数据的格式、类型和内容符合预期,从而提高数据的质量和程序的健壮性。
Schema 配置示例
{
type: 'object',
properties: {
name: {
type: 'string',
label: '姓名',
tableOption: {
visible: true,
width: 120
},
searchOption: {
comType: 'input',
default: ''
},
createFormOption: {
comType: 'input',
required: true
}
}
}
}
Schema 解析流程
// hook/schema.js 中的核心解析逻辑
const buildDtoSchema = (_schema, comName) => {
const dtoSchema = {
type: 'object',
properties: {}
};
for (const key in _schema.properties) {
const props = _schema.properties[key];
if (props[`${comName}Option`]) {
// 提取对应组件的配置
dtoSchema.properties[key] = {
...props,
option: props[`${comName}Option`]
};
}
}
return dtoSchema;
};
目录结构
app/pages/dashboad/complex-view/schema-view/
├── schema-view.vue # 主视图组件
├── hook/ # 业务逻辑
│ └── schema.js # Schema 解析 Hook
├── components/ # 动态组件
│ ├── component-config.js # 组件配置映射
│ ├── create-form/ # 创建表单
│ ├── detail-panel/ # 详情面板
│ └── edit-form/ # 编辑表单
└── complex-view/ # 复杂视图
├── search-panel/ # 搜索面板
└── table-panel/ # 表格面板
IV. 动态组件库搭建
graph TD
A[组件库] --> B[Schema Form]
A --> C[Schema Table]
A --> D[Schema Search Bar]
A --> E[Header Container]
A --> F[Sider Container]
B --> G[Form Item Config]
C --> H[Table Config]
D --> I[Search Item Config]
G --> J[Input]
G --> K[Select]
G --> L[Input Number]
H --> M[Column Config]
H --> N[Button Config]
I --> O[Date Range]
I --> P[Dynamic Select]
组件扩展机制
- 配置驱动:通过配置文件定义组件映射关系
- 插件化扩展:支持业务自定义组件注册
- 统一接口:所有组件遵循相同的接口规范
组件配置示例
// form-item-config.js
const FormItemConfig = {
input: { component: input },
select: { component: select },
inputNumber: { component: inputNumber }
};
// 支持业务扩展
export default {
...FormItemConfig,
...BusinessFormItemConfig
};
动态组件渲染
<template>
<component
:is="FormItemConfig[itemSchema.option?.comType]?.component"
:schema="itemSchema"
:model="model"
/>
</template>
目录结构
app/pages/widgets/
├── schema-form/ # 表单组件
│ ├── schema-form.vue # 表单主组件
│ ├── form-item-config.js # 表单项配置
│ └── complex-view/ # 复杂视图
│ ├── input/ # 输入框组件
│ ├── input-number/ # 数字输入框
│ └── select/ # 选择器组件
├── schema-table/ # 表格组件
│ └── schema-table.vue # 表格主组件
├── schema-search-bar/ # 搜索组件
│ ├── schema-search-bar.vue # 搜索栏主组件
│ ├── search-item-config.js # 搜索项配置
│ └── complex-view/ # 复杂视图
│ ├── date-range/ # 日期范围组件
│ ├── dynamic-select/ # 动态选择器
│ ├── input/ # 搜索输入框
│ └── select/ # 搜索选择器
├── header-container/ # 头部容器
│ ├── header-container.vue # 头部容器组件
│ └── asserts/ # 资源文件
│ ├── avatar.png # 头像
│ └── logo.png # 标志
└── sider-container/ # 侧边栏容器
└── sider-container.vue # 侧边栏容器组件
V. 发包 elpis npm 包
发布流程设计
graph LR
A[源码开发] --> B[代码审查]
B --> C[版本管理]
C --> D[构建打包]
D --> E[NPM 发布]
E --> F[文档更新]
F --> G[版本标签]
包结构设计
@moranliunian/elpis/
├── elpis-core/ # 核心框架
├── app/ # 前端应用
│ ├── pages/ # 页面模块
│ ├── widgets/ # 组件库
│ └── webpack/ # 构建配置
├── config/ # 配置文件
├── model/ # 数据模型
└── index.js # 主入口
发布配置
{
"name": "@moranliunian/elpis",
"version": "1.0.1",
"main": "index.js",
"scripts": {
"lint": "eslint --quiet --ext js,vue .",
"test": "_ENV='local' mocha 'test/**/*.js'"
}
}
项目特色与优势
1. 全栈一体化
- 前后端统一开发框架,减少技术栈切换成本
- 统一的配置管理和开发规范
2. 配置驱动开发
- 通过 DSL 配置快速构建 CRUD 页面
- 减少重复代码,提高开发效率
3. 高度可扩展
- 插件化架构支持业务定制
- 组件库支持动态扩展
4. 工程化完善
- 完整的构建、测试、发布流程
- 支持多环境部署
5. 企业级特性
- 支持多页面、权限控制、错误处理
- 完善的日志和监控机制
使用示例
服务端启动
const { start } = require('@moranliunian/elpis');
// 启动 elpis 服务
const app = start({
name: 'my-project',
homePage: '/dashboard'
});
前端构建
const { frontendBuild } = require('@moranliunian/elpis');
// 编译构建前端工程
frontendBuild(process.env._ENV);
Schema 配置
const schemaConfig = {
api: '/api/users',
schema: {
type: 'object',
properties: {
name: {
type: 'string',
label: '姓名',
tableOption: { visible: true },
searchOption: { comType: 'input' },
createFormOption: { comType: 'input', required: true }
}
}
}
};
总结
总体理念上,设计大于编码,从程序员的角度去考虑业务问题,而不是局限于前端开发工程师。虽然课程中选择了 koa + express + webpack + vue,但是在思维层面是完全跳出框架限制的,框架只是实现思维的工具。在思考到设计再到实现的过程中,思考和设计占据了绝大多数时间,无时无刻不在灌输作为一个程序员的素养。
最后,在互联网这个日趋激烈的大环境下面,希望同学们可以保持初心,保持对技术的热情,保持学习,时刻提升自身的打铁水平,与君共勉!
🎉完结撒花🎉