学习 elpis 有感 -- 初识 elpis-core (实现简易版 Egg.js)

首先什么是 Egg.js ?

Egg.js 是 阿里 开源的 Node.js 企业级应用框架 ,基于 Koa 封装,提供更强的规范性和扩展性。它的核心理念是 "约定大于配置"(Convention Over Configuration) ,即通过 "预设规则" 减少开发者的决策成本,提升团队协作效率。

为什么要用约定 ?

核心原因分为两种:1. 开发习惯差异 2. 人员更迭风险

我们假设一个场景,开发者A可能将用户模块的数据库操作写在Controller里(MVC习惯),开发者B却独立出Domain Service层(DDD习惯)。比如项目组有10人,开发习惯五花八门。有一天B离职了工作需要A接手,A不得不花费大量时间理解这些个性化风格;再假设项目干上2-3年,恐怕会慢慢变成一个烫手的山芋...没人愿意接手这个烂摊子。于是 "约定大于配置" 的理念诞生了,Egg.js 强制约定 controller 只处理HTTP请求、service 承载业务逻辑... 使得新人加入时能立即定位代码。

elpis-core 登场

elpis-core 是参考 Egg.js "约定大于配置" 理念实现的服务端框架。

核心分为以下7个模块:

  • middleware(中间件)
  • config(配置项)
  • router(路由)
  • router-schema(路由参数校验规范)
  • controller(控制器)
  • service(服务)
  • extend(扩展例如日志)

接下来我用 middleware.js 举例说明 elpis-core 内核内部运作的原理。

ini 复制代码
module.exports = (app) => {
    // 读取 app/middleware/**/**.js 下所有的文件
    const middlewarePath = path.resolve(app.businessPath, `.${sep}middleware`);
    const fileList = glob.sync(path.resolve(middlewarePath, `.${sep}**${sep}**.js`));

    // 遍历所有文件目录,把内容加载到 app.middlewares 下
    const middlewares = {};
    fileList.forEach((file) => {
        // 提取文件名称
        let name = path.resolve(file);

        // 截取路径 app/middleware/custom-module/custom-middleware.js => custom-module/custom-middleware
        name = name.substring(name.lastIndexOf(`middleware${sep}`) + `middleware${sep}`.length, name.lastIndexOf("."));

        // 把 '-' 统一改为驼峰式,custom-module/custom-middleware => customModule.customMiddleware
        name = name.replace(/[_-][a-z]/gi, (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]];
            }
        }
    });
    app.middlewares = middlewares;
};

这段代码实现了一个 自动加载中间件 的机制,它会扫描 app/middleware/ 目录下的所有 .js 文件,并按一定的规则挂载到 app.middlewares 对象上,方便在项目中统一调用。

例如我创建了文件: app/middleware/api-params-verify.js ,那么我可以在任何地方 app.middlewares.apiParamsVerify 使用它。再或者我们进行业务开发时定义了一个 service app/service/project.js 那么我们可以直接在controller中这样去调用 project 的 getList 方法。

ini 复制代码
const { project: projectService } = app.service;
const projectList = projectService.getList();

其余模块的原理也类似,在此不做过多代码说明。

结语

elpis-core 它就像是一个乐高说明书,虽然限制了拼装方式,但能保证所有人拼出来的都是稳固结构,特别适合需要长期维护的企业级应用。

相关推荐
前端小咸鱼一条6 分钟前
React中的this绑定
前端·javascript·react.js
影子信息12 分钟前
vue vxe-table :edit-config=“editConfig“ 可以编辑的表格
前端·javascript·vue.js
YGY Webgis糕手之路13 分钟前
Cesium 快速入门(四)相机控制完全指南
前端·经验分享·笔记·vue·web
JavaDog程序狗16 分钟前
【软件环境】Windows安装NVM
前端·node.js
黑土豆19 分钟前
为什么我要搞一个Markdown导入组件?说出来你可能不信...
前端·javascript·markdown
前端小巷子21 分钟前
Vue 2 响应式系统
前端·vue.js·面试
前端小咸鱼一条38 分钟前
React的基本语法和原理
前端·javascript·react.js
qq_2787877738 分钟前
Golang 调试技巧:在 Goland 中查看 Beego 控制器接收的前端字段参数
前端·golang·beego
YGY Webgis糕手之路38 分钟前
Cesium 快速入门(六)实体类型介绍
前端·经验分享·笔记·vue·web
come1123440 分钟前
前端ESLint扩展的用法详解
前端