天天看點

Koa - Node.js架構學習@郝晨光

本文由郝晨光整理總結并編寫,未經允許禁止轉載。

前言

學習koa,我之前學習過express,但是在使用express的時候,還是一直使用的回調函數的方式來處理異步,現在想想真是恐怖,後來了解到koa這個架構,它相對于express來說,小巧了很多,對于異步的處理也變得更加優雅了。用官方的話來說,koa是基于Node.js平台的下一代web平台開發架構。

Koa - Node.js架構學習@郝晨光

正文

  1. 首先肯定要先開始一個新的項目,建立一個檔案夾
  2. 在檔案夾目錄中,使用指令提示符執行

    npm init -y

    ,初始化

    package.json

    檔案;
  3. 安裝
    • Koa 依賴 node - v7.6.0 或 ES2015及更高版本和 async 方法支援。
    • 先檢視node的版本

      node -v

      ;
    • 然後執行

      npm install koa

      進行安裝;
  4. 使用
    • 建立index.js;
    • 先上官網上最經典的hello wrold案例;
    • const Koa = require('koa');
      const app = new Koa();
      app.use(async ctx => {
          ctx.body = 'hello word!'
      })
      app.listen(8000, () => {
          console.log('Koa server listen in http://localhost:8000/');
      })
                 
    • 打開浏覽器,輸入

      localhost:8000/

      ,就可以看到我們的頁面上顯示了hello world!;
    • 那我們的第一個koa應用就已經運作起來了~。
  5. 讀取 HTML 頁面
    • 在Koa中,我們如果要使用 HTML 應該怎麼做呢?
    • 在原生nodejs或者express中,我們使用的是fs子產品,然後使用回調函數,一層套一層;
    • 在Koa中,我們依舊使用fs子產品,但是,要對fs子產品的讀取檔案,進行進一步的封裝;
    • 在目錄下,我們建立一個

      index.html

      ,并随便寫一點東西;
    • 接着開始我們漸行漸遠的程式生涯;
    • const Koa = require('koa');
      const fs = require('fs');
      const app = new Koa();
      function readFile(url) {
        return new Promise((resolve, reject) => {
        	fs.readFile(url, (err,file) => {
        		if(err) {
        			reject(err);
        		}else {
        			resolve(file);
        		}
        	})
        })
      }
      app.use(async ctx => {
          ctx.response.type = 'html'; // 重點
          ctx.body = await readFile('index.html');
      })
      app.listen(8000, () => {
          console.log('Koa server listen in http://localhost:8000/');
      })
      // 本文由郝晨光整理總結并編寫,未經允許禁止轉載。
                 
    • 可以看到,我們利用Promise将fs子產品的readFile方法進行了二次封裝,讀取了本地的index.html檔案
    • 重點的地方我已經标出來了,為什麼要标重點呢?
    • 因為在Koa中,ctx.body預設傳回的類型是

      text/plain

      ;而我們要傳回html檔案,是以應該在傳回檔案之前,設定好傳回類型,讓用戶端可以正确的接收;
    • 然後我們使用

      async + await

      組合,在資料讀取完成之後,傳回讀取的檔案;
    • 最後,打開浏覽器,可以看到浏覽器上輸出的就是我們

      index.html

      檔案中寫的内容
  6. 讀取靜态資源
    • html檔案我們已經可以正确的讀取并傳回了,在我們的頁面上,避免不了會使用很多的靜态資源,例如css、js、image、font等等;這些靜态資源我們應該怎麼處理呢?
    • 在這裡,我們使用一個Koa的中間件

      koa-static

    • 在指令提示符中執行

      npm install koa-static

    • 接着書寫我們的程式
    • // ~~ 省略 ~~
      const app = new Koa(); // 建立koa對象
      const KoaStatic = require('koa-static'); // koa靜态檔案讀取
      app.use(KoaStatic(__dirname)); // 使用中間件
      // ~~ 省略 ~~
                 
      • 接着,打開我們的浏覽器,可以看到靜态資源已經可以正确的請求到了;
      • 當然了,這個時候,我們可以把之前傳回html檔案時設定響應類型的一步省略掉了,因為

        koa-static

        已經幫我們做了這件事了。
  7. 定義路由
    • 所謂的路由,就是根據不同的請求路徑,響應不同的内容;
    • 在Koa中,我們可以定義原生路由,也可以使用封裝好的路由中間件;
    • 先看一下原生路由的寫法吧!
    • app.use(async ctx => {
      	if(ctx.request.method==='GET') { // GET請求
        	switch (ctx.request.path) {
        		case '/': // 比對預設路由
        			ctx.body = await readFile('index.html'); // 傳回index.html檔案
        			break;
                case '/about': // 比對/about路由
            		ctx.body = 'about路由頁面'; // 傳回about路由頁面
        			break;
        		default:
        			ctx.body = await readFile('index.html'); // 預設傳回index.html檔案
        			break;
        	}
        }else if(ctx.request.method==='POST') { // POST請求
        	switch (ctx.request.path) {
        		default:
        			ctx.body = 'post 請求'; // 響應post請求
        			break;
        	}
        }
      });
      //本文由郝晨光整理總結并編寫,未經允許禁止轉載。
                 
    • 可以看到我上邊寫的,對請求方式以及路由進行了處理,在不同的請求方式,不同的請求路徑下,執行不同的操作,響應不同的結果。
      Koa - Node.js架構學習@郝晨光
  8. 路由中間件
    • 我們使用原生方式定義路由,未免有些太過繁瑣,并且代碼不便于閱覽和維護;是以,建議使用中間件的方式進行路由配置。
    • 我這裡使用的是 koa-router 這個中間件,當然,koa的路由中間件不是隻有這一種。
    • 在指令提示符中安裝

      npm install koa-router

      ;
    • 接着,進行我們的程式生涯;
    • // ~~ 省略 ~~
      const KoaRouter = require('koa-router');
      const router = new KoaRouter();
      router.get('/',async ctx => {
          ctx.body = await readFile('index.html');
      });
      
      router.get('/about',async ctx=> {
           ctx.body = 'about路由頁面'
      });
      
      router.post('/form',async ctx => {
          ctx.body = 'post請求'
      });
      
      app.use(router.routes());
      // ~~ 省略 ~~
                 
    • 打開頁面,并進行路由切換,可以看到沒有任何問題;
    • 但是這樣所有的路由配置都寫在了index.js中,不利于維護,是以我們要将路由内容和公用方法提取出來。
      Koa - Node.js架構學習@郝晨光
  9. 代碼子產品化
    • 首先對路由進行子產品化
    • 在目錄下建立routes檔案夾,并建立home.js,用來存放關于首頁的一些路由配置。
    • 将上邊的路由代碼單獨寫到home.js中,并抛出,如下:
    • home.js
    • const KoaRouter = require('koa-router');
      const router = new KoaRouter();
      
      router.get('/',async ctx => {
        ctx.body = await readFile('index.html');
      });
      
      router.get('/about',async ctx=> {
      	ctx.body = 'about 路由'
      });
      
      router.post('/from',async ctx => {
         ctx.body = 'post請求'
      });
      
      module.exports = router;
                 
    • index.js
    • const homeRouter = require('./routes/home');
      
      app.use(KoaStatic(__dirname));
      
      app.use(homeRouter.routes());
                 
    • 在index.js中,直接引入使用即可,和原有代碼沒有任何差別。
    • 需要注意的是,對于功能中間件,例如

      koa-static

      這種,我們應該放在路由中間件之前。
    • 否則的話可能會出錯,在路由中不能正确的讀取靜态檔案等。
  10. 二級路由
    • 已經學會了一級路由的定義,我們趁熱打鐵,學習二級路由
    • 在routes目錄下,建立user.js,定義使用者路由
    • // user.js
      const KoaRouter = require('koa-router');
      const router = new KoaRouter({
        prefix: '/user'
      });
      
      router.get('/',async ctx => {
        ctx.body = '使用者界面'
      });
      
      router.get('/:id',async ctx => {
      	ctx.body = '使用者詳情'+ctx.params.id;
      });
      
      router.post('/login',async ctx => {
      	ctx.body = '使用者注冊成功!'
      });
      
      module.exports = router;
      
      // index.js
      // ~~ 省略 ~~
      const userRouter = require('./routes/user');
      // ~~ 省略 ~~
      app.use(userRouter.routes());
                 
    • 接着,打開浏覽器,我們測試一下我們的二級路由,是沒有任何問題的。
      Koa - Node.js架構學習@郝晨光
  11. post請求處理
    • 我們都知道,使用post請求一般用來送出表單,或者修改資料等。而post請求中的資料,存放在請求體中,使用原生Node的方法的話,我們需要監聽原生req對象上的data方法以及end方法,并将資料格式轉化。而express中,我們有一個

      body-parser

      的插件可以使用。
    • 那麼在Koa中,我們應該使用什麼呢?
    • 在Koa中,我們使用

      koa-bodyparser

      這個中間件來處理post請求的資料
    • 在指令提示符安裝

      npm install koa-bodyparser

      ;
    • 接着我們在index.js中使用這個中間件;
    • 加入下面兩行代碼,與原先使用

      koa-static

      中間件位置一樣即可;
    • const KoaBodyParser = require('koa-bodyparser');
      
      app.use(KoaBodyParser());
                 
    • 使用了這個中間件,我們就可以在

      ctx.request.body

      中拿到post請求的資料了;
    • router.post('/login',async ctx => {
          ctx.body = ctx.request.body;
       });
                 
    • 接着改造一下我們的html檔案,送出一下表單;
    • <form action="/user/login" method="post">
        	姓名:<input type="text" name="name" autocomplete="off"/>
        	<br>
        	<input type="radio" name="sex" value="男"/>男
        	<input type="radio" name="sex" value="女"/>女
        	<input type="submit" value="送出form">
       	</form>
                 
      • 可以看到,表單送出正常,我們也正确的拿到了表單送出的資料。
        Koa - Node.js架構學習@郝晨光
  12. CORS跨域配置
    • 在我們現在的伺服器生涯中,避免不了要使用跨域,特别是CORS跨域。
    • 那麼,在Koa中,我們應該怎麼去設定跨域呢?
    • 我們可以使用中間件,也可以使用原生方法手寫。
    • 老規矩,先看原生方法。
    • app.use(async (ctx, next) => {
        	// 允許來自所有域名請求
        	ctx.set("Access-Control-Allow-Origin", "*");
        	// 這樣就能隻允許 http://localhost:8080 這個域名的請求了
        	// ctx.set("Access-Control-Allow-Origin", "http://localhost:8080");
      
        	// 設定所允許的HTTP請求方法
        	ctx.set("Access-Control-Allow-Methods", "OPTIONS, GET, PUT, POST, DELETE");
      
        	// 字段是必需的。它也是一個逗号分隔的字元串,表明伺服器支援的所有頭資訊字段.
        	ctx.set("Access-Control-Allow-Headers", "x-requested-with, accept, origin, content-type");
      
        	// 伺服器收到請求以後,檢查了Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段以後,确認允許跨源請求,就可以做出回應。
      
        	// Content-Type表示具體請求中的媒體類型資訊
        	ctx.set("Content-Type", "application/json;charset=utf-8");
      
        	// 該字段可選。它的值是一個布爾值,表示是否允許發送Cookie。預設情況下,Cookie不包括在CORS請求之中。
        	// 當設定成允許請求攜帶cookie時,需要保證"Access-Control-Allow-Origin"是伺服器有的域名,而不能是"*";
        	ctx.set("Access-Control-Allow-Credentials", true);
      
        	// 該字段可選,用來指定本次預檢請求的有效期,機關為秒。
        	// 當請求方法是PUT或DELETE等特殊方法或者Content-Type字段的類型是application/json時,伺服器會提前發送一次請求進行驗證
        	// 下面的的設定隻本次驗證的有效時間,即在該時間段内服務端可以不用進行驗證
        	ctx.set("Access-Control-Max-Age", 300);
      
        	/*
        	CORS請求時,XMLHttpRequest對象的getResponseHeader()方法隻能拿到6個基本字段:
            Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。
            */
        	// 需要擷取其他字段時,使用Access-Control-Expose-Headers,
        	// getResponseHeader('myData')可以傳回我們所需的值
        	ctx.set("Access-Control-Expose-Headers", "myData");
        	await next();
        })
                 
      • 原生方法來自:node.js 應答跨域請求實作(以koa2-cors為例)
      • 接着我們來看中間件吧!
      • 在Koa2中,我們使用

        koa2-cors

        這個中間件來設定CORS跨域請求。
      • 先安裝

        npm install koa2-cors

        ;
      • 接着,在index.js中使用。
      • const KoaCors = require('koa2-cors');
        // ~~ 省略 ~~
        // CORS跨域
        app.use(KoaCors({
        	origin: ctx => {
        		return ctx.request.header.origin;
        	},
        	exposeHeaders: ['WWW-Authenticate', 'Server-Authorization'],
        	maxAge: 5,
        	credentials: true,
          	allowMethods: ['GET', 'POST', 'DELETE', 'OPTIONS'],
        	allowHeaders: ['Content-Type', 'Authorization', 'Accept', 'Origin'],
        }));
        //本文由郝晨光整理總結并編寫,未經允許禁止轉載。
                   
      • 特别需要注意的是,在

        app.use()

        使用

        koa2-cors

        的時候,我們應該把它放在别的中間件的前邊,特别是靜态資源處理和路由進行中間件的前邊,這樣可以保證我們在跨域請求靜态資源的時候不會出問題。

如果本文對您有幫助,可以看看本人的其他文章:

前端常見面試題(十三)@郝晨光

前端常見面試題(十二)@郝晨光

前端常見面試題(十一)@郝晨光

結言

感謝您的查閱,本文由郝晨光整理并總結,代碼備援或者有錯誤的地方望不吝賜教;菜鳥一枚,請多關照

繼續閱讀