Express Koa 簡述

前言

  • Node.js 是一個(gè)基于 Chrome V8 引擎的 JavaScript 運(yùn)行環(huán)境。
    這也就讓我們可以脫離瀏覽器運(yùn)行JavaScript代碼。

  • 在Node.js漸漸成熟走向商業(yè)化應(yīng)用的過程中,必須要提起Express,Koa框架,它們均來自同一作者TJ--node圈中的人物,誕生于Nodejs發(fā)展的不同時(shí)期,在Ecmascript語言規(guī)范的發(fā)展中前行。

在Express誕生前,沒有成熟輪子,需要通過Nodejs基礎(chǔ)模塊構(gòu)建應(yīng)用

const http = require("http");
http.createServer(function(request, response){
  response.writeHead(200, {"Content-Type": "text/plain"});
  response.write("Hello world");
  response.end();
}).listen(8081);

Express

基于 Node.js 平臺(tái),快速、開放、極簡的 Web 開發(fā)框架,這來自Express官方描述。

const express = require('express')
const app = express()

app.get('/', (req, res) => res.send('Hello World!'))

app.listen(3001, () => {
    console.info(`Server has started, 地址為: http://0.0.0.0:3001`)
})

Express是一個(gè)自身功能極簡,完全是路由和中間件構(gòu)成一個(gè)web開發(fā)框架:從本質(zhì)上來說,一個(gè)Express應(yīng)用就是在調(diào)用各種中間件。

可以看出中間件在Express開發(fā)中的重要性,這里主要就介紹一下中間件。

image.png

1、內(nèi)置中間件

express.static 是Express目前唯一內(nèi)置的一個(gè)中間件。用來處理靜態(tài)資源文件。
例如,通過如下代碼就可以將 public 目錄下的圖片、CSS 文件、JavaScript 文件對外開放訪問了:
app.use(express.static('public'))
現(xiàn)在,你就可以訪問 public 目錄中的所有文件了:

http://localhost:3001/images/kitten.jpg
http://localhost:3001/css/style.css
http://localhost:3001/js/app.js
http://localhost:3001/images/bg.png
http://localhost:3001/hello.html

2、自定義中間件
這是一個(gè)名為“myLogger”的中間件函數(shù)的簡單示例。當(dāng)對應(yīng)用程序的請求通過時(shí),此函數(shù)只打印“LOGGED”。中間件函數(shù)被分配給名為的變量myLogger。

var express = require('express')
var app = express()

var myLogger = function (req, res, next) {
  console.log('LOGGED')
  next()
}

app.use(myLogger)

app.get('/', function (req, res) {
  res.send('Hello World!')
})

app.listen(3000)

每次應(yīng)用程序收到請求時(shí),它都會(huì)向終端輸出消息“LOGGED”。

中間件加載的順序很重要:首先加載的中間件函數(shù)也會(huì)先執(zhí)行。

如果myLogger在到根路徑的路由之后加載,則請求永遠(yuǎn)不會(huì)到達(dá)它并且應(yīng)用程序不會(huì)打印“LOGGED”,因?yàn)楦窂降穆酚商幚沓绦蚪K止請求 - 響應(yīng)循環(huán)。

中間件函數(shù)myLogger只是打印一條消息,然后通過調(diào)用該next()函數(shù)將請求傳遞給堆棧中的下一個(gè)中間件函數(shù)。

Koa

Koa 是由 Express 原班人馬打造的,致力于成為一個(gè)更小、更富有表現(xiàn)力、更健壯的 Web 框架。使用 Koa 編寫 web 應(yīng)用,通過組合不同的 generator,可以免除重復(fù)繁瑣的回調(diào)函數(shù)嵌套,并極大地提升錯(cuò)誤處理的效率。Koa 不在內(nèi)核方法中綁定任何中間件,它僅僅提供了一個(gè)輕量優(yōu)雅的函數(shù)庫,使得編寫 Web 應(yīng)用變得得心應(yīng)手。

// 導(dǎo)入koa,和koa 1.x不同,在koa2中,我們導(dǎo)入的是一個(gè)class,因此用大寫的Koa表示:
const Koa = require('koa');

// 創(chuàng)建一個(gè)Koa對象表示web app本身:
const app = new Koa();

// 對于任何請求,app將調(diào)用該異步函數(shù)處理請求:
// 參數(shù)ctx是由koa傳入的封裝了request和response的變量,我們可以通過它訪問request和response,next是koa傳入的將要處理的下一個(gè)異步函數(shù)。
// 這里首先用await next();處理下一個(gè)異步函數(shù),然后,設(shè)置的返回內(nèi)容。
app.use(async (ctx, next) => {
    await next();
    ctx.body = 'Hello World';
});

