Eggjs入门系列-基础全面讲解(中)

在这里插入图片描述
回顾一下上篇讲到的内容,上篇讲了:

运行环境

一个 Web 应用本身应该是无状态的,并拥有根据运行环境设置自身的能力。

指定运行环境

  • 通过 config/env 文件指定,该文件的内容就是运行环境,如 prod
  • 通过 EGG_SERVER_ENV 环境变量指定

方式 2 比较常用,因为通过 EGG_SERVER_ENV 环境变量指定运行环境更加方便,比如在生产环境启动应用:

EGG_SERVER_ENV=prod npm start

应用内获取运行环境

使用 app.config.env 获取

与 NODE_ENV 的区别

框架默认支持的运行环境及映射关系(如果未指定 EGG_SERVER_ENV 会根据 NODE_ENV 来匹配)

NODE_ENV EGG_SERVER_ENV 说明
local 本地开发环境
test unittest 单元测试
production prod 生产环境

NODE_ENVproductionEGG_SERVER_ENV 未指定时,框架会将 EGG_SERVER_ENV 设置成 prod

自定义环境

常规开发流程可能不仅仅只有以上几种环境,Egg 支持自定义环境来适应自己的开发流程。

比如,要为开发流程增加集成测试环境 SIT。将 EGG_SERVER_ENV 设置成 sit(并建议设置 NODE_ENV = production),启动时会加载 config/config.sit.js,运行环境变量 app.config.env 会被设置成 sit

与 Koa 的区别

在 Koa 中我们通过 app.env 来进行环境判断,app.env 默认的值是 process.env.NODE_ENV

在 Egg(和基于 Egg 的框架)中,配置统一都放置在 app.config 上,所以我们需要通过 app.config.env 来区分环境,app.env 不再使用。

Config 配置

框架提供了强大且可扩展的配置功能,可以自动合并应用、插件、框架的配置,按顺序覆盖,且可以根据环境维护不同的配置。合并后的配置可直接从 app.config 获取。

Egg 选择了配置即代码,配置的变更也应该经过 review 后才能发布。应用包本身是可以部署在多个环境的,只需要指定运行环境即可。

多环境配置

框架支持根据环境来加载配置,定义多个环境的配置文件

config
|- config.default.js
|- config.prod.js
|- config.unittest.js
|- config.local.js

config.default.js 为默认的配置文件,所有环境都会加载这个配置文件,一般也会作为开发环境的默认配置文件。

当指定 env 时会同时加载对应的配置文件,并覆盖默认配置文件的同名配置。如 prod 环境会加载 config.prod.jsconfig.default.js 文件,config.prod.js 会覆盖 config.default.js 的同名配置。

配置写法

配置文件返回的是一个 object 对象,可以覆盖框架的一些配置,应用也可以将自己业务的配置放到这里方便管理,获取时直接通过 app.config

导出对象式写法

// 配置 logger 文件的目录,logger 默认配置由框架提供
module.exports = {
   
  logger: {
   
    dir: '/home/admin/logs/demoapp',
  },
};

配置文件也可以返回一个 function,可以接受 appInfo 参数

// 将 logger 目录放到代码目录下
const path = require('path');
module.exports = appInfo => {
   
  return {
   
    logger: {
   
      dir: path.join(appInfo.baseDir, 'logs'),
    },
  };
};

内置的 appInfo 有:

appInfo 说明
pkg package.json
name 应用名,同 pkg.name
baseDir 应用代码的目录
HOME 用户目录,如 admin 账户为 /home/admin
root 应用根目录,只有在 local 和 unittest 环境下为 baseDir,其他都为 HOME。

appInfo.root 是一个优雅的适配,比如在服务器环境我们会使用 /home/admin/logs 作为日志目录,而本地开发时又不想污染用户目录,这样的适配就很好解决这个问题。

合并规则

配置的合并使用 extend2 模块进行深度拷贝

const a = {
   
  arr: [ 1, 2 ],
};
const b = {
   
  arr: [ 3 ],
};
extend(true, a, b);
// => { arr: [ 3 ] }
// 框架直接覆盖数组而不是进行合并。

配置结果

框架在启动时会把合并后的最终配置 dump 到 run/application_config.json(worker 进程)和 run/agent_config.json(agent 进程)中,可以用来分析问题。

中间件(Middleware)

