这份Koa的简易Router手敲指南请收下

栏目: Node.js · 发布时间: 6年前

内容简介:上一期链接——也就是本文的基础,本文参考仓库:Router其实就是路径匹配,通过匹配路径,返回给用户相应的网站内容。

上一期链接——也就是本文的基础, 参考KOA,5步手写一款粗糙的web框架

本文参考仓库: 点我

Router其实就是路径匹配,通过匹配路径,返回给用户相应的网站内容。

以下方例子为例,主要通过提取 req 中的 path 信息,来匹配当前路径,并给 ctx.body 赋值,返回相应的界面。这个过程不复杂,就是一个匹配路径的过程。但是这种会不会太臃肿了呢,而且很有可能路径一多,就要被 if...else... 给弄晕了。

app.use((ctx,next)=>{
   //简易路由
   let {path}=ctx
   if(path==="/"){
       ctx.body="index"
   }else if(path==="/admin"){
        ctx.body="admin"
   }else if(path==="/user"){
        ctx.body="user"
   }
})
复制代码

这个时候专门处理路径的插件就出现了,写一个 Router ,专门用来管理路径。

Router的功能一共是两个:

  • 匹配路径
  • 返回相应页面

如果Router要挂载到app上,那么语法是这样的 app.use(router.routes()) ,也就是说:

app

了解了Router的大概,我们开始一步步动手写Router吧!

STEP1 创建Router

先把Router的框架写好,一个构造器,一个 get 方法用于配置路由,一个 routers 变成路由匹配的中间件挂在到app上。

class Router{
    constructor(){}
    get(path,callback){}
    routers(){}
}
复制代码

我们获取路由的时候,一定会配置页面,那么这个页面的类也要加上了,每次 get 的时候,就加入一个页面到数组中。

class Page{
    constructor(path,callback){
        this.path=path
        this.callback=callback
    }
}
class Router{
    constructor(){
        this.pages=[]
    }
    get(path,callback){
        this.pages.push(new Page(path,callback))
    }
    routers(){}
}
复制代码

因为路由是对中间件的封装,所以用法上是和 app.use 类似的:

router.get(path,(ctx,next){
    ctx.body='xxx'
    next()
})
复制代码

是不是很眼熟?这个get中的 callback 参数就是中间件。

STEP2 写一个中间件,返回匹配路由的中间件

routers 就干三件事:

array.filter
compose(ctx,next,routers){
    function dispatch(index){
        if(index===routers.length) return next();
        let router=routers[index]
        router(ctx,()=>dispatch(index+1));
    }
    dispatch(0)
}
routers(){
    let dispatch = (ctx,next)=>{
        let path=ctx.path    
        let routers=this.pages.filter(p=>{console.log(p.path);return p.path===path}).map(p=>p.callback)
        this.compose(ctx,next,routers)
    }
    return dispatch
}
复制代码

大家有没有很眼熟,和koa中的application.js的回调很像。其实就是一个回调的过程,封装之后,便于我们使用。

STEP3 给路由分个组吧

我们再写路由的时候,如果全部写全路径,感觉会很啰嗦:

router.get("/admin",(ctx,next)=>{})
router.get("/admin/login",(ctx,next)=>{})
router.get("/admin/register",(ctx,next)=>{})
...
router.get("/user",(ctx,next)=>{})
router.get("/user/login",(ctx,next)=>{})
router.get("/user/register",(ctx,next)=>{})
....
复制代码

我们给路由分组,其实思路很简单,就是给每个小路由新建一个Router,然后大路由用 use 方法,将这些路由集合到一起。

let admin=new Router()
admin.get("/",(ctx,next)=>{
    ctx.body="admin"
    next()
})
let user=new Router()
user.get("/",(ctx,next)=>{
    ctx.body="user"
    next()
})
//链式调用~
let router=new Router()
router.use("/admin",admin.routers())
.use("/user",user.routers())

app.use(router.routers())
复制代码

那么问题来了, use 要怎么写呢才能组合这些routers??我们先来分析下 use 的功能:

  • 组合路径
  • 将route加入当前对象的数组中

use 中有两个参数一个 path ,一个 router.routers() 的中间件,可是我们需要router数组对象,所以我们可以这么做:

routers(){
    let dispatch = (ctx,next)=>{
      .....
    }
    dispatch.router=this
    return dispatch
}
复制代码

在中间件上暗搓搓地加一个router的对象,将自己一起传递出去,有么有很机智

有了 router 的数组对象,那么 use 这个方法就很好实现了,将 page 循环一波,加入当前对象的 pages ,就好了。这里再将自己返回,然后就可以愉快地使用链式调用了。

use(path,middleware) {
    let router = this;
    middleware.router.pages.forEach(p => {
        router.get(path+p.path,p.callback)
    });
    return router
}
复制代码

step4 LAST BUT NOT LEAST

大家需要注意,还记得上一期讲的async/await异步吗?

如果有任何除了路由的操作都要放在路由上方执行,因为路由只是匹配路径,返回结果,并没有async/await操作。

所以一定注意:

这样是有效的·,页面返回aaa

app.use(async (ctx,next)=>{
    await makeAPromise(ctx).then(()=>{next()})
})
...
app.use(router.routers())
复制代码

这样是无效的,页面不会返回aaa

...
app.use(router.routers())
app.use(async(ctx,next)=>{
    await next()//等待下方完成后再继续执行
    ctx.body="aaa"
})
复制代码

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

大演算

大演算

佩德羅.多明戈斯 / 張正苓,胡玉城 / 三采 / 2016-8-1 / 620

揭開大數據、人工智慧、機器學習的祕密, 打造人類文明史上最強大的科技——終極演算法! 有一個終極演算法,可以解開宇宙所有的祕密, 現在大家都在競爭,誰能最先解開它! .機器學習是什麼?大演算又是什麼? .大演算如何運作與發展,機器可以預測什麼? .我們可以信任機器學過的東西嗎? .商業、政治為什麼要擁抱機器學習? .不只商業與政治,醫學與科學界也亟需......一起来看看 《大演算》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具