Egg 漸進(jìn)式開發(fā)

2020-02-06 10:54 更新

在 Egg 里面,有插件,也有框架,前者還包括了 path 和 package 兩種加載模式,那我們應(yīng)該如何選擇呢?

本文將以實(shí)例的方式,一步步給大家演示下,如何漸進(jìn)式地進(jìn)行代碼演進(jìn)。

全部的示例代碼可以參見 eggjs/examples/progressive。

最初始的狀態(tài)

假設(shè)我們有一段分析 UA 的代碼,實(shí)現(xiàn)以下功能:

  • ctx.isAndroid
  • ctx.isIOS

通過之前的教程,大家一定可以很快地寫出來,我們快速回顧下:

對(duì)應(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'));
},
};

插件的雛形

我們很明顯能感知到,這段邏輯是具備通用性的,可以寫成插件。

但一開始的時(shí)候,功能還沒完善,直接獨(dú)立插件,維護(hù)起來比較麻煩。

此時(shí),我們可以把代碼寫成插件的形式,但并不獨(dú)立出去。

對(duì)應(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 移動(dòng)到 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'),
};

抽成獨(dú)立插件

經(jīng)過一段時(shí)間開發(fā)后,該模塊的功能成熟,此時(shí)可以考慮抽出來成為獨(dú)立的插件。

首先,我們抽出一個(gè) 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

對(duì)應(yīng)的代碼參見 step3/egg-ua。

然后改造原有的應(yīng)用,對(duì)應(yīng)的代碼參見 step3/example-app

  • 移除 lib/plugin/egg-ua 目錄。
  • package.json 中聲明對(duì) egg-ua 的依賴。
  • config/plugin.js 中修改依賴聲明為 package 方式。
// config/plugin.js
exports.ua = {
enable: true,
package: 'egg-ua',
};

注意:在插件還沒發(fā)布前,可以通過 npm link 的方式進(jìn)行本地測(cè)試,具體參見 npm-link。

$ cd example-app
$ npm link ../egg-ua
$ npm i
$ npm test

沉淀到框架

重復(fù)上述的過程,很快我們會(huì)積累了好幾個(gè)插件和配置,并且我們會(huì)發(fā)現(xiàn),在團(tuán)隊(duì)的大部分項(xiàng)目中,都會(huì)用到這些插件。

此時(shí),就可以考慮抽象出一個(gè)適合團(tuán)隊(duì)業(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
  • 對(duì)應(yīng)的代碼參見 example-framework
  • 把原來的 egg-ua 等插件的依賴,從 example-app 中移除,配置到該框架的 package.json 和 config/plugin.js 中。

然后改造原有的應(yīng)用,對(duì)應(yīng)的代碼參見 step4/example-app。

  • 移除 config/plugin.js 中對(duì) egg-ua 的依賴。
  • package.json 中移除對(duì) egg-ua 的依賴。
  • package.json 中聲明對(duì) example-framework 的依賴,并配置 egg.framework。
{
"name": "progressive",
"version": "1.0.0",
"private": true,
"egg": {
"framework": "example-framework"
},
"dependencies": {
"example-framework": "*"
}
}

注意:在框架還沒發(fā)布前,可以通過 npm link 的方式進(jìn)行本地測(cè)試,具體參見 npm-link。

$ cd example-app
$ npm link ../egg-framework
$ npm i
$ npm test

寫在最后

綜上所述,大家可以看到我們是如何一步步漸進(jìn)地去進(jìn)行框架演進(jìn),這得益于 Egg 強(qiáng)大的插件機(jī)制、代碼的共建,以及復(fù)用和下沉,這些步驟竟然可以這么地?zé)o痛來得以完成!

  • 一般來說,當(dāng)應(yīng)用中有可能會(huì)復(fù)用到的代碼時(shí),直接放到 lib/plugin 目錄去,如例子中的 egg-ua。
  • 當(dāng)該插件功能穩(wěn)定后,即可獨(dú)立出來作為一個(gè) node module 。
  • 如此以往,應(yīng)用中相對(duì)復(fù)用性較強(qiáng)的代碼都會(huì)逐漸獨(dú)立為單獨(dú)的插件。
  • 當(dāng)你的應(yīng)用逐漸進(jìn)化到針對(duì)某類業(yè)務(wù)場景的解決方案時(shí),將其抽象為獨(dú)立的 framework 進(jìn)行發(fā)布。
  • 當(dāng)在新項(xiàng)目中抽象出的插件,下沉集成到框架后,其他項(xiàng)目只需要簡單的重新 npm install 下就可以使用上,對(duì)整個(gè)團(tuán)隊(duì)的效率有極大的提升。
  • 注意:不管是應(yīng)用/插件/框架,都必須編寫單元測(cè)試,并盡量實(shí)現(xiàn) 100% 覆蓋率。


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)