前言

最早提出 Middleware 概念的是 Express,随后由原班人马打造的 Koa 不但沿用了 Middleware 的架构设计,还更加彻底的把自己定义为中间件框架。Redux也引入了 Middleware 的概念,方便独立功能的函数对 Action 进行处理。Axios虽然没有中间件,但其拦截器的用法却跟中间件十分相似。本文结合使用场景,拆解对比各大框架的 Middleware 的实现原理, 。

Middleware

Middleware(中间件)本意是指位于服务器的操作系统之上,管理计算资源和网络通信的一种通用独立的系统软件服务程序。分布式应用软件借助这种软件在不同的技术之间共享资源。

而大前端领域,Middleware 一般指提供通用独立功能的数据处理函数,包括日志记录、数据叠加和错误处理等。

Express

Express 中应用层级的中间件的注册方式:

const stack = [];
/** 通过 use 注册 */
function use(fn) {stack.push(fn);
}/** 请求到达的时候,会触发handle方法。接着next函数从队列中顺序取出并执行 */
function handle(req, res) {var idx = 0;next();function next() {var fn = stack[idx++];fn(req, res, next)}
}

Koa

Koa的 Middleware 注册跟路由无关,所有的请求都会经过注册的中间件。同时Koa 支持async/await异步编程模式:

/** 注册 */
function use(fn) {// 省略部分代码...this.middleware.push(fn);return this;
}

Koa 的 Middleware 顺序执行,通过 dispatch函数来控制,compose 函数对已注册的中间件列表(middleware)栈内每一个中间件函数的校验,并返回 fn  函数。

function compose (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!');}/*** @param {Object} ctx* @return {Promise}* @api public*/return function fn (ctx, next) {return dispatch(0);function dispatch (i) {let middlewareFn = middleware[i]try {return Promise.resolve(middlewareFn(ctx, dispatch.bind(null, i + 1)));} catch (err) {return Promise.reject(err);}}}
}

Redux

Redux中间件的参数经过柯里化,store是applyMiddleware内部传进来的,next是compose后传进来的,action是dispatch传进来的

export default function applyMiddleware(...middlewares) {return (createStore) =>(reducer, preloadedState) => {const store = createStore(reducer, preloadedState)let dispatch = store.dispatch;let chain = [];const middlewareAPI = {getState: store.getState,dispatch: (action) => dispatch(action)}/** 先执行一遍middleware,把第一个参数store传进去 */chain = middlewares.map(middleware => middleware(middlewareAPI));/** 传入原始的dispatch */dispatch = compose(...chain)(store.dispatch)return {...store,dispatch}}
}

这里 compose 的返回值又重新赋值给dispatch:

function compose (...funcs) {if (funcs.length === 0) {return arg => arg}if (funcs.length === 1) {return funcs[0]}return funcs.reduce((a, b) =>(...args) => a(b(...args)))
}

说明我们在应用内调用的dispatch并不是store自带的,而是经过 Middleware 处理的升级版。

Axios

axios没有中间件,但有类似功能的拦截器(interceptors),本质上都是在数据处理链路的 2 点之间,提供独立的、配置化的、可叠加的额外功能。

function Axios(instanceConfig) {this.defaults = instanceConfig;this.interceptors = {request: new InterceptorManager(),response: new InterceptorManager()};
}
function InterceptorManager() {this.handlers = [];
}
InterceptorManager.prototype.use = function use(fulfilled, rejected) {this.handlers.push({fulfilled: fulfilled,rejected: rejected});return this.handlers.length - 1;
};

Axios内部会维护 2 个 interceptors,它们有独立的 handlers 数组。use就是往数组添加元素而已,跟其它框架不同的是这里的数组元素不是一个函数,而是一个对象,包含fulfilled和rejected 2 个属性。第二个参数不传的时候rejected就是 undefined。

通过 promise 的链式调用,将 interceptors 串联了起来,执行顺序是:requestInterceptorChain -> chain -> responseInterceptorChain。这里有一个默认的约定,chain 里的元素都是按照[fulfilled1, rejected1, fulfilled2, rejected2]这种模式排列的,所以注册 interceptors 的时候如果没有提供第二个参数,也会有一个默认值 undefined:

Axios.prototype.request = function request(config) {config = mergeConfig(this.defaults, config);// 成对的添加元素var requestInterceptorChain = [];this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {requestInterceptorChain.unshift(interceptor.fulfilled, interceptor.rejected);});var responseInterceptorChain = [];this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {responseInterceptorChain.push(interceptor.fulfilled, interceptor.rejected);});var chain = [dispatchRequest, undefined];Array.prototype.unshift.apply(chain, requestInterceptorChain);chain.concat(responseInterceptorChain);promise = Promise.resolve(config);while (chain.length) {promise = promise.then(chain.shift(), chain.shift());}return promise;
}

总结

