插件機(jī)制是我們框架的一大特色。它不但可以保證框架核心的足夠精簡(jiǎn)、穩(wěn)定、高效,還可以促進(jìn)業(yè)務(wù)邏輯的復(fù)用,生態(tài)圈的形成。有人可能會(huì)問(wèn)了
- Koa 已經(jīng)有了中間件的機(jī)制,為啥還要插件呢?
- 中間件、插件、應(yīng)用它們之間是什么關(guān)系,有什么區(qū)別?
- 我該怎么使用一個(gè)插件?
- 如何編寫一個(gè)插件?
- ...
接下來(lái)我們就來(lái)逐一討論
為什么要插件
我們?cè)谑褂?Koa 中間件過(guò)程中發(fā)現(xiàn)了下面一些問(wèn)題:
- 中間件加載其實(shí)是有先后順序的,但是中間件自身卻無(wú)法管理這種順序,只能交給使用者。這樣其實(shí)非常不友好,一旦順序不對(duì),結(jié)果可能有天壤之別。
- 中間件的定位是攔截用戶請(qǐng)求,并在它前后做一些事情,例如:鑒權(quán)、安全檢查、訪問(wèn)日志等等。但實(shí)際情況是,有些功能是和請(qǐng)求無(wú)關(guān)的,例如:定時(shí)任務(wù)、消息訂閱、后臺(tái)邏輯等等。
- 有些功能包含非常復(fù)雜的初始化邏輯,需要在應(yīng)用啟動(dòng)的時(shí)候完成。這顯然也不適合放到中間件中去實(shí)現(xiàn)。
綜上所述,我們需要一套更加強(qiáng)大的機(jī)制,來(lái)管理、編排那些相對(duì)獨(dú)立的業(yè)務(wù)邏輯。
中間件、插件、應(yīng)用的關(guān)系
一個(gè)插件其實(shí)就是一個(gè)『迷你的應(yīng)用』,和應(yīng)用(app)幾乎一樣:
他們的關(guān)系是:
- 應(yīng)用可以直接引入 Koa 的中間件。
- 當(dāng)遇到上一節(jié)提到的場(chǎng)景時(shí),則應(yīng)用需引入插件。
- 插件本身可以包含中間件。
- 多個(gè)插件可以包裝為一個(gè)上層框架。
使用插件
插件一般通過(guò) npm 模塊的方式進(jìn)行復(fù)用:
注意:我們建議通過(guò) ^ 的方式引入依賴,并且強(qiáng)烈不建議鎖定版本。
{ "dependencies": { "egg-mysql": "^3.0.0" } }
|
然后需要在應(yīng)用或框架的 config/plugin.js 中聲明:
// config/plugin.js // 使用 mysql 插件 exports.mysql = { enable: true, package: 'egg-mysql', };
|
就可以直接使用插件提供的功能:
app.mysql.query(sql, values);
|
參數(shù)介紹
plugin.js 中的每個(gè)配置項(xiàng)支持:
- {Boolean} enable - 是否開啟此插件,默認(rèn)為 true
- {String} package - npm 模塊名稱,通過(guò) npm 模塊形式引入插件
- {String} path - 插件絕對(duì)路徑,跟 package 配置互斥
- {Array} env - 只有在指定運(yùn)行環(huán)境才能開啟,會(huì)覆蓋插件自身 package.json 中的配置
開啟和關(guān)閉
在上層框架內(nèi)部?jī)?nèi)置的插件,應(yīng)用在使用時(shí)就不用配置 package 或者 path,只需要指定 enable 與否:
// 對(duì)于內(nèi)置插件,可以用下面的簡(jiǎn)潔方式開啟或關(guān)閉 exports.onerror = false;
|
根據(jù)環(huán)境配置
同時(shí),我們還支持 plugin.{env}.js 這種模式,會(huì)根據(jù)運(yùn)行環(huán)境加載插件配置。
比如定義了一個(gè)開發(fā)環(huán)境使用的插件 egg-dev,只希望在本地環(huán)境加載,可以安裝到 devDependencies。
// npm i egg-dev --save-dev // package.json { "devDependencies": { "egg-dev": "*" } }
|
然后在 plugin.local.js 中聲明:
// config/plugin.local.js exports.dev = { enable: true, package: 'egg-dev', };
|
這樣在生產(chǎn)環(huán)境可以 npm i --production 不需要下載 egg-dev 的包了。
注意:
- 不存在 plugin.default.js
- 只能在應(yīng)用層使用,在框架層請(qǐng)勿使用。
package 和 path
- package 是 npm 方式引入,也是最常見的引入方式
- path 是絕對(duì)路徑引入,如應(yīng)用內(nèi)部抽了一個(gè)插件,但還沒(méi)達(dá)到開源發(fā)布獨(dú)立 npm 的階段,或者是應(yīng)用自己覆蓋了框架的一些插件
- 關(guān)于這兩種方式的使用場(chǎng)景,可以參見漸進(jìn)式開發(fā)。
// config/plugin.js const path = require('path'); exports.mysql = { enable: true, path: path.join(__dirname, '../lib/plugin/egg-mysql'), };
|
插件配置
插件一般會(huì)包含自己的默認(rèn)配置,應(yīng)用開發(fā)者可以在 config.default.js 覆蓋對(duì)應(yīng)的配置:
// config/config.default.js exports.mysql = { client: { host: 'mysql.com', port: '3306', user: 'test_user', password: 'test_password', database: 'test', }, };
|
具體合并規(guī)則可以參見配置。
插件列表
如何開發(fā)一個(gè)插件
參見文檔:插件開發(fā)。
更多建議: