插件

12003

插件机制是我们框架的一大特色。它不但可以保证框架核心的足够精简、稳定、高效,还可以促进业务逻辑的复用,生态圈的形成。有人可能会问了:

  • Koa 已经有了中间件的机制,为什么还要插件呢?
  • 中间件、插件、应用之间是什么关系,它们之间有什么区别?
  • 我该如何使用一个插件?
  • 如何编写一个插件?

接下来我们就来逐一讨论。

为什么要插件

我们在使用 Koa 中间件过程中发现了下面一些问题:

  1. 中间件加载其实是有先后顺序的,但是中间件自身却无法管理这种顺序,只能交给使用者。这样其实非常不友好,一旦顺序不对,结果可能有天壤之别。
  2. 中间件的定位是拦截用户请求,并在它前后做一些事情,例如:鉴权、安全检查、访问日志等等。但实际情况是,有些功能是和请求无关的,例如:定时任务、消息订阅、后台逻辑等等。
  3. 有些功能包含非常复杂的初始化逻辑,需要在应用启动的时候完成。这显然也不适合放到中间件中去实现。

综上所述,我们需要一套更加强大的机制,来管理、编排那些相对独立的业务逻辑。

中间件、插件、应用的关系

一个插件其实就是一个“迷你的应用”,和应用(app)几乎一样:

他们的关系是:

  • 应用可以直接引入 Koa 的中间件。
  • 当遇到上一节提到的场景时,应用需引入插件。
  • 插件本身可以包含中间件。
  • 多个插件可以包装为一个上层框架

    使用插件

插件通常通过 npm 模块的方式进行复用:

  1. $ npm i egg-mysql --save

注意:我们推荐通过 ^ 的方式引入依赖,并且强烈不建议锁定版本。

  1. {
  2. "dependencies": {
  3. "egg-mysql": "^3.0.0"
  4. }
  5. }

接着,需要在应用或框架的 config/plugin.js 中声明:

  1. // config/plugin.js
  2. // 使用 mysql 插件
  3. exports.mysql = {
  4. enable: true,
  5. package: 'egg-mysql',
  6. };

这样就可以直接使用插件提供的功能:

  1. app.mysql.query(sql, values);

参数介绍

plugin.js 中的每个配置项支持:

  • {Boolean} enable - 是否开启此插件,默认为 true
  • {String} package - npm 模块名称,通过 npm 模块形式引入插件
  • {String} path - 插件绝对路径,与 package 配置互斥
  • {Array} env - 只有在指定运行环境才能开启,会覆盖该插件自身 package.json 中的配置

开启和关闭

在上层框架内置的插件,应用在使用时,可以不配置 package 或者 path,只需指定 enable 即可:

  1. // 对于内置插件,可采用以下简洁方式开启或关闭
  2. exports.onerror = false;

根据环境配置

同时,我们还支持 plugin.{env}.js 的模式,会根据运行环境加载插件配置。

比如,如果定义了一个只在开发环境使用的插件 egg-dev,希望只在本地环境加载,可以将其安装到 devDependencies 中。

  1. // npm i egg-dev --save-dev
  2. // package.json
  3. {
  4. "devDependencies": {
  5. "egg-dev": "*"
  6. }
  7. }

接下来,在 plugin.local.js 中声明:

  1. // config/plugin.local.js
  2. exports.dev = {
  3. enable: true,
  4. package: 'egg-dev',
  5. };

这样,在生产环境下执行 npm i --production 时,就不需要下载 egg-dev 包了。

注意:

  • plugin.default.js 不存在
  • 只能在应用层使用,框架层请勿使用。

package 和 path

  • packagenpm 方式引入,也是最常见的引入方式
  • path 是绝对路径引入,例如应用内部提取了一个插件,但尚未发布至 npm;或者是应用自行改写了框架的某些插件
  • 关于这两种方式的使用场景,可参见渐进式开发文档。
  1. // config/plugin.js
  2. const path = require('path');
  3. exports.mysql = {
  4. enable: true,
  5. path: path.join(__dirname, '../lib/plugin/egg-mysql'),
  6. };

插件配置

插件一般会包含自己的默认配置。应用开发者可以在 config.default.js 中覆盖对应的配置:

  1. // config/config.default.js
  2. exports.mysql = {
  3. client: {
  4. host: 'mysql.com',
  5. port: '3306',
  6. user: 'test_user',
  7. password: 'test_password',
  8. database: 'test'
  9. }
  10. };

具体的合并规则可以参见配置

插件列表

如何开发一个插件

参见文档:插件开发