Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

通过redux-thunk的源码来理解redux对中间件的处理机制 #4

Open
lewenweijia opened this issue Mar 22, 2018 · 0 comments
Open

Comments

@lewenweijia
Copy link
Owner

lewenweijia commented Mar 22, 2018

Warning ⚠️

  • 全程涉及源码, 有阅读源码不适者迅速撤离
  • 笔者不才, 本文篇幅长

前言

redux-thunk是redux的一个中间件, 实现对dispatch操作的异步实现, 然而其原码却极其精简:

  • 仓库src/目录里面只有一个index.js文件
  • index.js文件里面只有14行(v2.2.0)
  • 没有任何其他的运行时依赖

揭开redux-thunk的面纱

因为只有十四行, 可以直接贴上来:

function createThunkMiddleware(extraArgument) {
  return ({ dispatch, getState }) => next => action => {
    if (typeof action === 'function') {
      return action(dispatch, getState, extraArgument);
    }

    return next(action);
  };
}

const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;

export default thunk;

细数一下有3层函数注入, 生成最后实际接收action的函数. 这里可以认为:

  • 前三次的每一次都利用闭包性质, 让最后的实体函数执行的时候能都访问前面的参数, 实现功能注入

先看最内层匿名函数的逻辑:

    if (typeof action === 'function') {
      return action(dispatch, getState, extraArgument);
    }
    return next()

因为如果action属于函数类型, 那么认为该函数会进行异步操作, 进行异步操作意味着不会马上执行, 而每一次action都需要实际的dispatch操作, 所以将dispatchgetState作为参数的形式注入异步action对象, 让该函数获得访问store的能力和提出dispatch的能力, 希望其(该action函数)在将来执行实际的dispatch操作.

因为redux-thunk是作为redux的中间件的实体存在, 所以辅以redux源码如何应用中间的逻辑就能帮助我们完全弄懂以上的各个=>的所有意思, 这里可以先根据每一次的参数提前点出相关认识:

  • extraAugument, 对异步action函数注入额外的参数, 相当于一个全局(所有action函数的范畴)变量. 默认的最终redux-thunk模块对外暴露是抛弃该参数的: const thunk = createThunkMiddleware();.

  • ({ dispatch, getState }), 注入访问store和发起dispath的能力

  • next, 与其他中间件获得链式关联

  • action, 实际action实体, redux-thunk的任务就是对每一次传到本层的action进行校验, 判断其是否为函数, 如果符合, 则对该action进行dispatchgetStore的能力的赋予,并终止该action在中间件的传播

最后需要清楚地点出, 所有的redux中间件都满足这种形式: ({ dispatch, getState }) => next => action => { }的形式, 并不是redux-thunk的特立独行, 这是因为这种方式才能对应的上redux对中间件的处理.

redux中处理中间件的源码阅读

根据刚才从redux-thunk的格式分析(所有)中, 可以推测redux对中间件们作了以下的处理:

  1. 让每一层中间件都具备访问和发出dipatch的能力, 由({ dispatch, getState }) 层注入
  2. 中间件之间形成链式联系, 由next参数实现, 这样可以在最终每个中间件就可以对下一层的中间件负责, 决定是否调用next(action)让后续中间件进行本次action的处理

根据这里要点的掌握就可以的redux的源码中寻找处理逻辑的相关根源了. 对开发者直接对中间件应用的认识毫无疑问是applyMiddleware函数, 应用形式为:

createStore(rootReducer, applyMiddleware(thunkMiddleware))

根据这个的感性认识, 翻看createStore的接口

export default function createStore(reducer, preloadedState, enhancer) {
  // 判断是否由preloadedState, 没有的话, enhancer获得preloadedState的值
  // 将createStore传给enhancer, 这里可以直观认为是applymiddleware函数工厂生成的函数
    return enhancer(createStore)(reducer, preloadedState)
}

可以看出

enhancer(createStore)

返回一个增强性的createStore(装饰器模式), 然后进行实际调用, 这里的增强对applymiddleware来说就是修改dispatch函数.
查看调用applyMiddleware(...)的返回的接口签名:

