Web 框架做的事情太少就会导致可用性差,做的太多就会比较定制,而 Egg 是框架的框架,帮助团队的技术负责人,来定制适合特定的业务场景的上层业务框架。egg.js 的名称含义正是这样,像 egg 一样孕育 Web 框架
前面章节介绍了如何使用 egg.js 完成业务开发、定制插件,这些是把 egg.js 当做一个 web 框架使用,本章节介绍下 egg.js 做为框架的框架为业务定制一个 web 框架的能力
设计目标
可以把前面章节实现的基础功能做为 demo 框架的默认功能,封装完成后提供给团队使用
- 自带 handlebars 模板渲染能力
- 所有请求自动统计耗时
-
配置框架
初始化代码
使用 egg.js 提供的 framework 脚手架初始化框架代码
$ mkdir framework-demo && cd framework-demo$ npm init egg --type=framework
目录结构应该很熟悉了,多出来的
lib/framework.js是框架的入口framework-demo├── app│ ├── extend│ └── service├── config│ ├── config.default.js│ └── plugin.js├── lib│ └── framework.js├── test├── README.md├── index.js└── package.json
handlebars 模板引擎支持
egg.js 使用的章节介绍过 如何配置模板引擎,定制框架的时候步骤一样
安装 egg-view-handlebars
$ npm i egg-view-handlebars --save
启用插件
// config/plugin.jsmodule.exports = {handlebars: {enable: true,package: 'egg-view-handlebars',},};
配置 view 渲染项
// config/config.default.jsconfig.view = {defaultViewEngine: 'handlebars',defaultExtension: '.hbs',mapping: {'.hbs': 'handlebars',},};
内置请求耗时中间件
中间件的编写规则和在 egg.js 中直接使用一致,不过添加到框架的方式有所不同
添加 cost 中间件
// app/middleware/cost.jsmodule.exports = options => {const header = options.header || 'X-Response-Time';return async function cost(ctx, next) {const now = Date.now();await next();ctx.set(header, `${Date.now() - now}ms`);};};
应用中间件
框架和插间添加中间件和直接在应用中使用不同,不支持修改 config 文件,需要在项目根目录下的
app.js修改// app.jsmodule.exports = app => {// 在中间件最前面统计请求时间app.config.coreMiddleware.unshift('cost');};
enum、util 挂载
在框架中有很多业务的字段枚举或者通用的工具类,一般是定义了文件夹统一管理,开发使用的时候手工 require,使用 egg.js 后可以把约定内置框架,在指定目录编写后自动加载到框架
文件添加
添加文件
app/enum/error.js和app/util/dto.jsframework-demo├── app│ ├── extend│ ├── service│ ├── enum│ │ └── error.js│ └── util│ │ └── dto.js└── package.json
```javascript // app/enum/error.js ‘use strict’;
exports.ERR_AUTH = { code: ‘403’, msg: ‘not perm’, };
exports.ERR_NOTFOUND = { code: ‘404’, msg: ‘not found’, };
exports.ERR_SERVER = { code: ‘500’, msg: ‘internal server error’, };
```javascript// app/util/dto.js'use strict';const assert = require('assert');function isObject(obj) {const objType = Object.prototype.toString.call(obj);return objType === '[object Object]' || objType === '[object Array]' || objType === '[object Null]';}class ResultDto {constructor(result, code = 200, errorMsg = '', errorStack = null) {assert(isObject(result), '[ResultDto:constructor]: arg[0] must be an object or null!');this.result = result;this.success = code === 200;this.code = code;if (code !== 200) {this.errorMsg = errorMsg;this.errorStack = errorStack;}}}exports.ResultDto = ResultDto;
配置 loader
在配置文件中为文件夹声明路径和注入的对象,更多细节参考 EggJS 加载器
// config/config.default.jsconfig.customLoader = {enum: {directory: 'app/enum',inject: 'app',loadunit: true,},util: {directory: 'app/util',inject: 'app',loadunit: true,},};
自动提示
由于 Egg 是动态挂载的,如需 TS 和智能提示支持,需要通过 egg-ts-helper 来自动生成映射
首先修改 package.json 文件声明
// package.json{"name": "framework-demo","egg": {"declaration": true,"tsHelper": {"watchDirs": {"enum": {"enabled": true,"directory": "app/enum","declareTo": "Application.enum"},"util": {"enabled": true,"directory": "app/util","declareTo": "Application.util"}}}}}
egg-bin 内置支持了自动生成 typings 文件夹,但框架开发通常不会使用 egg-bin dev 为了方便框架开发可以在 scripts 配置生成 typeings 的命令
"scripts": {"typing": "npx ets"},
使用
测试 & 发布
这样就完成了框架定制,框架因为涉及多人使用,需要有完善的测试保证可用性,egg.js 提供了完备的测试支持,测试工作完成后可以进入发布流程
- 根据语义化版本号规则使用合适的版本
- 发布 beta 版本
npm publish --tag=beta - 测试 OK 后发布正式版本
npm publish使用框架
在 egg.js 应用中使用框架很简单,把 egg 脚手架生成的应用 package.json 稍作修改即可
package.json 声明框架后{"name": "egg-demo","version": "1.0.0","egg": {"declarations": true,"framework": "egg-framework-demo"},"dependencies": {"egg-framework-demo": "^1","egg-scripts": "^2.11.0"}}
npm run dev可以看到已经使用 egg-demo-framework 启动框架了,cost 中间件也正常工作INFO 76333 [master] egg-framework-demo started on http://127.0.0.1:7001 (1901ms)

完整代码:https://github.com/Samaritan89/egg-framework-demo
