美文网首页JavaScript 进阶营
js中不同框架中间件实现的区别

js中不同框架中间件实现的区别

作者: 勤奋的大鱼 | 来源:发表于2018-07-09 18:01 被阅读1次

      用过express,koa,react(redux)的都会知道中间件,那么在这三个框架的中间件的实现有什么不同呢?

    Express中间件

    首先来看看express,分析express的源码可以看出express中router中间件和express是强耦合的,express把中间件的实现放在了router中去。
    大概讲一下这里的中间件实现逻辑:

    1. 通过app.use收集中间件到stacks中去
    2. 有客户端请求时,遍历执行所有的中间件(把下一个中间件作为上一个中间件的next传入)

    以下是这个逻辑的简单实现:

        var app = {
          stacks: [],
          // 收集中间件
          use: function () {
            var fns = arguments
            for (var i = 0, l = fns.length; i < l; i++) {
              this.stacks.push(fns[i])
            }
          },
          // 触发中间件
          handle: function (req, res) {
            var idx = 0,
                stacks = this.stacks;
            next()
            function next () {
              if (idx === stacks.length) {
                return;
              }
              var match = stacks[idx];
              idx++;
              match(req, res, next)
            }
          }
        }
        app.use(function (req, res, next) {
          console.log(0)
          next()
          console.log('00')
        }, function (req, res, next) {
          console.log(1)
          next()
          console.log(11)
        }, function () {
          console.log(2)
        })
        app.handle()
    
    Koa中间件
        function compose (middleware) {
          return function (context, next) {
            // last called middleware #
            let index = -1
            return dispatch(0)
            function dispatch (i) {
              // 注释的部分为Koa源码,源码对async/await更加友好,为了简便对比,这里省略一些
              // if (i <= index) return Promise.reject(new Error('next() called multiple times'))
              index = i
              let fn = middleware[i]
              if (i === middleware.length) return Promise.resolve()
              // if (!fn) return Promise.resolve()
              // try {
              //   return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
              // } catch (err) {
              //   return Promise.reject(err)
              // }
              return fn(context, dispatch.bind(null, i + 1))
            }
          }
        }
        var app = {
          stacks: [],
          use: function () {
            var fns = arguments
            for (var i = 0, l = fns.length; i < l; i++) {
              this.stacks.push(fns[i])
            }
          },
          listen: function (...args) {
            // const server = http.createServer(this.callback())
            // return server.listen(...args)
            // 以下代码是为了方便调用中间件
            var cb = this.callback()
            cb()
          },
          callback: function () {
            var fn = compose(this.stacks)
            return fn
          }
        }
        app.use(async function(ctx, next) {
          console.log(1)
          await next()
          console.log(11)
        }, async function (ctx, next) {
          console.log(2)
          await next()
          console.log(22)
        }, async function (ctx, next) {
          console.log(3)
          await next()
          console.log(33)
        })
        app.listen()
    

    中间件在Koa中的实现其实和Express没有本质的区别,逻辑基本上是一样的,多了一步compose,就是把中间件组合成一个函数。
    说起Koa的中间件,很多地方都会提到洋葱模型,关于洋葱模型,按之前express的例子来说,就是按输出0,1,2,11,00的顺序执行,所以说洋葱模型并不是Koa特有的东西,另外,async/await也不是Koa才有的,async/await只是一个语法糖而已。
    在我看来,Koa的中间件和Express的中间件区别在于Koa对异步中间件的处理可能更友好一些,从代码中的注释的源码部分可以看出。(对Koa没有实际的项目经验,仅就自己对源码的一些理解,有不对的地方希望可以不吝赐教)

    Redux中间件

    Redux中间件和这里使用的场景不同,这里我按Redux中间件的写法实现在node框架中的中间件。

        function compose (middleware) {
          return middleware.reduceRight((composed, f) => f(composed), () => {})
        }
        var app = {
          stacks: [],
          use: function () {
            var fns = arguments
            for (var i = 0, l = fns.length; i < l; i++) {
              this.stacks.push(fns[i])
            }
          },
          listen: function (...args) {
            // const server = http.createServer(this.callback())
            // return server.listen(...args)
            var cb = this.callback()
            cb()
          },
          callback: function () {
            var ctx = {request: {}, response: {}}
            var middlewares = this.stacks.map(middleware => middleware(ctx))
            var fn = compose(middlewares)
            return fn
          }
        }
        app.use(ctx => next => () => {
          console.log(1, ctx)
          next()
          console.log(11)
        }, ctx => next => () => {
          console.log(2, ctx)
          next()
          console.log(22)
        }, ctx => next => () => {
          console.log(3, ctx)
          next()
          console.log(33)
        })
        app.listen()
    

    比较有意思的是compose函数的写法,利用es6,仅有一行代码就实现了。
    这里是函数式编程中的currying,是一种使用匿名单参数函数实现多参数函数的方法。

    相关文章

      网友评论

        本文标题:js中不同框架中间件实现的区别

        本文链接:https://www.haomeiwen.com/subject/qnbayftx.html