Koa(1)之——koa入門

  koa是基於Node.js的一個新的web框架,它的特色是輕量、健壯、富有表現力。它是Express的下一代基於Node.js的web 框架,koa2徹底使用Promise配合async來實現異步。html

特色

  1. 輕量、無捆綁(koa再也不內核方法中綁定任何中間件,它僅僅提供一個輕量的函數庫,幾乎全部的功能都須要引用第三方中間件來實現)
  2. 中間件架構(函數式編程的一種實踐,開發者能夠根據業務和項目須要使用已有的第三方中間件或者定製中間件)
  3. 優雅的API設計
  4. 加強的錯誤處理(async語法對於錯誤處理的加強)

context對象

  Koa將Node.js的Request請求和Response響應對象封裝到Context對象中,因此也能夠把Context對象成爲一次對話的上下文,經過加工Context對象,就能夠控制返回給用戶的內容。像Express中的Req和Res都封裝到了Context中。Context中還內置了一些經常使用的屬性:前端

  1. req Node的request
  2. res Node的response
  3. request Koa的request
  4. response Koa的response
  5. state 推薦的命名空間,用於中間件傳遞消息和前端視圖
  6. app 應用程序引用

Koa的中間件機制

  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')
})

複製代碼

瀏覽器中顯示以下:編程


node控制檯打印結果以下:

錯誤處理

  某個中間件出錯,能夠在它上一級的錯誤處理中間件中捕獲,再一層層向上捕獲,從全局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等流行的前端框架都提供了路由切換。服務器

koa路由

自定義路由

  若是不採用路由組件應該如何根據不一樣的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

  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模塊,負責默認頁面路由。


index.js的內容以下

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())
複製代碼

瀏覽器中顯示以下:

相關文章
相關標籤/搜索