[TOC]javascript
在 nodejs 發展日益健壯和穩定的狀況下,咱們在平常的開發中使用 node 已是一件很是常規的事情了,那麼對於咱們必要的掌握一個服務端框架仍是很是有必要的。下面咱們就開始吧。css
在瞭解目錄結構以前,咱們須要對 mvc 的編程模式,進行一個理解。html
Model(模型)
是處理應用程序數據邏輯的部分,主要是和數據庫打交道。View(視圖)
是做爲視圖展現使用,若是沒有這部分的需求的化, view 這一個層面的內容是能夠被省略的。Controller(控制器)
是應用程序中處理與用戶交互的部分,一般是爲了 視圖須要的數據作準備,並向 Model 發送數據(get、post) 等Service(服務)
在實際應用中,Controller 通常不會本身產出數據,也不會包含複雜的邏輯,複雜的過程應抽象爲業務邏輯層 Service可是在一些 mvc 框架的論壇中,也有一部分的聲音是說但願,業務層面的架構是一個
Thin Controller Fat Model and no need Service
那麼在這樣的一個出發點上來將的話,咱們能夠在後面實際的業務中去不斷嘗試。前端
egg-project
├── package.json
├── app.js (可選)
├── agent.js (可選)
├── app
│ ├── router.js // 路由設置
│ ├── controller // 控制器
│ └── home.js
│ ├── service (可選) // 服務
│ └── user.js
│ ├── middleware (可選) // 中間件
│ └── response_time.js
│ ├── schedule (可選) // 用於定時任務
│ └── my_task.js
│ ├── public (可選) // 靜態資源文件
│ └── reset.css
│ ├── view (可選) // 模板視圖
│ └── 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
複製代碼
目錄結構java
咱們在看 egg 的內置對象的時候,咱們先看下 koa 的內置對象,application、context、request、response。
再對比下 egg 的內置對象: 包括從 Koa 繼承而來的 4 個對象
(Application, Context, Request, Response)
以及框架擴展的一些對象(Controller, Service, Helper, Config, Logger)
node
Application 是一個全局對象,上面掛載了這個框架經常使用到的 全局方法和對象。git
Context 是一個請求級別的對象,最多見的 Context 實例獲取方式是在 Middleware, Controller 以及 Service 中,例如:github
// controller
class HomeController extends Controller {
async index() {
const { ctx } = this;
ctx.body = 'hi, egg, ss';
}
}
複製代碼
Request 是一個請求級別的對象,繼承自 Koa.Request。封裝了 Node.js 原生的 HTTP Request 對象,提供了一系列輔助方法獲取 HTTP 請求經常使用參數。數據庫
// app/controller/user.js
class UserController extends Controller {
async fetch() {
const { app, ctx } = this;
const id = ctx.request.query.id;
ctx.response.body = app.cache.get(id);
}
}
// 可是若是 須要注意的是,獲取 POST 的 body 應該使用 ctx.request.body,而不是 ctx.body. 因此咱們在獲取 請求對應參數的時候均採用 ctx.request.query 或者 ctx.request.body 的方式.
複製代碼
Response 是一個請求級別的對象,繼承自 Koa.Response。封裝了 Node.js 原生的 HTTP Response 對象,提供了一系列輔助方法設置 HTTP 響應。編程
框架提供了一個 Controller 基類,這個基類包括了一些經常使用的屬性: ctx、app、config、service、logger 等
const Controller = require('egg').Controller;
class TestController extends Controller {
async index() {
const { ctx, app, config, service, logger } = this;
ctx.body = 'test';
}
}
module.exports = TestController;
複製代碼
框架提供了一個 Service 基類, Service 基類的屬性和 Controller 基類屬性一致,訪問方式也相似
const Service = require('egg').Service;
class TestService extends Service {
index() {
const { ctx } = this;
return ctx.request.query;
}
}
module.exports = TestService;
複製代碼
Helper 用來提供一些實用的 utility 函數 ( 工具函數 ) ,能夠在 Context 的實例上獲取到當前請求的 Helper(ctx.helper) 實例。 其中咱們還能夠自定義 helper 方法,以下:
// Demo
// app/extend/helper.js
module.exports = {
upperCase(str) {
return str.toUpperCase();
}
};
複製代碼
咱們能夠經過 app.config 從 Application 實例上獲取到 config 對象,也能夠在 Controller, Service, Helper 的實例上經過 this.config 獲取到 config 對象。 被建立出來的原則就是但願,配置和代碼分離的原則。
日誌系統包含了 四大層面的 日誌對象, 分別是 App Logger、App CoreLogger、Context Logger、Context CoreLogger、Controller Logger & Service Logger
這些從 app、context 、logger、service 等各個層面都提供對應的 debug 方法,這些方法中包括
後需在開發中使用到,再具體介紹每一部分的功能。
經過 config/env 文件指定對應的環境,可是通常推薦使用 構建工具來生成這個文件,那麼咱們看下這個文件裏面的內容是什麼樣子的
// config/env
prod
// 對,就是這麼簡單。
複製代碼
在 Koa 中咱們經過 app.env 來進行環境判斷,app.env 默認的值是 process.env.NODE_ENV。可是在 Egg(和基於 Egg 的框架)中,配置統一都放置在 app.config 上,因此咱們須要經過 app.config.env 來區分環境,app.env 再也不使用。
框架提供了強大且可擴展的配置功能,能夠自動合併應用、插件、框架的配置,按順序覆蓋,且能夠根據環境維護不一樣的配置。合併後的配置可直接從 app.config 獲取。
配置文件:
這裏簡單介紹下,如何編寫 中間件 和如何使用 中間件。
從文件夾規則來講,咱們編寫的本身的中間件通常都會放在 app/middleware/ xxx.js 具體的 Demo 以下:
// egg 的中間件
// 基於 洋蔥圈模型
// 和 koa 的中間件的構成差很少,只不過 egg 在外面包裹了一層
module.exports = options => {
return async function toLowerCase(ctx, next) {
await next();
const result = ctx.request.query;
ctx.body = result;
};
};
複製代碼
// koa 的中間件
function log(ctx) {
console.log(ctx.method, ctx.header.host);
}
module.exports = function() {
return async function(ctx, next) {
next();
await log(ctx);
};
};
複製代碼
在開始問如何使用中間件的時候,咱們要清除一個問題就是咱們每每在何時須要用到中間件、怎麼用的問題,下面就簡單介紹下。
// config.default.js
module.exports = {
// 配置須要的中間件,數組順序即爲中間件的加載順序
middleware: [ 'gzip' ],
// 配置 gzip 中間件的配置
gzip: {
threshold: 1024, // 小於 1k 的響應體不壓縮
},
};
複製代碼
// app.js
module.exports = app => {
// 在中間件最前面統計請求時間
app.config.coreMiddleware.unshift('report');
};
// app/middleware/report.js
module.exports = () => {
return async function (ctx, next) {
const startTime = Date.now();
await next();
// 上報請求時間
reportTime(Date.now() - startTime);
}
};
// 核心方法是 app.config.coreMiddleware.unshift('report')
複製代碼
module.exports = app => {
const gzip = app.middleware.gzip({ threshold: 1024 });
app.router.get('/needgzip', gzip, app.controller.handler);
};
複製代碼
// 修改一些初始化的配置
// config/config.default.js
module.exports = {
bodyParser: {
jsonLimit: '10mb',
},
};
複製代碼
module.exports = {
bodyParser: {
enable: fasle // 不開啓使用 bodyParser 中間件
},
gzip: {
match: '/static' // 只壓縮 /static 目錄下的文件
},
apiAgent: {
ignore: /^\/api/ // 只用 /api 的請求路徑纔不會使用 apiAgent 中間件
}
};
複製代碼
Router 主要用來描述請求 URL 和具體承擔執行動做的 Controller 的對應關係, 框架約定了 app/router.js 文件用於統一全部路由規則
// demo
module.exports = app => {
const { router, controller } = app;
router.get('/', controller.home.index);
router.get('/news', controller.news.list);
router.get('/user/:id', controller.test.user);
};
複製代碼
router 提供的訪問方法
router 有哪些使用方式? 主要列舉2中方式:
router.verb('path-match', app.controller.action);
複製代碼
router.verb('router-name', 'path-match', middleware1, ..., middlewareN, app.controller.action);
// router-name 是指路徑別名
// middleware 是指 中間件
// path-match 是指 路由 url
複製代碼
如何使用 RESTful 風格的 URL 定義
// 定義
// router.js
router.resources('posts', '/api/posts', controller.posts);
複製代碼
// controller/posts.js
exports.index = async ctx => {
ctx.body = {
success: true,
data: [1, 2, 34]
};
};
複製代碼
這裏須要仔細一點,查看關於 restful 的 使用方法,須要對 restful 有一個基本對理解。
其實整篇文章,寫到這裏已經初步對於 egg 已經有了一個大概的瞭解了,那麼後續我猜應該還會有對應的實際的寫項目的文章吧(但願有時間和經從來寫)
GitHub 地址:(歡迎 star 、歡迎推薦 : ) 《前端之路》 - 重溫 EggJS