- 提供基于 Egg 定制上層架構的能力
- 高度可擴充的插件機制
- 内置多程序管理
- 基于 Koa 開發,性能優異
- 架構穩定,測試覆寫率高
- 漸進式開發
目錄結構
├── package.json
├── app.js (app.js 和 agent.js 用于自定義啟動時的初始化工作)
├── agent.js (可選)
├── app
| ├── router.js(用于配置 URL 路由規則)
│ ├── controller(用于解析使用者的輸入,處理後傳回相應的結果)
│ | └── home.js
│ ├── service (用于編寫業務邏輯層,可選)
│ | └── user.js
│ ├── middleware (用于編寫中間件,可選)
│ | └── response_time.js
│ ├── schedule (用于定時任務,可選)
│ | └── my_task.js
│ ├── public (用于放置靜态資源,可選)
│ | └── reset.css
│ ├── extend (用于架構的擴充,可選)
│ | └── application.js app 對象指的是 Koa 的全局應用對象,全局隻有一個,在應用啟動時被建立。
│ ├── context.js (Context 指的是 Koa 的請求上下文,這是 請求級别 的對象)
│ ├── request.js (Request 對象和 Koa 的 Request 對象相同,是 請求級别 的對象)
│ ├── response.js (Response 對象和 Koa 的 Response 對象相同,是 請求級别 的對象)
│ ├── helper.js (Helper 函數用來提供一些實用的 utility 函數)
│ ├── view (用于放置模闆檔案)
│ | └── home.tpl
├── |── model (用于放置領域模型)
│ | └── home.tpl
│ └── extend (用于架構的擴充)
│ ├── helper.js (可選)
│ ├── request.js (可選)
│ ├── response.js (可選)
│ ├── context.js (可選)
│ ├── application.js (可選)
│ └── agent.js (可選)
├── config(用于編寫配置檔案)
| ├── plugin.js(用于配置需要加載的插件)
| ├── config.default.js
│ ├── config.prod.js
| ├── config.test.js (可選)
| ├── config.local.js (可選)
| └── config.unittest.js (可選)
└── test(用于單元測試)
├── middleware
| └── response_time.test.js
└── controller
└── home.test.js
通路
檔案 app ctx service config logger helper
Controller this.app this.ctx this.service this.config this.logger this.app.helper
Service this.app this.ctx this.service this.config this.logger this.app.helper
初始化項目
mkdir egg-news
cd egg-news
npm init -y
cnpm i egg --save
cnpm i egg-bin --save-dev
-
添加 npm scripts 到 package.json:
“scripts”: {
“dev”: “egg-bin dev”
}
-
跑通路由
├─app
│ │─router.js
│ ├─controller
│ │ news.js
├─config
│ config.default.js
|─package.json
-
配置路由
// app/router.js
module.exports = app => { const { router, controller } = app; router.get('/news', controller.news.index); }
-
編寫控制器
//- app\controller\news.js
const { Controller } = require('egg'); class NewsController extends Controller { async index() { this.ctx.body = 'hello world'; } } module.exports = NewsController;
-
編寫配置檔案
exports.keys = ‘zfpx’;
靜态檔案中間件
-
Egg 内置了 static 插件
static 插件預設映射 /public/ -> app/public/ 目錄
把靜态資源都放到 app/public 目錄即可
bootcss
-
使用模闆引擎
├─app
│ │─router.js
│ ├─controller
│ │ news.js
│ ├─public
│ │ ├─css
│ │ │ bootstrap.css
│ │ └─js
│ │ bootstrap.js
│ └─view
│ news.ejs
├─config
│ config.default.js
│ plugin.js
-
安裝依賴的插件
cnpm install egg-view-nunjucks --save
-
啟用插件
//- {ROOT}\config\plugin.js
exports.nunjucks = { enable: true, package: 'egg-view-nunjucks' }
-
配置模闆
// - {ROOT}\config\config.default.js
module.exports=app => {
let config={};
config.keys=‘zfpx’;
config.view={
defaultExtension: ‘.html’,
defaultViewEngine: ‘nunjucks’,
mapping: {
‘.html’:‘nunjucks’
}
}
return config;
}
-
編寫模闆
{{title}} {% for news in list%}
{{news.title}}
{% endfor %} 6.5 編寫控制器 const {Controller}=require('egg'); class NewsController extends Controller{ async index() { const {ctx}=this; const list=[ { id: '45154322_0', title: '世界首富早晚是這個人,坐擁7家獨角獸公司,估值破數萬!', url: 'http://tech.ifeng.com/a/20180904/45154322_0.shtml', image:'http://p0.ifengimg.com/pmop/2018/0905/CFFF918B94D561D2A61FB434ADA81589E8972025_size41_w640_h479.jpeg' }, { id: '16491630_0', title: '支付寶們來了!将來人民币會消失嗎?', url: 'http://finance.ifeng.com/a/20180907/16491630_0.shtml', image:'http://p0.ifengimg.com/pmop/2018/0907/2AF684C2EC49B7E3C17FCB13D6DEEF08401D4567_size27_w530_h369.jpeg' }, { id: '2451982', title: '《福布斯》專訪貝索斯:無業務邊界的亞馬遜 令對手生畏的CEO', url: 'https://www.jiemian.com/article/2451982.html', image:'https://img1.jiemian.com/101/original/20180907/153628523948814900_a580x330.jpg' } ]; await ctx.render('index',{list}); } } module.exports=NewsController;
讀取遠端接口服務
在實際應用中,Controller 一般不會自己産出資料,也不會包含複雜的邏輯,複雜的過程應抽象為業務邏輯層 Service。
-
添加配置
// - config.default.js
config.news={ newsListUrl: 'https://www.easy-mock.com/mock/5b923eb2321f1076a4fc13f4/api/news', }
-
編寫Service
// - const {Service}=require(‘egg’);
class NewsService extends Service {
async list(pageNum,pageSize) {
const {ctx}=this;
const {newsListUrl}=this.config.news;
const result=await ctx.curl(newsListUrl,{
method: ‘GET’,
data: {
pageNum,pageSize
},
dataType:‘json’
});
return result.data.data;
}
}
module.exports=NewsService;
-
編寫控制層
app/controller/news.js
const {Controller}=require('egg'); class NewsController extends Controller{ async index() { const {ctx,service}=this; let {pageNum=1,pageSize=this.config.news.pageSize}=ctx.query; const list=await service.news.list(pageNum,pageSize); await ctx.render('index',{list}); } } module.exports=NewsController;
擴充工具方法
- egg最重要的5個對象
- app 代表整個應用對象
- ctx 代表上下文對象
- request 代表請求對象
- response 對象
- helper 工具方法
- 架構提供了一種快速擴充的方式,隻需在app/extend目錄下提供擴充腳本即可
- Helper 函數用來提供一些實用的 utility 函數。
- 通路方式 通過 ctx.helper 通路到 helper 對象
app\extend\helper.js
const moment=require('moment');
moment.locale('zh-cn');
exports.fromNow=dateTime => moment(new Date(dateTime)).fromNow();
news.js
list.forEach(item => {
item.createAt=ctx.helper.fromNow(item.createAt);
return item;
});
index.html
時間: {{helper.fromNow(news.createAt)}}
中間件
app/middleware/robot.js
module.exports=(options,app) => {
return async function(ctx,next) {
const source=ctx.get('user-agent')||'';
const matched=options.ua.some(ua => ua.test(source));
if (matched) {
ctx.status=403;
ctx.body='你沒有通路權限';
} else {
await next();
}
}
}
config.default.js
config.middleware=[
'robot'
]
config.robot={
ua: [
/Chrome/
]
}
運作環境
架構有兩種方式指定運作環境:
- 通過 config/env 檔案指定,該檔案的内容就是運作環境,如 prod。
- 通過 EGG_SERVER_ENV 環境變量指定。
- 架構提供了變量 app.config.env 來表示應用目前的運作環境。
-
支援按環境變量加載不同的配置檔案,如 config.local.js, config.prod.js 等等
EGG_SERVER_ENV 說明
local 本地開發環境
prod 生産環境
npm install cross-env --save-dev
“scripts”: {
“start”: “cross-env EGG_SERVER_ENV=local egg-bin dev”,
“debug”: “egg-bin debug”
}
單元測試