Egg 是基于 Koa 实现的,所以 Egg 的中间件形式和 Koa 的中间件形式是一样的,都是基于洋葱圈模型。每次我们编写一个中间件,就相当于在洋葱外面包了一层。

编写中间件

写法

中间件内容的写法

// app/middleware/gzip.js
const isJSON = require('koa-is-json');
const zlib = require('zlib');

async function gzip(ctx, next) {
   
  await next();

  // 后续中间件执行完成后将响应体转换成 gzip
  let body = ctx.body;
  if (!body) return;
  if (isJSON(body)) body = JSON.stringify(body);

  // 设置 gzip body,修正响应头
  const stream = zlib.createGzip();
  stream.end(body);
  ctx.body = stream;
  ctx.set('Content-Encoding', 'gzip');
}

框架的中间件和 Koa 的中间件写法是一模一样的,所以任何 Koa 的中间件都可以直接被框架使用。

配置

一般来说中间件也会有自己的配置。

我们约定一个中间件是一个放置在 app/middleware 目录下的单独文件,它需要 exports 一个普通的 function,接受两个参数:

  • options: 中间件的配置项,框架会将 app.config[${middlewareName}] 传递进来,所以可以直接获取到配置文件中的中间件配置
  • app: 当前应用 Application 的实例

将上面的 gzip 中间件做一个简单的优化,让它支持指定只有当 body 大于配置的 threshold 时才进行 gzip 压缩,我们要在 app/middleware 目录下新建一个文件 gzip.js

// app/middleware/gzip.js
const isJSON = require('koa-is-json');
const zlib = require('zlib');

module.exports = options => {
   
  return async function gzip(ctx, next) {
   
    await next();

    // 后续中间件执行完成后将响应体转换成 gzip
    let body = ctx.body;
    if (!body) return;

    // 支持 options.threshold
    if (options.threshold && ctx.length < options.threshold) return;

    if (isJSON(body)) body = JSON.stringify(body);

    // 设置 gzip body,修正响应头
    const stream = zlib.createGzip();
    stream.end(body);
    ctx.body = stream;
    ctx.set('Content-Encoding', 'gzip');
  };
};

使用中间件

中间件编写完成后,我们还需要手动挂载

在应用中使用中间件

config.default.js 中加入下面的配置就完成了中间件的开启和配置

module.exports = {
   
  // 配置需要的中间件,数组顺序即为中间件的加载顺序
  middleware: [ 'gzip' ],

  // 配置 gzip 中间件的配置
  gzip: {
   
    threshold: 1024, // 小于 1k 的响应体不压缩
  },
  // options.gzip.threshold
};

该配置最终将在启动时合并到 app.config.appMiddleware

在框架和插件中使用中间件

框架和插件不支持在 config.default.js 中匹配 middleware,需要通过以下方式:

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

// app/middleware/report.js
module.exports = () => {
   
  return async function (ctx, next) {
   
    const startTime = Date.now();
    await next();
    // 上报请求时间
    reportTime(Date.now() - startTime);
  }
};

应用层定义的中间件(app.config.appMiddleware)和框架默认中间件(app.config.coreMiddleware)都会被加载器加载,并挂载到 app.middleware 上。

router 中使用中间件

以上两种方式配置的中间件是全局的,会处理每一次请求

如果你只想针对单个路由生效,可以直接在 app/router.js 中实例化和挂载,如下:

module.exports = app => {
   
 const gzip = app.middleware.gzip({
    threshold: 1024 });
 app.router.get('/needgzip', gzip, app.controller.handler);
};

框架默认中间件

除了应用层加载中间件之外,框架自身和其他的插件也会加载许多中间件。

所有的这些自带中间件的配置项都通过在配置中修改中间件同名配置项进行修改。

框架自带的中间件中有一个 bodyParser 中间件(框架的加载器会将文件名中的各种分隔符都修改成驼峰形式的变量名),我们想要修改 bodyParser 的配置,只需要在 config/config.default.js 中编写

module.exports = {
   
  bodyParser: {
   
    jsonLimit: '10mb',
  },
};

使用 Koa 的中间件

在框架里面可以非常容易的引入 Koa 中间件生态。

以 koa-compress 为例,在 Koa 中使用时:

const koa = require('koa');
const compress = require('koa-compress');

const app = koa();

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值