koa框架會用也會寫—(koa-router)

Koa中經常使用的中間件:

  • koa-session:讓無狀態的http擁有狀態,基於cookie實現的後臺保存信息的session
  • koa-mysql:封裝了須要用到的SQL語句
  • koa-mysql-session:當不想讓session存儲到內存,而想讓session存儲到mysql數據庫中時使用
  • koa-router:後臺會接受到各類請求的url,路由會根據不一樣的url來使用不一樣的處理邏輯。
  • koa-view:請求html頁面時,後臺會用模板引擎渲染數據到模板上,而後返回給後臺
  • koa-static:請求img、js、css等文件時,不須要其餘邏輯,只須要讀取文件
  • koa-better-body:post上傳文件時,解析請求體

koa系列文章:

koa-router的使用

var Koa = require('koa');
var Router = require('koa-router');
var app = new Koa();
var router = new Router();
router.get('/home',(ctx,next)=>{ 
  ctx.body = 'home'
  next();
});
router.get('/user', (ctx, next) => {
  ctx.body = 'user';
  next();
});
app.use(router.routes()).use(router.allowedMethods());
複製代碼

koa-router的奧祕

假如沒有koa-router

var Koa = require('koa');
var Router = require('koa-router');
var app = new Koa();
var router = new Router();
//將路由的處理交給中間件
app.use((ctx, next) => {
    if (ctx.path === '/' && ctx.method === 'GET') {
        ctx.body = '首頁'
   } else {
        next();
   }
})
app.use((ctx, next) => {
   if (ctx.path === '/user' && ctx.method === 'GET') {
        ctx.body = '用戶'
   } else {
        next();
  }
});
複製代碼

從上面能夠知道,若是沒有koa-router,其實每一個路由使用的koa註冊中間件的形式來進行處理的,這樣不利於鬆耦合和模塊化,因此將全部路由的處理邏輯抽離出來組合成一個大的中間件koa-router來處理,最後將大的中間件註冊到koa上,若是關於koa中間件原理還不瞭解,能夠參考另外一篇文章koa框架會用也會寫—(koa的實現)css

koa-router的原理

既然koa-router也是大的中間件,裏面擁有許多小的中間件,那麼裏面必然也須要用到洋蔥模型,洋蔥模型的特色:html

  • middles:存放中間件的容器,用來存放註冊的中間件
  • get(path,fn):用來註冊中間件,往middles中存放,因爲是路由中間件這裏多了一個參數path
  • compose():用來組合中間件,讓路由中間件按順序執行
  • routes():用來將koa-router中間件註冊到app的中間件上,主要做用是調用路由中間件匹配請求的路徑ctx.path

若是對於中間件和洋蔥模型有疑問的,能夠參考koa框架會用也會寫—(koa的實現)mysql

middles:存放中間件的容器,用來存放註冊的中間件

class Router {
    constructor(){
        this.middles=[];
    }
}
module.exports = Router
複製代碼

get(path,fn):用來註冊中間件,往middles中存放,因爲是路由中間件這裏多了一個參數path

class Router {
    constructor(){
        this.middles=[];
    }
    get(path,fn){//set,post等同理
        let layer = {
            path,
            fn,
            method
        }
        //處理相似/article/:id的路由
        if(path.includes(':')){ 
            let params  = [];
            let reg = path.replace(/:([^\/]*)/g,function () {
                params.push(arguments[1]);  //params = [id]
                return '([^\/]*)'   //返會字符串/article/([^\/]*)
            });
            //將返回的字符串變成正則,後面解析路由是會用到
            layer.reg = new RegExp(reg);//返回/\/article\/([^\/]*)/
            layer.params = params;
      }
        this.middles.push(layer);
    }
}
module.exports = Router
複製代碼

compose():用來組合中間件,讓路由中間件按順序執行

class Router {
    constructor(){
        this.middles=[];
    }
    get(path,fn){//set,post等同理
        let layer = {
            path,
            fn,
            method
        }
        //處理相似/article/:id的路由
        if(path.includes(':')){ 
            let params  = [];
            let reg = path.replace(/:([^\/]*)/g,function () {
                params.push(arguments[1]);  //params = [id]
                return '([^\/]*)'   //返會字符串/article/([^\/]*)
            });
            //將返回的字符串變成正則,後面解析路由是會用到
            layer.reg = new RegExp(reg);//返回/\/article\/([^\/]*)/
            layer.params = params;
      }
        this.middles.push(layer);
    }
    compose(lasts,next,ctx){//lasts爲匹配的路由集合 
        dispatch(index){
            if(index === lasts.length) return next();
            let route = lasts[index];
            //將路徑參數都取出來exp:id的值
            let params = route.params;
            let [, ...args] = pathname.match(route.reg);
            ctx.request.params = params.reduce((memo,key,index)=>(memo[key] = args[index], memo), {});
            //執行路由邏輯,next賦值爲下一個路由邏輯
            route.fn(ctx,()=>{  
                dispatch(index+1);
            })
        }
        dispatch(0)
    }
}
module.exports = Router
複製代碼

routes():用來將koa-router中間件註冊到app的中間件上,主要做用是調用路由中間件匹配請求的路徑ctx.path

class Router {
    constructor(){
        this.middles=[];
    }
    get(path,fn){//set,post等同理
        let layer = {
            path,
            fn,
            method
        }
        //處理相似/article/:id的路由
        if(path.includes(':')){ 
            let params  = [];
            let reg = path.replace(/:([^\/]*)/g,function () {
                params.push(arguments[1]);  //params = [id]
                return '([^\/]*)'   //返會字符串/article/([^\/]*)
            });
            //將返回的字符串變成正則,後面解析路由是會用到
            layer.reg = new RegExp(reg);//返回/\/article\/([^\/]*)/
            layer.params = params;
      }
        this.middles.push(layer);
    }
    compose(lasts,next,ctx){//lasts爲匹配的路由集合 
        dispatch(index){
            if(index === lasts.length) return next();
            let route = lasts[index];
            route.fn(ctx,()=>{
                dispatch(index+1);
            })
        }
        dispatch(0)
    }
    routes() {
    // ctx上下文next指的是koa中的next方法
    return async (ctx, next) => {
      let pathname = ctx.path;  //請求的路徑
      let lasts = this.middles.filter(item => {
            if (route.reg) {     // 說明當前路由是一個路徑參數
                if (method === route.method && route.reg.test(pathname)) {
                    return true //路徑參數匹配到了,添加進路由組 
                }
            }
            if ((method === route.method || route.method === 'all') && (route.p === pathname || route.p === '*')) {
                return true //路徑是'/'或者'all',添加進路由組 
            }
            return false;
      });
      this.compose(lasts, next, ctx);
    }
  }
}
module.exports = Router
複製代碼

關於其餘

上面的router是簡化版的koa-router,它只實現了koa-router中的一級路由,可是倒是能說明koa-router的主要思想,koa-router中添加了use來註冊二級路由,同時添加了不少包括重定向等其餘邏輯處理sql

結語

koa-router中間件的原理基本就介紹完了,後面一塊兒學習kao的其餘中間件:數據庫

相關文章
相關標籤/搜索