• 插件
  • 为什么要插件
    • 中间件、插件、应用的关系
  • 使用插件
    • 参数介绍
    • 开启和关闭
    • 根据环境配置
    • package 和 path
  • 插件配置
  • 插件列表
  • 如何开发一个插件

    插件

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

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

    接下来我们就来逐一讨论

    为什么要插件

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

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

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

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

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

    • 它包含了 Service、中间件、配置、框架扩展等等。
    • 它没有独立的 Router 和 Controller。
    • 它没有 plugin.js,只能声明跟其他插件的依赖,而不能决定其他插件的开启与否。

    他们的关系是:

    • 应用可以直接引入 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. };

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

    插件列表

    • 框架默认内置了企业级应用常用的插件:
      • onerror 统一异常处理
      • Session Session 实现
      • i18n 多语言
      • watcher 文件和文件夹监控
      • multipart 文件流式上传
      • security 安全
      • development 开发环境配置
      • logrotator 日志切分
      • schedule 定时任务
      • static 静态服务器
      • jsonp jsonp 支持
      • view 模板引擎
    • 更多社区的插件可以 GitHub 搜索 egg-plugin。

    如何开发一个插件

    参见文档:插件开发。