Egg源码

Egg

在Koa的基础上,阿里封装了一个企业级的NodeJS框架——egg.js。将Koa的行为进一步分离成controller,service,model三层,并规范了路由的写法——约定式路由。

约定式路由

实现约定式路由的核心方法是load函数,通过fs遍历约定文件夹( controller,service,model… )下的文件,导出文件的内容,生成一个对象挂载到Egg的实例上。

load

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const fs = require("fs");
const path = require("path");
function load(dir, cb) {
// 获取绝对路径
const url = path.resolve(__dirname, dir);
// 读取路径下的文件
const files = fs.readdirSync(url, { encoding: "ascii" });
// 遍历路由文件 将配置解析到路由器中
files.forEach((filename) => {
filename = filename.replace(".js", "");
// 导入文件
const file = require(url + "/" + filename);
// 处理逻辑
cb(filename, file);
});
}

initRouter

处理routes下面的文件,约定每一个文件为一个路由,其中的每一个是对应的子路由,这里对路由简单书写,实际可能会更复杂一点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function initRouter(app) {
const router = new Router();
load("routes", (filename, routes) => {
// 若是index文件 就不加前缀 如果是其他的文件 路由前缀就是文件名
const prefix = filename === "index" ? "" : `/${filename}`;
routes = typeof routes == "function" ? routes(app) : routes;
Object.keys(routes).forEach((key) => {
const [method, path] = key.split(" ");
router[method](prefix + path, async (ctx) => {
// 传⼊ctx
app.ctx = ctx; // 挂载⾄app
await routes[key](app); // 路由处理器现在接收到的是app
});
});
});
return router;
}
// routes/index.js
module.exports = (app) => ({
"get /": app.$controller.home.index,
"get /detail": app.$controller.home.detail,
});

MVC分离

controller,service,model同理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// controller-loader.js
const { load } = require("./router-loader");

function initController(app) {
const controllers = {};

load("controller", (filename, controller) => {
controllers[filename] = controller(app);
});
return controllers;
}

module.exports = { initController };
// controller/home.js
// 这里对controller做简单处理,egg中会继承Controller类 然后在类中书写方法,但是作用一致
module.exports = (app) => ({
index: async (ctx) => {
const name = await app.$service.user.getName();
app.ctx.body = "ctrl user" + name;
},
detail: (ctx) => {
app.ctx.body = "Home Detail";
},
});


// service-loader.js
const { load } = require("./router-loader");

function initService() {
const services = {};
load("service", (filename, service) => {
services[filename] = service;
});
return services;
}

module.exports = { initService };

Egg类

本质上是对Koa的扩展,实现MVC结构。

  • model层即数据库实体层与数据库中的表一一对应;
  • controller层负责前后端交互,接受前端请求,调用service层,接收service层返回的数据,最后返回具体的页面和数据到客户端;
  • service层即业务逻辑层,完成业务的功能设计。 主要是针对具体的问题的操作,把一些数据层的操作进行组合,间接与数据库打交道(提供操作数据库的方法)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
const { initController } = require("./ctrl-loader");
const Koa = require("./lib/application");
const { initRouter } = require("./router-loader");
const { initService } = require("./service-loader");
const { loadConfig } = require("./config-loader");
class Egg {
constructor(conf) {
this.$app = new Koa(conf);
loadConfig(this);
this.$controller = initController(this);
this.$service = initService();
this.$router = initRouter(this);
console.log(this.$router.stack);
this.$app.use(this.$router.routes());
}
start(...arg) {
this.$app.listen(...arg);
}
}

module.exports = Egg;

Egg源码
https://jing-jiu.github.io/jing-jiu/2022/08/05/Node/Egg/
作者
Jing-Jiu
发布于
2022年8月5日
许可协议