// 在端口3001監(jiān)聽:
app.listen(3001);

console.info(`Server has started, 地址為: http://0.0.0.0:3001`)

Koa middleware

首先我們看一下執(zhí)行的核心代碼

app.use(async (ctx, next) => {
    await next();
    ctx.body = 'Hello World';
});

每收到一個(gè)http請求,Koa就會(huì)調(diào)用通過app.use()注冊的async函數(shù),并傳入ctx和next參數(shù)。

我們可以對ctx操作,并設(shè)置返回內(nèi)容。但是為什么要調(diào)用await next()?

因?yàn)镵oa 中間件以更傳統(tǒng)的方式級聯(lián),使用 async 功能,我們可以實(shí)現(xiàn) “真實(shí)” 的中間件。通過一系列功能直接傳遞控制,直到一個(gè)返回,Koa 調(diào)用“下游”,然后控制流回“上游”。

下面以 “Hello World” 的響應(yīng)作為示例,當(dāng)請求開始時(shí)首先請求流通過 x-response-time 和 logging 中間件,然后繼續(xù)移交控制給 response 中間件。當(dāng)一個(gè)中間件調(diào)用 next() 則該函數(shù)暫停并將控制傳遞給定義的下一個(gè)中間件。當(dāng)在下游沒有更多的中間件執(zhí)行后,堆棧將展開并且每個(gè)中間件恢復(fù)執(zhí)行其上游行為。

const Koa = require('koa');
const app = new Koa();

// logger

app.use(async (ctx, next) => {
    await next(); // 調(diào)用下一個(gè)middleware
    const rt = ctx.response.get('X-Response-Time');
    console.log(`${ctx.method} ${ctx.url} - ${rt}`); // 打印日志
});

// x-response-time

app.use(async (ctx, next) => {
    const start = Date.now(); // 當(dāng)前時(shí)間
    await next(); // 調(diào)用下一個(gè)middleware
    const ms = Date.now() - start; // 耗時(shí)時(shí)間
    ctx.set('X-Response-Time', `${ms}ms`); // 打印耗時(shí)時(shí)間
});

// response

app.use(async ctx => {
    ctx.body = 'Hello World';
});

app.listen(3001);

middleware的順序很重要,也就是調(diào)用app.use()的順序決定了middleware的順序。

此外,如果一個(gè)middleware沒有調(diào)用await next(),會(huì)怎么辦?答案是后續(xù)的middleware將不再執(zhí)行了。這種情況也很常見,例如,一個(gè)檢測用戶權(quán)限的middleware可以決定是否繼續(xù)處理請求,還是直接返回403錯(cuò)誤:

app.use(async (ctx, next) => {
    if (await checkUserPermission(ctx)) {
        await next();
    } else {
        ctx.response.status = 403;
    }
});

總結(jié)

  • koa是一個(gè)比express更精簡,使用node新特性的中間件框架,相比之前express就是一個(gè)龐大的框架。

Express基于ES5語法,通過回調(diào)組合邏輯。在復(fù)雜邏輯中會(huì)包含大量回調(diào)嵌套,難以捕捉問題,不便調(diào)試。當(dāng)下Es6,Es7盛行,可以通過相關(guān)三方庫完善支持Promise或Async/Await來彌補(bǔ)。

如果你喜歡diy,可以考慮koa,它有足夠的擴(kuò)展和中間件,而且自己寫很簡單
如果你想簡單點(diǎn),找一個(gè)框架啥都有,那么先使用express

  • 中間件理解

瀏覽器向服務(wù)器發(fā)送一個(gè)請求后,服務(wù)器直接通過request.定位屬性的方式得到通過request攜帶過去的數(shù)據(jù)(有用戶輸入的數(shù)據(jù)和瀏覽器本身的數(shù)據(jù)信息)。這中間就一定有一個(gè)函數(shù)將這些數(shù)據(jù)分類做了處理,已經(jīng)處理好了,最后讓request對象調(diào)用使用。對的,這個(gè)處理數(shù)據(jù)處理函數(shù)就是我們要說的 中間件 。

  • 中間件可以總結(jié)為以下兩點(diǎn):

1、封裝了一些處理一個(gè)完整事件的功能函數(shù)。

2、封裝了一些或許復(fù)雜但肯定是通用的功能。

參考:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容