• 看完 Koa 源码我把核心思想应用到了公司项目


    本文你可以学到

    1.理解koa2 洋葱模型核心源码 compose 函数实现
    2.理解函数式编程衍生范式——面向切面编程
    3.除了看源码,实战把洋葱模型思想应用到 SDK 项目中

    Koa2 源码实现

    通过它控制中间件内部内容的执行顺序。先来复习一下中间件结构。

    function logger(ctx,next){} 
    
    • 1

    类似这种的每个函数都是一个中间件。 然后这些中间件会被存放到一个 middlewares 中间件数组中。

    然后依赖的核心库是 koa-compose,去完成整个 middlewares 数组中函数的执行,他不是普通的遍历依次执行过程,里面有一些特殊实现,重点关注下 源码中 dispatch 函数实现。

    源码如下,对核心代码部分进行了注释讲解

    // compose 函数参数是前面提到的 middlewares 中间件数组
    function compose(middleware) {// 参数校验:判断middleware是否为数组if (!Array.isArray(middleware))throw new TypeError("Middleware stack must be an array!");// 数组内容校验:中间件数组中每一项必须是一个方法for (const fn of middleware) {if (typeof fn !== "function")throw new TypeError("Middleware must be composed of functions!");}// 返回一个方法,这个方法就是compose的结果// 外部可以通过调用这个方法来开起中间件数组的遍历// 参数形式和普通中间件一样,都是context和nextreturn function (context, next) {return dispatch(0); // 开始中间件执行,从数组第一个开始// 中间件执行函数function dispatch(i) {let fn = middleware[i]; // 取出需要执行的中间件// 如果i等于数组长度,说明数组已经执行完了if (i === middleware.length) {fn = next; // fn等于外部传进来的next,结束执行}// 如果外部没有传结束执行的next,直接就resolveif (!fn) {return Promise.resolve();}// 执行中间件,注意传给中间件接收的参数应该是context和next// 传给下一个中间件的next是函数,一定注意这里是使用的bind dispatch.bind(null, i + 1)// 所以中间件里面调用 next 的时候其实调用的是dispatch(i + 1),也就是执行下一个中间件try {return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));} catch (err) {return Promise.reject(err);}}};
    } 
    
    • 1
    • 2
    • 3

    对这段代码的实现进行了详细的注释,再次强调一下代码的核心部分

    return Promise.resolve(fn(context, dispatch.bind(null,i+1))) 
    
    • 1

    fn 执行的第二个参数实际是中间件数组中的函数引用(使用了 bind 函数),在中间件内部调用 next 实际调用的是 dispatch(i+1),也就是下一个中间件。

    洋葱模型思想在项目中的应用

    需求描述

    我们要提供一个 Node.jsSDK,在这个 SDK 中我们提供了一系列功能,本文要讲的是其中一个小部分:请求函数中聚合中间件实现,SDK 使用者发起一个请求会调用 SDKrequest 请求函数,这个函数我们应该怎么封装呢?

    SDK.request 调用理论上会执行下面的一系列中间件函数。

    如图所示,包括的功能有 危险字符过滤日志记录响应处理等等(这里就不一一列举了),它的实现正需要一个洋葱模型的机制,上分支是洋葱进入时前期处理的函数,然后交给并等待其他中间件处理面,下分支是扒开洋葱后期处理的过程,

    理论科普: 洋葱模型也叫面向切面编程。 AOP 为 Aspect Oriented Programming 的缩写,中文意思为:面向切面编程,它是函数式编程的一种衍生范式。面向切面编程的是在现有程序中,加入或减去一些功能(函数中间件)不影响原有的代码功能。比如我们的 request 需求中去除 log 记录中间件。

    分析与代码实现

    1.首先我们定义一个存放中间件的 moduleList
    2.定义compose函数
    3.执行 dispatch(0) 以及在 dispatch 函数内部调用 fn.call(null,context,dispatch.bind(null,i+1))

    const moduleList = [require('./dangerQuery'),require('./log'),...// 省略一部分require('./resHandler')
    ];
    export const compliations = (ctx:Context,next:Next)=>{const context = {ctx,next};cosnt composeFn = compose(moduleList);composeFn(context);
    }
    
    function compose = (list:Array)=>{if(!Array.isArray(list)){ throw new TypeError("ModuleList must be an array!");}for(const fn of list){if(typeof fn !== 'function'){throw new TypeError("ModuleList must be composed of functions!");}}return function(context:{ctx:Context,next:Next}){const dispatch = async (i:number)=>{if(list.length>i){const fn = list[i];await fn.call(null,context,dispatch.bind(null,i+1))}}return dispatch(0)}
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    中间件实现很多注意的点,本文只是想把洋葱模型部分思想理解,并应用起来,实际每个中间件内部要支持可插拔机制和开关机制的;并且 moduleList 中最后一个中间件函数,实际函数的第二个 next 参数已经为空了,不要再次执行,如果SDK是基于egg,midway等进行封装的,最后一个中间件内部应使用 await context.next()

    感悟

    1.好东西就要用起来,除了这部分,自己项目中用到洋葱模型思想的还比较多的,并且开源项目中也很多,比如WebpackRedux。 可以看看他们的使用有哪些巧妙之处。
    2.我们在看源码的过程中。不要为了看源码而看源码,最好看懂后应用起来才会真的掌握
    3.面试过程中如果写到了 koa,洋葱模型肯定是一个必考项,如果能把远离说清楚,并举例将思想用到了自己项目中我觉得也是一个加分项。

    参考文章

    1.juejin.cn/post/707890…
    2.github.com/koajs/koa

  • 相关阅读:
    Docker安装EMQX
    Android 状态栏显示运营商名称
    程序的编译与链接
    2023湖北汽车工业学院计算机考研信息汇总
    java的ArrayList && LinkedList的操作
    9.1 运用API创建多线程
    ROS从入门到精通(十) TF坐标变换原理,为什么需要TF变换?
    进程间通信(27000字超详解)
    Java学习笔记3.10.5 异常处理 - 自定义异常
    如何使用 .Net Core 实现数据库迁移 (Database Migration)
  • 原文地址:https://blog.csdn.net/web220507/article/details/126872650