基于Egg.js定制業(yè)務(wù)Web框架(一) - 框架擴(kuò)展
前言
談到Node.js Web開發(fā),你會想到的是什么?
- 首先是框架選型:Express、Koa、NestJS、EggJS等,需要從繁多的框架中選擇一個(gè)適合團(tuán)隊(duì)的。
 - 框架選好之后,需要初始化項(xiàng)目,安裝依賴、插件、中間件、配置常見庫;
 - 假如再一次做新項(xiàng)目,還得從頭初始化再來一次。好一點(diǎn)的可以封裝一個(gè)骨架,從骨架中生成新項(xiàng)目,周而復(fù)始。
 
我們先想想這樣做的缺陷是什么?
- 如果沒有骨架,團(tuán)隊(duì)成員技術(shù)選型不一致,變成遍地開花;每個(gè)新項(xiàng)目維護(hù)都有學(xué)習(xí)成本。
 - 即便有骨架,發(fā)生一些升級也需要每個(gè)項(xiàng)目都統(tǒng)一升級一遍,后續(xù)也會導(dǎo)致不統(tǒng)一。
 
基于以上的原因,我們考慮的解法是:基于通用框架封裝一個(gè)統(tǒng)一的業(yè)務(wù)框架,將團(tuán)隊(duì)用到的公共功能下沉到業(yè)務(wù)框架中。
業(yè)務(wù)框架封裝
框架選型
為了選擇合適的底層框架,小編整理了awesome-nodejs的倉庫(每個(gè)分類按github star數(shù)排序),從Web框架部分可以看到非常豐富,那么我們?nèi)绾芜x擇。
awesome-nodejs地址: https://github.com/huaize2020/awesome-nodejs/blob/main/README-zh-CN.md
主流的Web框架選型主要分為兩大流派,基于Express 和 基于Koa,他們都是由同一團(tuán)隊(duì)打造。Express和Koa在設(shè)計(jì)思路上的區(qū)別在于:
- Express本身功能內(nèi)置了很多中間件,集成度高,使用省心,上手即用;
 - Koa使用精簡的內(nèi)核,更輕量,使用需要使用者做技術(shù)選型按需搭建積木;
 
基于以上的設(shè)計(jì)思路上的差別,基于對靈活配置型的考慮,筆者選擇了 基于Koa 的 流派。但他們都太底層,對于搭建企業(yè)級業(yè)務(wù)框架還需要做較多定制。
Egg.js是什么
基于上述的訴求,最后小編選擇了Egg.js。
Egg.js地址:https://eggjs.org/zh-cn/
Egg.js是阿里開源的基于Koa2企業(yè)級Node.JS框架,其核心設(shè)計(jì)就是希望基于Egg.js孕育出更多上層框架。
引用官網(wǎng)的一句話
- 我們深知企業(yè)級應(yīng)用在追求規(guī)范和共建的同時(shí),還需要考慮如何平衡不同團(tuán)隊(duì)之間的差異,求同存異。所以我們沒有選擇社區(qū)常見框架的大集市模式(集成如數(shù)據(jù)庫、模板引擎、前端框架等功能),而是專注于提供 Web 開發(fā)的核心功能和一套靈活可擴(kuò)展的插件機(jī)制?!?mdash;—官網(wǎng)」
 
定制目標(biāo)
在開始定制業(yè)務(wù)框架前,我們先設(shè)定一下需要定制的目標(biāo)。
- 擴(kuò)展Controller,添加API成功/失敗返回結(jié)果函數(shù);
 - 設(shè)置默認(rèn)模板引擎為 pug;
 - 擴(kuò)展ctx,添加util, 將dayjs作為統(tǒng)一日期處理庫;
 
編碼
如果對Egg.js不太熟悉,建議可以先學(xué)習(xí)下Egg.js的基本使用。
框架初始化
通過以下命令,初始化項(xiàng)目。其中 --type=framework 表示框架骨架
villa-framework為框架名和項(xiàng)目文件夾名
- $ npm init egg --type=framework villa-framework
 
以下是初始化出來的目錄
- villa-framework
 - ├── app
 - │ ├── extend
 - │ └── service
 - ├── config
 - │ ├── config.default.js
 - │ └── plugin.js
 - ├── lib
 - │ └── framework.js
 - ├── test
 - │ ├── fixtures
 - │ └── framework.test.js
 - ├── README.md
 - ├── index.js
 - └── package.json
 
其中app、config目錄基本跟正常的Egg.js應(yīng)用無差異,但增加了framework.js文件,對應(yīng)用進(jìn)行擴(kuò)展。
- // lib/framework.js
 - 'use strict';
 - const path = require('path');
 - const egg = require('egg');
 - const EGG_PATH = Symbol.for('egg#eggPath');
 - class Application extends egg.Application {
 - get [EGG_PATH]() {
 - return path.dirname(__dirname);
 - }
 - }
 - class Agent extends egg.Agent {
 - get [EGG_PATH]() {
 - return path.dirname(__dirname);
 - }
 - }
 - module.exports = Object.assign(egg, {
 - Application,
 - Agent,
 - });
 
