redux-middleware与apollo-links方法链执行及上下文传递原理咨询
Redux Middleware 与 Apollo Links 执行调度、上下文传递原理
两者核心都基于洋葱圈函数嵌套模型实现,不存在特殊调度黑魔法,执行顺序和上下文传递逻辑可以拆解为以下两部分:
Redux Middleware 运行机制
调度顺序
- 所有传入
applyMiddleware的中间件会在store初始化阶段,通过compose方法被组装成一层套一层的嵌套函数链,注册顺序越靠前的中间件,处在嵌套结构的越外层。 - 触发dispatch时,执行流先从最外层中间件开始逐层向内穿透,直到到达最内层的原生
dispatch方法,执行reducer完成state更新;之后执行流会按注册顺序的逆序逐层向外折返,每个中间件里写在next(action)调用之后的逻辑,会在折返阶段依次执行。 - Redux源码中核心组合逻辑只有几行,去掉边界判断的核心实现如下:
function compose(...funcs) { return funcs.reduce((composed, currentFunc) => { return (...args) => composed(currentFunc(...args)) }) }
举个实际执行流例子:如果按顺序注册logger、thunk、auth三个中间件,完整执行路径为:
- 外部调用dispatch,首先进入logger中间件
- logger打印action触发日志,调用next把action传给thunk
- thunk判断action类型:如果是函数就执行函数本身,否则调用next把action传给auth
- auth给action附加鉴权token字段,调用next把action传给原生dispatch
- 原生dispatch执行reducer更新state,返回更新结果
- 执行流折返到auth,拿到返回结果做后置处理
- 执行流折返到thunk,透传返回结果
- 执行流折返到logger,打印state更新完成日志,把最终结果返回给外部调用方
上下文传递
每个Redux中间件的标准签名为({ getState, dispatch }) => next => action => {},上下文通过三个渠道传递:
- 第一层入参是store初始化时注入的全局共享上下文:所有中间件拿到的是同一个
getState方法(调用时始终返回最新的store state),以及包装后的全局dispatch方法(如果在中间件内调用这个dispatch而非next,会从最外层中间件重新走完整链路,而非继续当前执行流)。 - 第二层入参
next是compose阶段自动注入的、指向链路中下一个中间件的引用,是执行流向下传递的唯一入口。 - 第三层入参
action是链路上手动传递的上下文载体:中间件可以在调用next前修改action、新增自定义字段,下一层中间件拿到的就是修改后的action对象;next方法的返回值是后续链路的执行结果,会逐层向上回传。
- 任意中间件都可以截断链路:只要不调用next方法,直接返回自定义结果,后续内层的所有中间件和原生dispatch都不会执行。
Apollo Links 运行机制
调度顺序
- Apollo Links的调度模型和Redux中间件完全一致,只是组合方法封装为
ApolloLink.from,传入的Link数组顺序越靠前,处在链路越外层;数组最后一个必须是终止链(Terminating Link,比如常用的HttpLink),等价于Redux里的原生dispatch,负责真正发起请求。 - 触发GraphQL操作时,执行流从最外层Link开始向内穿透,直到到达终止链发起真实请求拿到响应,之后再按链路逆序向外折返,每个Link在forward调用之后定义的响应处理逻辑,会在折返阶段执行。
- 去掉框架封装后的核心组合逻辑如下:
function composeLinks(links) { return links.reduceRight((nextLink, currentLink) => { return new ApolloLink((operation, forward) => { return currentLink.request(operation, () => nextLink.request(operation, forward)) }) }) }
举个实际执行流例子:如果按顺序传入AuthLink、LogLink、HttpLink三个Link,完整执行路径为:
- 客户端触发query/mutation,生成operation实例,首先进入AuthLink
- AuthLink读取本地存储的token,给请求附加鉴权头,调用forward把执行权传给LogLink
- LogLink打印请求发起日志,调用forward把执行权传给HttpLink
- HttpLink读取请求参数和配置,发起真实HTTP请求,拿到响应后包装为Observable返回
- 执行流折返到LogLink,给返回的Observable加日志埋点,捕获错误打印日志
- 执行流折返到AuthLink,给Observable加401状态捕获逻辑,遇到鉴权失效就触发登出流程
- 最终把处理后的Observable返回给调用方,订阅后即可拿到最终响应
上下文传递
每个Link的核心处理方法签名为request(operation, forward),上下文传递比Redux更封装化:
- 第一个入参
operation是贯穿整个链路的共享上下文实例,自带getContext、setContext方法,所有Link拿到的是同一个对象引用,不需要手动透传:任意一层调用setContext写入的配置(比如请求头、超时时间、自定义标记),后续所有层都可以通过getContext直接读取,不需要像Redux那样手动修改action传值。 - 第二个入参
forward等价于Redux里的next方法,调用后就会把执行权交给下一层Link,默认会把同一个operation实例传给下一层,不需要手动传入。 - forward方法返回的是Observable对象,支持通过map、catchError、tap等操作符处理后续链路返回的响应、错误,处理后的结果会逐层向上回传。
- 和Redux中间件一致,任意Link都可以截断链路:只要不调用forward方法,直接返回自定义的Observable对象,后续内层Link和终止链都不会执行。
核心差异总结
- 调度逻辑完全同源,都是「正向逐层穿透→终点执行核心逻辑→反向逐层折返」的洋葱模型,单线程按嵌套顺序同步/异步执行,不存在特殊的并行调度逻辑。
- 上下文传递的区别:
- Redux Middleware没有内置独立的上下文存储,靠闭包引用getState、手动修改透传action来共享数据,调用next时必须显式传入action对象。
- Apollo Links内置绑定在operation上的上下文存储,不需要手动透传操作对象,上下文读写有统一API,更适配异步请求场景的配置传递需求。
- 终点逻辑的区别:
- Redux链路终点是同步的原生dispatch,执行reducer后直接返回更新后的state。
- Apollo Links终点是异步的终止链,返回Observable对象,天然支持流式响应、请求取消等异步场景能力。
内容的提问来源于stack exchange,提问作者milad shirian
相关产品推荐
相关产品推荐

