本文基于抖音"哲玄前端", 《全栈实践课》,引擎内核实现章节所写。
elpis-core
elpis-core是基于Koa实现的一个类似于Egg的轻量级的服务端框,包括routerLoader 、routerSchemaLoader 、middlewareLoader 、controllerLoader 、serviceLoader 、extendLoader 、configLoader。目录如下
arduino
├─ app
│ ├─ controller
│ ├─ extend
│ ├─ middleware
│ │ ├─ api-params-verify.js
│ │ ├─ api-sign-verify.js
│ │ ├─ error-handler.js
│ ├─ public
│ ├─ router
│ ├─ router-schema
│ ├─ service
│ ├─ middleware.js
├─ config
├─ eggjs-core
│ └─ loader
│ ├─ config.js
│ ├─ controller.js
│ ├─ extend.js
│ ├─ middleware.js
│ ├─ router-schema.js
│ ├─ router.js
│ └─ service.js
│ ├─ env.js
│ ├─ index.js
├─ index.js
config
该目录下通过读取 <math xmlns="http://www.w3.org/1998/Math/MathML"> env.js \color{#FF4500}\textup{env.js} </math>env.js 根据运行的环境去加载如下对应的配置文件: <math xmlns="http://www.w3.org/1998/Math/MathML"> config.local.js(本地) \color{#FF4500}\textup{config.local.js(本地)} </math>config.local.js(本地)、 <math xmlns="http://www.w3.org/1998/Math/MathML"> config.beta.js(测试) \color{#FF4500}\textup{config.beta.js(测试)} </math>config.beta.js(测试)、 <math xmlns="http://www.w3.org/1998/Math/MathML"> config.prod.js(生产) \color{#FF4500}\textup{config.prod.js(生产)} </math>config.prod.js(生产)
js
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.production.js`));
}
} catch(e) {
console.log('[exception] there is no default.config file');
}
service/middleware/controller
同为加载app下相对应的所有文件,并挂载到app.对应的目录下
js
module.exports = (app) => {
// 读取 app/xxx/**/**.js 下所有的文件
const xxxPath = path.resolve(app.businessPath, `.${sep}xxx`);
const fileList = glob.sync(path.resolve(xxxPath, `.${sep}**${sep}**.js`))
// 遍历所有文件目录,把内容加载到 app.xxx 下
const service = {}
fileList.forEach(file => {
// 提取文件名称
let name = path.resolve(file);
// 截取路径 app/xxx/custom-module/custom-xxx.js => custom-module/custom-xxx
name = name.substring(name.lastIndexOf(`xxx${sep}`) + `xxx${sep}`.length, name.lastIndexOf('.'));
// 把 '-' 统一改成驼峰式,custom-module/custom-xxx => customModule.customxxx
name = name.replace(/[_-][a-z]/ig, (s) => s.substring(1).toUpperCase());
// 挂载 xxx 到内存 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 xxxMoule = require(path.resolve(file))(app);
tempxxx[names[i]] = new xxxMoule();
} else { // 文件夹
if (!tempxxx[names[i]]) {
tempxxx[names[i]] = {};
}
tempxxx = tempxxx[names[i]];
}
}
});
app.xxx = xxx;
}
router-schema
通过 'json-schema & ajv' 对 API 规则进行约束,配合 app-params-verify 中间件使用形成如下结构
js
{
'${api1}': ${jsonSchema},
'${api2}': ${jsonSchema},
'${api3}': ${jsonSchema},
'${api4}': ${jsonSchema},
}
router
解析所有 app/router/ 下所有 js 文件,加载到 KoaRouter 下且路由兜底 (健壮性)(当前路由不存在时,临时重定向到指定菜单的同时返回状态码302)
extend
该目录下放置扩展文件,主要为 Koa 实例扩展额外的功能,如: 日志文件(方便日常更加便利的寻找对应的错误)
总结
目前初次接触Nodejs、Koa中间件等内容,对elpis-core的理解浅薄。上诉如有错误望各位大佬们指点一二。