这里面最精妙也是最难理解的就是Array.reduce这种形式,需要反复的推敲。promise.then链式调用的任务编排方法也十分巧妙,前面处理完的数据会自动传给下一个then。递归调用的形式则最好理解,Koa在Express实现的基础上天然支持异步调用,更符合服务器端场景

理解前端的 Middleware 原理与实现相关推荐

  1. 理解前端Babel编译原理

    大厂技术  坚持周更  精选好文 背景 我们知道编程语言主要分为「编译型语言」和「解释型语言」,编译型语言是在代码运行前编译器将编程语言转换成机器语言,运行时不需要重新翻译,直接使用编译的结果就行了. ...

  2. 你如何理解前端的工作(面试题)

    入坑前端到今天也将近两年半了,这两天突然想到了第一次面试时面试官的一个问题-------你怎样理解前端的工作? 对于当时我一个小白而言完全是胡说一通,词不达意,搞得面试官一脸懵逼,现在想想那可能就叫尬 ...

  3. Vue模板语法(理解前端渲染)

    目录 如何理解前端渲染: 前端渲染方式: 1.原生js拼接字符串: 2.使用前端模板引擎 ​ 模板语法概括: 指令? ​ v-cloak解决闪动问题.​ 数据绑定指令 数据响应式: 双向数据绑定: 事 ...

  4. 前端路由工作原理与使用

    单页应用和多页应用 单页面应用:所有功能在一个页面上实现 一个.html 文件 前端路由 组件化开发 网易云音乐 小米移动端 多页应用:与单页应用相对应的,不同的功能通过不同的页面来实现 单页面 - ...

  5. 大前端的技术原理和变迁史

    本文适合前端新手入门,阅读人群最好是前端新手或者后台开发人员,因为我不敢保证对前端老司机有太多收获. 通过阅读本文,你将会大致了解前端这些年发生的事情,以及一些前端当前主流技术的简单原理介绍.所有涉及 ...

  6. [diango]理解django视图工作原理

    前言:正确理解django视图view,模型model,模板的概念及其之间的关联关系,才能快速学习并上手使用django制作网页 本文主要讲解自己在学习django后对视图view的理解 在进入正文之 ...

  7. 深入理解CPU的调度原理

    前言 软件工程师们总习惯把OS(Operating System,操作系统)当成是一个非常值得信赖的管家,我们只管把程序托管到OS上运行,却很少深入了解操作系统的运行原理.确实,OS作为一个通用的软件 ...

  8. 京东面试官:你是怎么理解 MySQL 的优化原理的?

    说起MySQL的查询优化,相信大家收藏了一堆奇技淫巧:不能使用 SELECT*.不使用NULL字段.合理创建索引.为字段选择合适的数据类型..... 你是否真的理解这些优化技巧?是否理解其背后的工作原 ...

  9. 【分布式ID】理解Snowflake算法的实现原理

    1.概述 转载:冷饭新炒:理解Snowflake算法的实现原理 我上次也看了一个视频讲解:[分布式ID]键高并发 分布式 全局唯一 ID 雪花算法 snowflake 2.前提# Snowflake( ...

最新文章

  1. gin context和官方context_gin 源码阅读(二) 路由和路由组
  2. 一家美资企业的java servlet面试题
  3. 【Alpha】第二次Scrum meeting
  4. 高等数学上-赵立军-北京大学出版社-题解-练习2.6
  5. Echarts报错:Component series.lines not exists. Load it first.
  6. failed to keep to the max pss of 66560
  7. lcd驱动解析(一)
  8. 由sock引起的感想
  9. 什么是PaaS云平台?
  10. hibernate历史版本下载
  11. 动态视频壁纸多功能工具箱微信小程序源码,支持外卖CPS和流量主
  12. 计算机单片机实训报告,单片机实训报告范文
  13. abp The value could not be converted to a GUID:
  14. 瞎琢磨先生のJava笔记之Java代码远程调用shell脚本
  15. 怎样查找计算机死机日志,死机和日志错误
  16. 使用功能点估算模型评估软件测试的工作量
  17. http 网络异常请求处理
  18. 什么是UI(UI百科)
  19. JZ-008-跳台阶
  20. SLUB和SLAB的区别

热门文章

  1. es带用户名密码验证并配置elasticsearch-head连接
  2. 既然来到了这个世界,何必还是那么认真
  3. android下保存图片到mySQL_android将图片保存进数据库
  4. 《SQL Server 2008从入门到精通》--20180627
  5. 仅需三步就可以把代码块完美插入到word中
  6. 2年内成准独角兽,影刀RPA登榜2021杭州独角兽准独角兽企业榜单
  7. Ralink无线驱动 有SoftAP_Mode ,STA_Mode
  8. 转载:天涯——散文天下——《自我介绍》——作者:南方孤驴
  9. 关于心理的二十五种倾向(查理·芒格)-2
  10. 城市经济发展——城市化与第三产业的发展 考题答案