《前端之路》--- 重溫 Egg.js
[TOC]javascript
在 nodejs 發展日益健壯和穩定的狀況下,咱們在平常的開發中使用 node 已是一件很是常規的事情了,那麼對於咱們必要的掌握一個服務端框架仍是很是有必要的。下面咱們就開始吧。css
1、基礎功能
1.一、目錄結構
在瞭解目錄結構以前,咱們須要對 mvc 的編程模式,進行一個理解。html
- M =》
Model(模型)
是處理應用程序數據邏輯的部分,主要是和數據庫打交道。 - V =》
View(視圖)
是做爲視圖展現使用,若是沒有這部分的需求的化, view 這一個層面的內容是能夠被省略的。 - C =》
Controller(控制器)
是應用程序中處理與用戶交互的部分,一般是爲了 視圖須要的數據作準備,並向 Model 發送數據(get、post) 等 - S =》
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
- app/router.js 用於配置 URL 路由規則
- app/controller/** 用於解析用戶的輸入,處理後返回相應的結果
- app/service/** 用於編寫業務邏輯層
- app/middleware/** 用於編寫中間件
- app/public/** 用於放置靜態資源
- app/extend/** 用於框架的擴展
- config/config.{env}.js 用於編寫配置文件
- config/plugin.js 用於配置須要加載的插件
- test/** 用於單元測試
- app.js 和 agent.js 用於自定義啓動時的初始化工做
1.二、內置對象
咱們在看 egg 的內置對象的時候,咱們先看下 koa 的內置對象,application、context、request、response。
再對比下 egg 的內置對象: 包括從 Koa 繼承而來的 4 個對象
(Application, Context, Request, Response)
以及框架擴展的一些對象(Controller, Service, Helper, Config, Logger)
node
1.2.一、Application
Application 是一個全局對象,上面掛載了這個框架經常使用到的 全局方法和對象。git
1.2.二、Context
Context 是一個請求級別的對象,最多見的 Context 實例獲取方式是在 Middleware, Controller 以及 Service 中,例如:github
// controller class HomeController extends Controller { async index() { const { ctx } = this; ctx.body = 'hi, egg, ss'; } }
1.2.三、Request
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 的方式.
1.2.四、Response
Response 是一個請求級別的對象,繼承自 Koa.Response。封裝了 Node.js 原生的 HTTP Response 對象,提供了一系列輔助方法設置 HTTP 響應。編程
1.2.五、Controller
框架提供了一個 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;
1.2.六、Service
框架提供了一個 Service 基類, Service 基類的屬性和 Controller 基類屬性一致,訪問方式也相似
const Service = require('egg').Service; class TestService extends Service { index() { const { ctx } = this; return ctx.request.query; } } module.exports = TestService;
1.2.七、Helper
Helper 用來提供一些實用的 utility 函數 ( 工具函數 ) ,能夠在 Context 的實例上獲取到當前請求的 Helper(ctx.helper) 實例。 其中咱們還能夠自定義 helper 方法,以下:
// Demo // app/extend/helper.js module.exports = { upperCase(str) { return str.toUpperCase(); } };
1.2.八、Config
咱們能夠經過 app.config 從 Application 實例上獲取到 config 對象,也能夠在 Controller, Service, Helper 的實例上經過 this.config 獲取到 config 對象。 被建立出來的原則就是但願,配置和代碼分離的原則。
1.2.九、Logger
日誌系統包含了 四大層面的 日誌對象, 分別是 App Logger、App CoreLogger、Context Logger、Context CoreLogger、Controller Logger & Service Logger
這些從 app、context 、logger、service 等各個層面都提供對應的 debug 方法,這些方法中包括
- logger.debug()
- logger.info()
- logger.warn()
- logger.error()
後需在開發中使用到,再具體介紹每一部分的功能。
1.三、運行環境
經過 config/env 文件指定對應的環境,可是通常推薦使用 構建工具來生成這個文件,那麼咱們看下這個文件裏面的內容是什麼樣子的
// config/env prod // 對,就是這麼簡單。
在 Koa 中咱們經過 app.env 來進行環境判斷,app.env 默認的值是 process.env.NODE_ENV。可是在 Egg(和基於 Egg 的框架)中,配置統一都放置在 app.config 上,因此咱們須要經過 app.config.env 來區分環境,app.env 再也不使用。
1.四、配置
框架提供了強大且可擴展的配置功能,能夠自動合併應用、插件、框架的配置,按順序覆蓋,且能夠根據環境維護不一樣的配置。合併後的配置可直接從 app.config 獲取。
配置文件:
- config.default.js ---默認配置文件,通常也做爲開發環境使用
- config.prod.js --- 生產環境配置文件,會覆蓋默認配置文件的同名配置
- config.unittest.js --- 單元測試, 測試環境
- config.local.js --- 本地開發環境,額外於 默認配置。
1.五、中間件
這裏簡單介紹下,如何編寫 中間件 和如何使用 中間件。
1.5.一、如何編寫中間件
從文件夾規則來講,咱們編寫的本身的中間件通常都會放在 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); }; };
1.5.二、如何使用中間件
在開始問如何使用中間件的時候,咱們要清除一個問題就是咱們每每在何時須要用到中間件、怎麼用的問題,下面就簡單介紹下。
- 在應用中使用中間件
// 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')
- router 中使用中間件
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', }, };
1.5.三、中間件的通用配置
- enable 是否開啓使用中間件
- match 設置只有符合某些規則的請求才會通過這個中間件
- ignore 設置符合某些規則的請求不通過這個中間件
module.exports = { bodyParser: { enable: fasle // 不開啓使用 bodyParser 中間件 }, gzip: { match: '/static' // 只壓縮 /static 目錄下的文件 }, apiAgent: { ignore: /^\/api/ // 只用 /api 的請求路徑纔不會使用 apiAgent 中間件 } };
1.六、路由
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.head - HEAD
- router.options - OPTIONS
- router.get - GET
- router.put - PUT
- router.post - POST
- router.patch - PATCH
- router.delete - DELETE
- router.redirect --- 這裏重點提示下 redirect 重定向,能夠對 URL 進行重定向處理,好比咱們最常用的能夠把用戶訪問的根目錄路由到某個主頁。
router 有哪些使用方式? 主要列舉2中方式:
- 最簡單的一種,直接 router.method('/url', controller)
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 有一個基本對理解。
2、總結
其實整篇文章,寫到這裏已經初步對於 egg 已經有了一個大概的瞭解了,那麼後續我猜應該還會有對應的實際的寫項目的文章吧(但願有時間和經從來寫)
GitHub 地址:(歡迎 star 、歡迎推薦 : ) 《前端之路》 - 重溫 EggJS
原文出處:https://www.cnblogs.com/erbingbing/p/12416887.html