是一個node.js的後臺web框架,相似的還有express,koa
優點:規範、插件機制
Egg.js約定了一套代碼目錄結構
(配置config、路由router、擴展extend、中間件middleware、控制器controller)
規範的目錄結構,可使得不一樣團隊的開發者使用框架寫出來的代碼風格會更一致,接手成本也會更低。html
BFF層(先後端之間的中間層)、全棧、SSR(服務端渲染)node
框架約定的目錄
app/router.js 用於配置URL路由規則 app/contorller/** 用於解析用戶的輸入,處理後返回相應的結果 app/service/** 用於編寫業務邏輯層 【可選】 app/middleware/** 用於編寫中間件 【可選】 app/service/** 用於框架的擴展 【可選】 ... 自定義的目錄 app/view/** 用於防止模板文件 【可選】
Egg 將應用、框架和插件都稱爲加載單元(loadUnit);
在加載過程當中,Egg會遍歷全部的加載單元,加載時有一點的優先級
· 插件 => 框架 => 應用依次加載
· 依賴方先加載
· 框架按繼承順序加載,越底層越先加載web
若有這樣一個應用配置了以下依賴數據庫
app | ├── plugin2 (依賴 plugin3) | └── plugin3 └── framework1 | └── plugin1 └── egg
最終的加載順序爲express
=> plugin1 => plugin3 => plugin2 => egg => framework1 => app
文件加載順序
package.json => config => app/extend => app.js => app/service => app/middleware => app/controller => app/router.js
首先咱們須要配置路由
由於咱們在實際的開發中會使用不少路由因此這裏咱們將路由改爲分級的
在app下建立一個router文件夾用來存放路由文件home.js
//app/router/home.js 'use strict' module exports = app => { const { home } = app.controller //獲取controller下的home.js app.get('/',home.index); app.get('/home/param',home.getParam); app.get('/home/postParam',home.postParam); } // app/router.js 'use strict' const RouteHome = require('./router/home'); module.exports = { const {router, controller} = app; RouteHome(app); } //app/controller/home.js 'use strict' const Controller = require('egg').Controller; class HomeController extends Controller { async index() { await this.ctx.render('/index',{name:'egg'}); } async getParam() { let id = await this.ctx.query.id; this.ctx.body = id; } async postParam(){ let id = await this.ctx.request.body.id; //獲取post參數 this.ctx.body = id; } } module exports = HomeController;
咱們經過 Router 將用戶的請求基於 method 和 URL 分發到了對應的 Controller 上, Controller 負責解析用戶的輸入,處理後返回相應的結果
框架推薦 Controller 層主要對用戶的請求參數進行處理(校驗、轉換),而後調用對應的 service 方法處理業務,獲得業務結果後封裝並返回
//在app/controller目錄下 新建一個controller 'use strict' const Controller = required('egg').Controller; class CustomerController extends Controller { async customIndex() { //ctx.body 是 ctx.response.body 的簡寫,不要和 ctx.request.body 混淆了 this.ctx.body = 'this is my controller'; } } module.exports = CustomController; //在router.js中配置路由(訪問時請求的路徑) 'use strict' module.exports = app => { //至關於拿到app文件夾下的router和controller const {router, controller} = app; router.get('/', controller.home.index); router.get('/custom',controller.customerController.customIndex); }
定義的 Controller 類,會在每個請求訪問到 server 時實例化一個全新的對象,而項目中的 Controller 類繼承於 egg.Controller,會有下面幾個屬性掛在 this 上
- this.ctx 框架封裝好的處理當前請求的各類便捷屬性和方法 - this.app 框架提供的全局對象和方法 - this.service 訪問抽象出的業務層 至關於 this.ctx.service - this.config 運行的配置項 - this.logger 日誌
業務邏輯層
'use strict'; const Service = require('egg').Service; class customService extends Service { async show(zc, hh) { //異步防阻塞 return zc + " and " + hh; } } module.exports = UserService; //controller代碼 'use strict'; const Controller = require('egg').Controller; class CustomController extends Controller { async custonIndex() { let str = await this.ctx.service.customService.show('zc','hh'); //這裏使用await來獲取異步方法的返回值 this.ctx.body = 'this is my controller'+str; } } module.exports = CustomController;
一個更完整的栗子
// app/router.js module.exports = app => { app.router.get('/user/:id', app.controller.user.info); }; // app/controller/user.js const Controller = require('egg').Controller; class UserController extends Controller { async info() { const userId = ctx.params.id; const userInfo = await ctx.service.user.find(userId); ctx.body = userInfo; } } module.exports = UserController; // app/service/user.js const Service = require('egg').Service; class UserService extends Service { // 默認不須要提供構造函數。 // constructor(ctx) { // super(ctx); 若是須要在構造函數作一些處理,必定要有這句話,才能保證後面 `this.ctx`的使用。 // // 就能夠直接經過 this.ctx 獲取 ctx 了 // // 還能夠直接經過 this.app 獲取 app 了 // } async find(uid) { // 假如 咱們拿到用戶 id 從數據庫獲取用戶詳細信息 const user = await this.ctx.db.query('select * from user where uid = ?', uid); // 假定這裏還有一些複雜的計算,而後返回須要的信息。 const picture = await this.getPicture(uid); return { name: user.user_name, age: user.age, picture, }; } async getPicture(uid) { const result = await this.ctx.curl(`http://photoserver/uid=${uid}`, { dataType: 'json' }); return result.data; } } module.exports = UserService; // curl http://127.0.0.1:7001/user/1234
app/extend文件夾裏面存放工具類
//app/extend/getName.js 'use strict' module.exports = { getUserName(id) { return list.find(i=>i.id===id).name; } } //app/extend/helper.js 'use strict' const getName = require('./getName'); module.exports = { showName() { return getName.getUserName('2221'); } } //controller引用helper 'use strict' const Controller = require('egg').Controller; class CustomController extends Controller { async customIndex() { ////this.ctx.helper拿到helper內置對象也就是進入helper.js這個文件 this.ctx.body = this.ctx.helper.showName(); } } module.exports = CustomController;
egg.js使用的是nunjucks頁面模板
//在config/plugin.js裏面添加 'use strict' exports.nunjucks = { enable: true, package: 'egg-view-nunjucks' } //config/config/default.js 添加 'use strict' ... module.exports = app => { ... config.view = { mapping: { '.html': 'nunjucks' }, root: path.join(appInfo.baseDir, 'app/view') } ... return config; } //app/routes/sign.js 'use strict'; module.exports = (router, controller) => { router.get('/sign/modifyPassword', controller.sign.modifyPassword); }; //app/controller/sign.js 'use strict'; const Controller = require('egg').Controller; class SignController extends Controller { async modifyPassword() { const { ctx } = this; //渲染view/sign/modifyPassword.html await ctx.render('sign/modifyPassword.html'); } } module.exports = SignController;