原文链接
在 Egg 里面,有插件,也有框架,前者还包括了 path 和 package 两种加载模式,那我们应该如何选择呢?
本文将以实例的方式,一步步给大家演示下,如何渐进式地进行代码演进。全部的示例代码可以参见 eggjs/examples/progressive。
1. 最初始的状态
假设我们有一段分析 UA 的代码,实现以下功能:
ctx.isAndroidctx.isIOS
通过之前的教程,大家一定可以很快地写出来,我们快速回顾下:对应的代码参见 step1。
目录结构:
example-app├── app│ ├── extend│ │ └── context.js│ └── router.js├── test│ └── index.test.js└── package.json
核心代码:
// app/extend/context.jsmodule.exports = {get isIOS() {const iosReg = /iphone|ipad|ipod/i;return iosReg.test(this.get('user-agent'));},};
2. 插件的雏形
我们很明显能感知到,这段逻辑是具备通用性的,可以写成插件。但一开始的时候,功能还没完善,直接独立插件,维护起来比较麻烦。
此时,我们可以把代码写成插件的形式,但并不独立出去。对应的代码参见 step2。
新的目录结构:
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.jsconst path = require('path');exports.ua = {enable: true,path: path.join(__dirname, '../lib/plugin/egg-ua'),};
注:可见插件代码是写在
lib/plugin/目录下。
3. 抽成独立插件
经过一段时间开发后,该模块的功能成熟,此时可以考虑抽出来成为独立的插件。
首先,我们抽出一个 egg-ua 插件,看过插件文档的同学应该都比较熟悉,我们这里只简单过一下:
目录结构:
egg-ua├── app│ └── extend│ └── context.js├── test│ ├── fixtures│ │ └── test-app│ │ ├── app│ │ │ └── router.js│ │ └── package.json│ └── ua.test.js└── package.json
对应的代码参见 step3/egg-ua。
然后改造原有的应用,对应的代码参见 step3/example-app。
- 移除
lib/plugin/egg-ua目录。 package.json中声明对egg-ua的依赖。config/plugin.js中修改依赖声明为**package**方式。
// config/plugin.jsexports.ua = {enable: true,package: 'egg-ua',};
注意:在插件还没发布前,可以通过 **npm link** 的方式进行本地测试,具体参见 npm-link。
$ cd example-app$ npm link ../egg-ua$ npm i$ npm test
4. 沉淀到框架
重复上述的过程,很快我们会积累了好几个插件和配置,并且我们会发现,在团队的大部分项目中,都会用到这些插件。此时,就可以考虑抽象出一个适合团队业务场景的框架。
首先,抽象出 example-framework 框架,如上看过框架文档的同学应该都比较熟悉,我们这里只简单过一下:
目录结构:
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
- 对应的代码参见 example-framework。
- 把原来的
egg-ua等插件的依赖,从 example-app 中移除,配置到该框架的package.json和config/plugin.js中。
然后改造原有的应用,对应的代码参见 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": "*"}}
注意:在框架还没发布前,可以通过 **npm link** 的方式进行本地测试,具体参见 npm-link。
$ cd example-app$ npm link ../egg-framework$ npm i$ npm test
5. 写在最后
综上所述,大家可以看到我们是如何一步步渐进地去进行框架演进,这得益于 Egg 强大的插件机制、代码的共建,以及复用和下沉,这些步骤竟然可以这么地无痛来得以完成!
- 一般来说,当应用中有可能会复用到的代码时,直接放到
lib/plugin目录去,如例子中的egg-ua。 - 当该插件功能稳定后,即可独立出来作为一个
node module。 - 如此以往,应用中相对复用性较强的代码都会逐渐独立为单独的插件。
- 当你的应用逐渐进化到针对某类业务场景的解决方案时,将其抽象为独立的 framework 进行发布。
- 当在新项目中抽象出的插件,下沉集成到框架后,其他项目只需要简单的重新
npm install下就可以使用上,对整个团队的效率有极大的提升。 - 注意:不管是应用/插件/框架,都必须编写单元测试,并尽量实现 100% 覆盖率。
