使用 egg.js 定制业务 web 框架

Web 框架作的事情太少就会致使可用性差,作的太多就会比较定制,而 Egg 是框架的框架,帮助团队的技术负责人,来定制适合特定的业务场景的上层业务框架。egg.js 的名称含义正是这样,像 egg 同样孕育 Web 框架html

前面章节介绍了如何使用 egg.js 完成业务开发、定制插件,这些是把 egg.js 当作一个 web 框架使用,本章节介绍下 egg.js 作为框架的框架为业务定制一个 web 框架的能力node

设计目标

能够把前面章节实现的基础功能作为 demo 框架的默认功能,封装完成后提供给团队使用git

  1. 自带 handlebars 模板渲染能力
  2. 全部请求自动统计耗时
  3. enum、util 挂载到 this.app

配置框架

初始化代码

使用 egg.js 提供的 framework 脚手架初始化框架代码github

$ mkdir framework-demo && cd framework-demo
$ npm init egg --type=framework
复制代码

目录结构应该很熟悉了,多出来的lib/framework.js 是框架的入口web

framework-demo
├── app
│   ├── extend
│   └── service
├── config
│   ├── config.default.js
│   └── plugin.js
├── lib
│   └── framework.js
├── test
├── README.md
├── index.js
└── package.json
复制代码

handlebars 模板引擎支持

egg.js 使用的章节介绍过 如何配置模板引擎,定制框架的时候步骤同样npm

安装 egg-view-handlebars

$ npm i egg-view-handlebars --save
复制代码

启用插件

// config/plugin.js
module.exports = {
  handlebars: {
    enable: true,
    package: 'egg-view-handlebars',
  },
};
复制代码

配置 view 渲染项

// config/config.default.js
config.view = {
  defaultViewEngine: 'handlebars',
  defaultExtension: '.hbs',
  mapping: {
    '.hbs': 'handlebars',
  },
};
复制代码

这样使用该框架就默认具有了 handlebars 渲染能力json

内置请求耗时中间件

中间件的编写规则和在 egg.js 中直接使用一致,不过添加到框架的方式有所不一样markdown

添加 cost 中间件

// app/middleware/cost.js
module.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

// app.js
module.exports = app => {
  // 在中间件最前面统计请求时间
  app.config.coreMiddleware.unshift('cost');
};
复制代码

enum、util 挂载

在框架中有不少业务的字段枚举或者通用的工具类,通常是定义了文件夹统一管理,开发使用的时候手工 require,使用 egg.js 后能够把约定内置框架,在指定目录编写后自动加载到框架框架

文件添加

添加文件 app/enum/error.jsapp/util/dto.js

framework-demo
├── app
│   ├── extend
│   ├── service
│   ├── enum
│   │       └── error.js
│   └── util
│   │       └── dto.js
└── package.json

// 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',
};

// 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.js
config.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"
},
复制代码

使用

image.png

测试 & 发布

这样就完成了框架定制,框架由于涉及多人使用,须要有完善的测试保证可用性,egg.js 提供了完备的测试支持,测试工做完成后能够进入发布流程

  1. 根据语义化版本号规则使用合适的版本
  2. 发布 beta 版本 npm publish --tag=beta
  3. 测试 OK 后发布正式版本 npm publish

使用框架

在 egg.js 应用中使用框架很简单,把 egg 脚手架生成的应用 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"
  }
}
复制代码

package.json 声明框架后 npm run dev 能够看到已经使用 egg-demo-framework 启动框架了,cost 中间件也正常工做

INFO 76333 [master] egg-framework-demo started on http://127.0.0.1:7001 (1901ms)
复制代码

image.png

完整代码:github.com/Samaritan89…

参考

  1. 如何为团队量身定制 EggJS 目录挂载规范
  2. 基于 EggJS 为团队定制本身的 Node.js 框架
相关文章
相关标签/搜索