在 Egg 里面,有插件,也有框架,前者還包括了 path 和 package 兩種加載模式,那我們應(yīng)該如何選擇呢?
本文將以實例的方式,一步步給大家演示下,如何漸進式地進行代碼演進。
全部的示例代碼可以參見 eggjs/examples/progressive。
最初始的狀態(tài)
假設(shè)我們有一段分析 UA 的代碼,實現(xiàn)以下功能:
通過之前的教程,大家一定可以很快地寫出來,我們快速回顧下:
對應(yīng)的代碼參見 step1。
目錄結(jié)構(gòu):
example-app ├── app │ ├── extend │ │ └── context.js │ └── router.js ├── test │ └── index.test.js └── package.json
|
核心代碼:
// app/extend/context.js module.exports = { get isIOS() { const iosReg = /iphone|ipad|ipod/i; return iosReg.test(this.get('user-agent')); }, };
|
插件的雛形
我們很明顯能感知到,這段邏輯是具備通用性的,可以寫成插件。
但一開始的時候,功能還沒完善,直接獨立插件,維護起來比較麻煩。
此時,我們可以把代碼寫成插件的形式,但并不獨立出去。
對應(yīng)的代碼參見 step2。
新的目錄結(jié)構(gòu):
example-app ├── app │ └── router.js ├── config │ └── plugin.js ├── lib │ └── plugin │ └── egg-ua │ ├── app │ │ └── extend │ │ └── context.js │ └── package.json ├── test │ └── index.test.js └── package.json
|
核心代碼:
- app/extend/context.js 移動到 lib/plugin/egg-ua/app/extend/context.js。
- lib/plugin/egg-ua/package.json 聲明插件。
{ "eggPlugin": { "name": "ua" } }
|
- config/plugin.js 中通過 path 來掛載插件。
// config/plugin.js const path = require('path'); exports.ua = { enable: true, path: path.join(__dirname, '../lib/plugin/egg-ua'), };
|
抽成獨立插件
經(jīng)過一段時間開發(fā)后,該模塊的功能成熟,此時可以考慮抽出來成為獨立的插件。
首先,我們抽出一個 egg-ua 插件,看過插件文檔的同學(xué)應(yīng)該都比較熟悉,我們這里只簡單過一下:
目錄結(jié)構(gòu):
egg-ua ├── app │ └── extend │ └── context.js ├── test │ ├── fixtures │ │ └── test-app │ │ ├── app │ │ │ └── router.js │ │ └── package.json │ └── ua.test.js └── package.json
|
對應(yīng)的代碼參見 step3/egg-ua。
然后改造原有的應(yīng)用,對應(yīng)的代碼參見 step3/example-app。
- 移除 lib/plugin/egg-ua 目錄。
- package.json 中聲明對 egg-ua 的依賴。
- config/plugin.js 中修改依賴聲明為 package 方式。
// config/plugin.js exports.ua = { enable: true, package: 'egg-ua', };
|
注意:在插件還沒發(fā)布前,可以通過 npm link 的方式進行本地測試,具體參見 npm-link。
$ cd example-app $ npm link ../egg-ua $ npm i $ npm test
|
沉淀到框架
重復(fù)上述的過程,很快我們會積累了好幾個插件和配置,并且我們會發(fā)現(xiàn),在團隊的大部分項目中,都會用到這些插件。
此時,就可以考慮抽象出一個適合團隊業(yè)務(wù)場景的框架。
首先,抽象出 example-framework 框架,如上看過框架文檔的同學(xué)應(yīng)該都比較熟悉,我們這里只簡單過一下:
目錄結(jié)構(gòu):
example-framework ├── config │ ├── config.default.js │ └── plugin.js ├── lib │ ├── agent.js │ └── application.js ├── test │ ├── fixtures │ │ └── test-app │ └── framework.test.js ├── README.md ├── index.js └── package.json
|
- 對應(yīng)的代碼參見 example-framework。
- 把原來的 egg-ua 等插件的依賴,從 example-app 中移除,配置到該框架的 package.json 和 config/plugin.js 中。
然后改造原有的應(yīng)用,對應(yīng)的代碼參見 step4/example-app。
- 移除 config/plugin.js 中對 egg-ua 的依賴。
- package.json 中移除對 egg-ua 的依賴。
- package.json 中聲明對 example-framework 的依賴,并配置 egg.framework。
{ "name": "progressive", "version": "1.0.0", "private": true, "egg": { "framework": "example-framework" }, "dependencies": { "example-framework": "*" } }
|
注意:在框架還沒發(fā)布前,可以通過 npm link 的方式進行本地測試,具體參見 npm-link。
$ cd example-app $ npm link ../egg-framework $ npm i $ npm test
|
寫在最后
綜上所述,大家可以看到我們是如何一步步漸進地去進行框架演進,這得益于 Egg 強大的插件機制、代碼的共建,以及復(fù)用和下沉,這些步驟竟然可以這么地無痛來得以完成!
- 一般來說,當應(yīng)用中有可能會復(fù)用到的代碼時,直接放到 lib/plugin 目錄去,如例子中的 egg-ua。
- 當該插件功能穩(wěn)定后,即可獨立出來作為一個 node module 。
- 如此以往,應(yīng)用中相對復(fù)用性較強的代碼都會逐漸獨立為單獨的插件。
- 當你的應(yīng)用逐漸進化到針對某類業(yè)務(wù)場景的解決方案時,將其抽象為獨立的 framework 進行發(fā)布。
- 當在新項目中抽象出的插件,下沉集成到框架后,其他項目只需要簡單的重新 npm install 下就可以使用上,對整個團隊的效率有極大的提升。
- 注意:不管是應(yīng)用/插件/框架,都必須編寫單元測試,并盡量實現(xiàn) 100% 覆蓋率。
更多建議: