课程名称:web前端架构师
课程章节:第14周 第三章、第四章
主讲老师:张轩
课程内容: eggjs 插件和中间键
egg 插件
相关文档地址。https://www.eggjs.org/zh-CN/basics/plugin
egg 插件列表 https://github.com/search?q=topic%3Aegg-plugin&type=Repositories
插件机制 是egg 的一大特色。它不但可以保证框架核心的足够精简、稳定、高效,还可以促进业务逻辑的复用,生态圈的形成。
为什么要插件
我们在使用 Koa 中间件过程中发现了下面一些问题:
- 中间件加载其实是有先后顺序的,但是中间件自身却无法管理这种顺序,只能交给使用者。这样其实非常不友好,一旦顺序不对,结果可能有天壤之别。
- 中间件的定位是拦截用户请求,并在它前后做一些事情,例如:鉴权、安全检查、访问日志等等。但实际情况是,有些功能是和请求无关的,例如:定时任务、消息订阅、后台逻辑等等。
- 有些功能包含非常复杂的初始化逻辑,需要在应用启动的时候完成。这显然也不适合放到中间件中去实现。
综上所述,我们需要一套更加强大的机制,来管理、编排那些相对独立的业务逻辑。
使用插件
比如我们要使用 ejs 模版渲染插件
npm i egg-plugin-ejs
在 config/plugin.ts 添加
const plugin: EggPlugin = {
ejs: {
enable: true,
package: 'egg-view-ejs',
},
};
修改 config/config.default.ts
config.view = {
mapping: {
'.ejs': 'ejs',
},
};
编写 view 文件
// app/view/dog.ejs
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><%=title%></title>
</head>
<body>
<img src="<%=url%>"/>
</body>
</html>
修改 controller
import { Controller } from 'egg';
export default class DogController extends Controller {
async dog() {
const { service, ctx } = this;
const res = await service.dog.show<{
message: string
status: string
}>();
// 渲染 eje 文件
await ctx.render('dog.ejs', {
url: res.message,
title: 'Dog',
});
}
}
middleweare
手写一个中间键
中间键放在 app/middleware 目录下
新建一个文件logger.ts
import { Context } from 'egg';
import { appendFileSync } from 'fs';
export default () => {
return async (ctx: Context, next: () => Promise<any>) => {
const startTime = Date.now();
await next();
appendFileSync('./log.txt', `time: ${Date.now() - startTime} ms -- url:${ctx.url}\n`);
};
};
然后在 config.default.ts 中配置
config.middleware = [ 'logger' ];
中间件和 Koa 的中间件写法是一模一样的,所以任何 Koa 的中间件都可以直接被框架使用。
向中间键传递参数
首先需要修改中间键的名称,因为上面写的 logger 与 eggjs 自带的中间键同名会发生冲突
修改 logger.ts 为 myLogger.ts.
然后修改 config.default.ts
const bizConfig = {
...
// 表示 myLogger 中间键需要传递的参数
myLogger: {
allowedMethod: [ 'POST' ],
},
};
在 中间键中接受参数
// options 就是中间键接受的参数对象
export default (options: any) => {
return async (ctx: Context, next: () => Promise<any>) => {
const startTime = Date.now();
await next();
if (options.allowedMethod.includes(ctx.method)) {
appendFileSync('./log.txt', `time: ${Date.now() - startTime} ms -- url:${ctx.url}, method: ${ctx.method}\n`);
}
};
};
middleware 在特定的路由上使用也是可以的, 中间键会挂载到 app 对象上,因此我们可以在很多地方都可以使用中间键
import { Service } from 'egg';
export default class DogService extends Service {
async show<T>() {
this.app.middleware.myLogger
const res = await this.ctx.curl<T>('https://dog.ceo/api/breeds/image/random', {
dataType: 'json',
});
return res.data;
}
}