export default function applyMiddleware(...middlewares) {
  return createStore => (...args) => {
    const store = createStore(...args)

可以(...args)看出, applyMiddleware没有对createStore的参数手脚, 可以推出后续的API的修改

({ dispatch, getState })相关: 实现对每个中间件对store的访问能力和提出dispatch的能力

const middlewareAPI = {
  getState: store.getState,
  dispatch: (...args) => dispatch(...args)
}
chain = middlewares.map(middleware => middleware(middlewareAPI))

中间结果是chain, 此时满足所有中间件被拆解成: next => action => { }

next相关: 实现所有中间件之间的链式关联

dispatch = compose(...chain)(store.dispatch)

这段代码有点绕, 先结论: store.dispatch是最后一层中间件, 如果前面的中间都没有处理本茨action的想法, 那么由原生的dispatch进行处理

  • compose(a, b, c), 最终返会 () => c(....), 意味着越靠前的组件(这里a), 越先执行

函数工具compose的出现意味着中间件的使用形式是从右到左的, 所以applyMiddleware()的时候注意中间件的传入顺序.
这里值得推翘的是即使在compose(...chain)完中间件的形式还是next => action => { }, store.dispatch才是真正的导火线, 让所有中间件成为完全态 action => { }

Warning ⚠️

  • 全程涉及源码, 有阅读源码不适者迅速撤离

前言

redux-thunk是redux的一个中间件, 实现对dispatch操作的异步实现, 然而其原码却极其精简:

  • 仓库src/目录里面只有一个index.js文件
  • index.js文件里面只有14行(v2.2.0)
  • 没有任何其他的运行时依赖

揭开redux-thunk的面纱

因为只有十四行, 可以直接贴上来:

function createThunkMiddleware(extraArgument) {
  return ({ dispatch, getState }) => next => action => {
    if (typeof action === 'function') {
      return action(dispatch, getState, extraArgument);
    }

    return next(action);
  };
}

const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;

export default thunk;

细数一下有3层函数注入, 生成最后实际接收action的函数. 这里可以认为:

  • 前三次的每一次都利用闭包性质, 让最后的实体函数执行的时候能都访问前面的参数, 实现功能注入

先看最内层匿名函数的逻辑:

    if (typeof action === 'function') {
      return action(dispatch, getState, extraArgument);
    }
    return next()

因为如果action属于函数类型, 那么认为该函数会进行异步操作, 进行异步操作意味着不会马上执行, 而每一次action都需要实际的dispatch操作, 所以将dispatchgetState作为参数的形式注入异步action对象, 让该函数获得访问store的能力和提出dispatch的能力, 希望其(该action函数)在将来执行实际的dispatch操作.

因为redux-thunk是作为redux的中间件的实体存在, 所以辅以redux源码如何应用中间的逻辑就能帮助我们完全弄懂以上的各个=>的所有意思, 这里可以先根据每一次的参数提前点出相关认识:

  • extraAugument, 对异步action函数注入额外的参数, 相当于一个全局(所有action函数的范畴)变量. 默认的最终redux-thunk模块对外暴露是抛弃该参数的: const thunk = createThunkMiddleware();.
  • ({ dispatch, getState }), 注入访问store和发起dispath的能力
  • next, 与其他中间件获得链式关联
  • action, 实际action实体, redux-thunk的任务就是对每一次传到本层的action进行校验, 判断其是否为函数, 如果符合, 则对该action进行dispatchgetStore的能力的赋予,并终止该action在中间件的传播

最后需要清楚地点出, 所有的redux中间件都满足这种形式: ({ dispatch, getState }) => next => action => { }的形式, 并不是redux-thunk的特立独行, 这是因为这种方式才能对应的上redux对中间件的处理.

redux中处理中间件的源码阅读

根据刚才从redux-thunk的格式分析(所有)中, 可以推测redux对中间件们作了以下的处理:

  1. 让每一层中间件都具备访问和发出dipatch的能力, 由({ dispatch, getState }) 层注入
  2. 中间件之间形成链式联系, 由next参数实现, 这样可以在最终每个中间件就可以对下一层的中间件负责, 决定是否调用next(action)让后续中间件进行本次action的处理

根据这里要点的掌握就可以的redux的源码中寻找处理逻辑的相关根源了. 对开发者直接对中间件应用的认识毫无疑问是applyMiddleware函数, 应用形式为:

createStore(rootReducer, applyMiddleware(thunkMiddleware))

根据这个的感性认识, 翻看createStore的接口

export default function createStore(reducer, preloadedState, enhancer) {
  // 判断是否由preloadedState, 没有的话, enhancer获得preloadedState的值
  // 将createStore传给enhancer, 这里可以直观认为是applymiddleware函数工厂生成的函数
    return enhancer(createStore)(reducer, preloadedState)
}

可以看出

enhancer(createStore)

返回一个增强性的createStore(装饰器模式), 然后进行实际调用, 这里的增强对applymiddleware来说就是修改dispatch函数.
查看调用applyMiddleware(...)的返回的接口签名:

export default function applyMiddleware(...middlewares) {
  return createStore => (...args) => {
    const store = createStore(...args)

可以(...args)看出, applyMiddleware没有对createStore的参数手脚, 可以推出后续的API的修改

({ dispatch, getState })相关: 实现对每个中间件对store的访问能力和提出dispatch的能力

const middlewareAPI = {
  getState: store.getState,
  dispatch: (...args) => dispatch(...args)
}
chain = middlewares.map(middleware => middleware(middlewareAPI))

中间结果是chain, 此时满足所有中间件被拆解成: next => action => { }

next相关: 实现所有中间件之间的链式关联

dispatch = compose(...chain)(store.dispatch)

这段代码有点绕, 先结论: store.dispatch是最后一层中间件, 如果前面的中间都没有处理本茨action的想法, 那么由原生的dispatch进行处理

  • compose(a, b, c), 最终返会 () => c(....), 意味着越靠前的组件(这里a), 越先执行

函数工具compose的出现意味着中间件的使用形式是从右到左的, 所以applyMiddleware()的时候注意中间件的传入顺序.
这里值得推翘的是即使在compose(...chain)完中间件的形式还是next => action => { }, store.dispatch才是真正的导火线, 让所有中间件成为完全态 action => { }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant