koa是基於Node.js的一個新的web框架,它的特色是輕量、健壯、富有表現力。它是Express的下一代基於Node.js的web 框架,koa2徹底使用Promise配合async來實現異步。html
Koa將Node.js的Request請求和Response響應對象封裝到Context對象中,因此也能夠把Context對象成爲一次對話的上下文,經過加工Context對象,就能夠控制返回給用戶的內容。像Express中的Req和Res都封裝到了Context中。Context中還內置了一些經常使用的屬性:前端
Koa的應用程序其實就是一個包含一組中間件函數的對象,經過app.use函數來加載中間件,這個函數有兩個參數,context指的是上下文環境對象,封裝了一些屬性;next用於把中間件的執行權交給下游的中間件,在當前中間件中位於next()以後的代碼會暫停執行,直到最後一箇中間件執行完畢,再自下而上依次執行每一箇中間件中next值周的代碼,相似於棧的先進後出。這種模型被稱做「洋蔥圈模型」。node
洋蔥圈的最外層是最上層的中間件,由上自下執行每一箇中間件中next()函數以前的代碼,以後由下自上執行每一箇中間件中next()函數以後的代碼。Koa的大部分功能都是經過中間件實現的。 web
const koa = require('koa')
const app = new koa()
app.use(async function (ctx, next) { //中間件1,位於最上層
console.log('one start') //(1)
ctx.body = 'Hello Koa' //(2)
await next()
ctx.body = ctx.body + '!!!'//(9)
console.log('one end') //(10)
})
app.use(async function (ctx, next) { //中間件2,位於中間
console.log('two start') //(3)
ctx.type = 'text/html;charset=utf-8' //(4)
await next()
console.log('two end') //(8)
})
app.use(async function (ctx, next) { //中間件3,位於最下層
console.log('three start') //(5)
ctx.body = ctx.body + ', I am zhunny' //(6)
await next()
console.log('three end') //(7)
})
app.listen(3000, () => {
console.log('server is running at http://localhost:3000')
})
複製代碼
瀏覽器中顯示以下:編程
某個中間件出錯,能夠在它上一級的錯誤處理中間件中捕獲,再一層層向上捕獲,從全局app應用層最後到Node層。Koa的錯誤處理是向上拋的。後端
const koa = require('koa')
const app = new koa()
//響應輸出中間件
app.use(async function (ctx, next) {
await next()
//獲取響應頭,印證執行順序
const rt = ctx.response.get('X-Response-Time')
console.log(`輸出計時:${ctx.method} ${ctx.url} - ${rt}`)
})
app.use(async function (ctx, next) {
const start = Date.now()
console.log('開始計時')
await next()
const ms = Date.now() - start
ctx.set('X-Response-Time', `${ms}ms`)
console.log('計時結束')
})
//中間件錯誤捕獲
app.use(async (ctx, next) => {
try {
await next()
} catch (error) {
ctx.status = error.statusCode || error.status || 500
ctx.body = error.message
//觸發應用層級的錯誤事件
ctx.app.emit('error', error, ctx)
//中間件出錯,能夠上拋到中間件錯誤捕獲->全局->Node
console.log('中間件捕捉', error.message)
}
})
app.use(async function (ctx, next) {
console.log('響應用戶請求')
//這裏設置一個未定義的函數
sleep(300)
ctx.status = 200
ctx.type = 'html'
ctx.body = '<h1>Hello Koa</h1>'
})
//全局的錯誤捕獲
app.on('error', err => {
console.error('app全局錯誤:', err.message)
//繼續上拋到Node,此時會停止服務
throw err
})
app.listen(3000)
複製代碼
node控制檯打印結果以下:瀏覽器
路由具備引導、匹配之意。路由匹配是根據URL的變動從新渲染頁面佈局和內容的過程。tomcat
早期,先後端沒有分離時,由後端來實現路由。用戶將每一個頁面的靜態資源所有都放到後端服務器上,當用戶進行頁面切換時,由瀏覽器向服務器發送不一樣的URL請求,經服務器解析後向瀏覽器返回對應頁面的靜態資源和數據,再由瀏覽器渲染成新的頁面。後端路由的弊端是每次切換頁面都會刷新頁面,給用戶形成一種卡頓的感受,用戶體驗不好。先後端不分離,路由是後端開發人員來作的,整個業務偏重後端,後端開發任務繁重,且先後端不解耦,使得服務器壓力大,開發效率也低。前端框架
先後端分離時代的到來,使得先後端能夠並行開發,開發效率,項目性能大大提高。前端服務器負責控制頁面引用&跳轉&路由,前端頁面異步調用後端的接口,後端/應用服務器使用tomcat(把tomcat想象成一個數據提供者),加快總體響應速度。前端路由再也不是每次都刷新頁面,而是在須要的時候才加載相應頁面的內容。不一樣路由切換對應的僅僅是一個文檔樹的切換,頁面所需的數據纔會向後端服務器發起請求。前端路由的主要場景是SPA單頁面應用,Vue、React等流行的前端框架都提供了路由切換。服務器
若是不採用路由組件應該如何根據不一樣的url和方法作不一樣的響應?以下自定義一個404頁面:
const koa = require('koa')
const app = new koa()
app.use(async (ctx, next) => {
if (ctx.url === '/' && ctx.method === 'GET') {
ctx.body = 'Page Not Found'
ctx.status = 404
} else {
ctx.body = 'Defalut Page'
ctx.status = 200
}
await next()
})
複製代碼
上述這種方式將路由處理和輸出響應都放在了一箇中間件函數中,而實際的項目中會存在不少的路由,若是按照這樣的方式處理,會嚴重影響到代碼的可讀性和可維護性。咱們一般使用路由組件來定義路由。
koa-router具備豐富的API,能夠實現命名路由、命名參數、多路由中間件、嵌套路由等多種功能。上述的代碼能夠用koa-router來改寫:
const koa = require('koa')
const app = new koa()
const Router = require('koa-router')
const router = new Router()
router.get('/', (ctx, next) => {
ctx.body = 'Page Not Found!!!'
ctx.status = 404
})
app.use(router.routes())
app.listen(3000)
複製代碼
在實際項目中,根據不一樣的職能將路由分爲不一樣的模塊,在入口文件中統一調用這些路由模塊。例如如今有一個users模塊,負責用戶信息的增刪改查,一個index模塊,負責默認頁面路由。
const Router = require('koa-router')
const router = new Router()
router.get('/', ctx => {
ctx.body = 'index'
})
module.exports = router
複製代碼
users.js的內容以下:
const Router = require('koa-router')
const router = new Router({ prefix: '/users' })
router.get('/', ctx => {
ctx.body = 'user'
})
module.exports = router
複製代碼
入口文件中使用這些路由中間件:
const index = require('./routes/index')
const users = require('./routes/users')
app.use(index.routes())
app.use(users.routes())
複製代碼
瀏覽器中顯示以下: