• wordpress CMS主题:ssmay主题 wordpress CMS主题:ssmay主题
  • 首页 > 前端开发 > 这份Koa的简易Router手敲指南请收下

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

    作者: 分类:前端开发 点击: 445 次
    wordpress CMS主题:ssmay主题

      扫描下面的二维码,“关注”我的百家号。

      上一期链接——也就是本文的基础,参考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()),也就是说:

      • Router本身就是个中间件
      • 为了返回匹配的路由,写一个中间件挂到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"
      })
      


      欢迎“关注”我的百家号。

      头条二维码
      加入我的QQ群
      头条二维码
      关注我的百家号

    文章作者:sunny
    本文地址:http://wanlimm.com/77202006218489.html
    版权所有 © 转载时必须以链接形式注明作者和原始出处!

    上一篇:
    下一篇:
    wordpress CMS主题:ssmay主题

    或许你会感兴趣的文章:

    发表评论

    电子邮件地址不会被公开。 必填项已用*标注

    This site uses Akismet to reduce spam. Learn how your comment data is processed.