注:引用抖音"哲玄前端",《全栈实践课》
前言
elpis-core 是一个基于 Koa.js 的应用框架,其核心目标是通过模块化、分层架构和设计模式的运用,为开发者提供高效、灵活且可维护的开发体验。它致力于解决复杂项目开发中的常见问题,如模块耦合、代码难以维护、功能扩展困难等。
项目整体架构设计
模块化设计思维
将应用划分为多个功能模块,如 config、extend、middleware、routerSchema、controller、service 等,每个模块有明确的职责和边界。这种设计便于团队协作,不同开发人员可以专注于各自擅长的模块,同时降低修改一个模块对其他模块的影响。
分层架构原则
遵循分层原则,将应用分为配置层、扩展层、中间件层、路由模式层、控制器层、服务层等。各层之间有清晰的接口定义,上层依赖下层,但下层不依赖上层,提高了系统的可维护性和可扩展性。
该框架使用了一系列的 loader来加载并挂载到 Koa 实例,每个 Loader 有明确的功能描述和关键技术点
configLoader
(配置管理模块)middlewareLoader
(中间件加载器)extendLoader
(扩展加载)routerSchemaLoader
(接口参数校验)serviceLoader
(服务加载)controllerLoader
(控制器)routerLoader
(路由加载器)
config
配置管理模块
通过不同环境配置文件(如 config.beta.js
、config.prod.js
),实现一键切换环境,满足开发、测试、生产等不同场景需求。config.default.js
作为基础配置,其他环境配置可在此基础上覆盖,平衡默认行为与灵活性。
- configLoader 核心代码
js
// 找到 config/ 目录
const configPath = path.resolve(app.baseDir, `.${sep}config`);
// 获取 default.config
let defaultConfig = {};
try {
defaultConfig = require(path.resolve(configPath, `.${sep}config.default.js`));
} catch (e) {
console.log('[exception] there is no default.config file');
}
// 获取env.config
let envConfig = {};
try {
if (app.env.isLocal()) { // 本地环境
envConfig = require(path.resolve(configPath, `.${sep}config.local.js`));
} else if (app.env.isBeta()) { // 测试环境
envConfig = require(path.resolve(configPath, `.${sep}config.beta.js`));
} else if (app.env.isProduction()) { // 生产环境
envConfig = require(path.resolve(configPath, `.${sep}config.prod.js`));
}
} catch (e) {
console.log('[exception] there is no default.config file');
}
// 覆盖并加载 config 配置
app.config = Object.assign({}, defaultConfig, envConfig);
middleware
中间件模块
灵活加载中间件并构建洋葱圈模型
- middlewareLoader核心代码
js
fileList.forEach(file => {
// 提取文件名称
let name = path.resolve(file);
// 截取路径 app/middleware/custom-module/custom-middleware => custom-module/custom-middleware
name = name.substring(name.lastIndexOf(`middleware${sep}`) + `middleware${sep}`.length, name.lastIndexOf('.'));
// 把 '-' 统一改为驼峰式,custom-module/custom-middleware.js => customModule.customMiddleware
name = name.replace(/[_-][a-z]/ig, (s) => s.substring(1).toUpperCase());
// 挂载 middleware 到内存 app 对象中
let tempMiddleware = middlewares;
const names = name.split(sep);
for (let i = 0, len = names.length; i < len; ++i) {
if (i === len - 1) {
tempMiddleware[names[i]] = require(path.resolve(file))(app);
} else {
if (!tempMiddleware[names[i]]) {
tempMiddleware[names[i]] = {};
}
tempMiddleware = tempMiddleware[names[i]];
}
}
});
extend
扩展功能
用于拓展应用的额外能力,例如日志
- extendLoader核心代码
js
// 遍历所有文件目录,把内容加载到 app.extend 下
fileList.forEach(file => {
// 提取文件名称
let name = path.resolve(file);
// 截取路径 app/extend/custom-extend => custom-extend
name = name.substring(name.lastIndexOf(`extend${sep}`) + `extend${sep}`.length, name.lastIndexOf('.'));
// 把 '-' 统一改为驼峰式,custom-extend.js => customExtend
name = name.replace(/[_-][a-z]/ig, (s) => s.substring(1).toUpperCase());
// 过滤 app 已经存在的key
for (const key in app) {
if (key === name) {
console.log(`[extend load error] name:${name} is already in app`);
return;
}
}
// 挂载 extend 到内存 app 对象中
app[name] = require(path.resolve(file))(app);
});
routerSchem
路由模式定义
定义路由模式,便于生成标准路由配置,减少重复定义,同时为参数校验等提供依据
- routerSchemaLoader核心代码
js
// 注册所有 routerSchema ,使得可以 'app.routerSchema' 这样访问
let routerSchema = {};
fileList.forEach(file => {
routerSchema = {
...routerSchema,
...require(path.resolve(file))
}
});
app.routerSchema = routerSchema;
service
服务加载器
提供业务逻辑,供 controller 调用
- serviceLoader核心代码
js
fileList.forEach(file => {
// 提取文件名称
let name = path.resolve(file);
// 截取路径 app/service/custom-module/custom-service => custom-module/custom-service
name = name.substring(name.lastIndexOf(`service${sep}`) + `service${sep}`.length, name.lastIndexOf('.'));
// 把 '-' 统一改为驼峰式,custom-module/custom-service.js => customModule.customService
name = name.replace(/[_-][a-z]/ig, (s) => s.substring(1).toUpperCase());
// 挂载 service 到内存 app 对象中
let tempService = service;
const names = name.split(sep); // [customModule(目录), customService(文件)]
for (let i = 0, len = names.length; i < len; ++i) {
if (i === len - 1) { // 文件
const serviceModule = require(path.resolve(file))(app);
tempService[names[i]] = new serviceModule();
} else { // 文件夹
if (!tempService[names[i]]) {
tempService[names[i]] = {};
}
tempService = tempService[names[i]];
}
}
});
app.service = service;
controller
控制器
控制器层,处理 HTTP 请求并调用服务层
- controllerLoader核心代码
js
// 遍历所有文件目录,把内容加载到 app.controller 下
const controller = {};
fileList.forEach(file => {
// 提取文件名称
let name = path.resolve(file);
// 截取路径 app/controller/custom-module/custom-controller => custom-module/custom-controller
name = name.substring(name.lastIndexOf(`controller${sep}`) + `controller${sep}`.length, name.lastIndexOf('.'));
// 把 '-' 统一改为驼峰式,custom-module/custom-controller.js => customModule.customController
name = name.replace(/[_-][a-z]/ig, (s) => s.substring(1).toUpperCase());
// 挂载 controller 到内存 app 对象中
let tempController = controller;
const names = name.split(sep); // [customModule(目录), customController(文件)]
for (let i = 0, len = names.length; i < len; ++i) {
if (i === len - 1) { // 文件
const ControllerModule = require(path.resolve(file))(app);
tempController[names[i]] = new ControllerModule();
} else { // 文件夹
if (!tempController[names[i]]) {
tempController[names[i]] = {};
}
tempController = tempController[names[i]];
}
}
});
app.controller = controller;
router
路由加载器
路由定义,映射对应的controller
- routerLoader核心代码
js
// 注册所有路由
const fileList = glob.sync(path.resolve(routerPath, `.${sep}**${sep}**.js`));
fileList.forEach(file => {
require(path.resolve(file))(app, router);
});
// 路由注册到 app 上
app.use(router.routes());
app.use(router.allowedMethods());
总结
本项目通过 elpis-core
核心框架,实现了环境配置管理、路由模式定义、中间件链式调用等功能,为业务开发提供了坚实的基础。同时,项目结构清晰,符合大型项目的开发规范,具有良好的扩展性和可维护性。