擴(kuò)展Controller,添加成功失敗返回結(jié)果處理函數(shù)
- // 以下是Egg.js Controller使用的代碼
 - const Controller = require('egg').Controller;
 - // 未來修改為
 - const Controller = require('villa-framework').Controller;
 
由上面源碼我們知道,Controller來自egg對象,因此我們只需要替換為我們定義的Controller即可。
- // lib/framework.js 增加
 - class Controller extends egg.Controller {
 - ok(data) {
 - this.ctx.body = {
 - success: true,
 - data,
 - };
 - }
 - fail(message, code) {
 - this.ctx.body = {
 - code,
 - message,
 - };
 - }
 - }
 - // 導(dǎo)出增加Controller
 - module.exports = Object.assign(egg, {
 - Application,
 - Agent,
 - Controller,
 - });
 
這樣用戶使用的Controller就變成框架的Controller。
模板引擎設(shè)置為 pug
安裝依賴
- $ npm i --save egg-view-pug
 
啟動插件
- // config/plugin.js
 - exports.pug = {
 - enable: true,
 - package: 'egg-view-pug',
 - };
 
設(shè)置view渲染
- // config/config.default.js
 - config.view = {
 - mapping: {
 - '.pug': 'pug',
 - },
 - };
 
這樣應(yīng)用框架就不需要再單獨(dú)安裝配置,默認(rèn)擁有pug模板引擎能力。不同的業(yè)務(wù)可以根據(jù)自己的需求,默認(rèn)加載并配置一些默認(rèn)插件。
擴(kuò)展ctx.util, 將dayjs作為統(tǒng)一日期處理庫
安裝依賴
- $ npm i --save dayjs
 
app文件夾中新建util文件,添加day.js并導(dǎo)出
- // app/util/dayjs.js
 - 'use strict';
 - const dayjs = require('dayjs');
 - exports.dayjs = dayjs;
 
因?yàn)樾略龅膗til不會默認(rèn)加載,配置自定義加載器。
- // config/config.default.js
 - config.customLoader = {
 - // 定義在 app 上的屬性名 app.util
 - util: {
 - directory: 'app/util',
 - // 如果是 ctx 則使用 loadToContext
 - inject: 'ctx',
 - // 是否加載框架和插件的目錄
 - loadunit: true,
 - },
 - };
 
這樣框架就會默認(rèn)加載util。
應(yīng)用創(chuàng)建
接下來我們創(chuàng)建應(yīng)用,使用上面的villa-framework
初始化應(yīng)用
- $ npm init egg --type=simple villa-project
 - $ npm i --save villa-framework
 - $ npm i
 - $ npm run dev
 
將Egg.js上層框架修改為villa-framework
package.json中egg字段中添加 "framework": "villa-framework"
- "egg": {
 - "framework": "villa-framework",
 - },
 
修改controller進(jìn)行測試
- 'use strict';
 - // app/controller/home.js
 - const Controller = require('villa-framework').Controller;
 - class HomeController extends Controller {
 - async index() {
 - const { dayjs } = this.ctx.util.dayjs;
 - const now = dayjs().format('YYYY-MM-DD HH:mm:ss');
 - await this.ctx.render('index.pug', { now });
 - }
 - renderOk() {
 - this.ok('hi, egg');
 - }
 - renderFail() {
 - this.fail('fail', 200);
 - }
 - }
 - module.exports = HomeController;
 
添加路由
- 'use strict';
 - // app/router.js
 - /**
 - * @param {Egg.Application} app - egg application
 - */
 - module.exports = (app) => {
 - const { router, controller } = app;
 - router.get('/', controller.home.index);
 - router.get('/ok', controller.home.renderOk);
 - router.get('/fail', controller.home.renderFail);
 - };
 
添加首頁模板
在app中創(chuàng)建view文件夾,并添加index.pug
- html
 - head
 - title hello world
 - body
 - p hello world
 - p #{now}
 
啟動后即可看到演示的結(jié)果。
打開 http:///127.0.0.1:7001
打開 http:///127.0.0.1:7001/ok
打開 http:///127.0.0.1:7001/fail
總結(jié)
以上通過實(shí)現(xiàn)擴(kuò)展Controller,擴(kuò)展ctx,以及添加默認(rèn)插件,演示了對Egg.js基本的擴(kuò)展。
當(dāng)然對于一個(gè)完整的業(yè)務(wù)框架,還缺少很多東西,但以上提供了一種對Egg.js的擴(kuò)展的思路,希望可以為你擴(kuò)展提供思路。
當(dāng)前系列作為《基于Egg.js定制業(yè)務(wù)Web框架》系列一, 后續(xù)將持續(xù)輸出:定制IoC容器,定制注解系統(tǒng)、monorepo改造、框架CLI系列,敬請關(guān)注。
框架地址:https://github.com/huaize2020/blog/tree/main/projects/villa-v1/villa-framework
應(yīng)用調(diào)用地址:https://github.com/huaize2020/blog/tree/main/projects/villa-v1/villa-project
awesome-nodejs: https://github.com/huaize2020/awesome-nodejs/blob/main/README-zh-CN.md


















 
 
 






 
 
 
 