博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
redux源码分析
阅读量:5977 次
发布时间:2019-06-20

本文共 4894 字,大约阅读时间需要 16 分钟。

我觉得redux对于初学者并不是很友好,很多概念都不太好理解,会使用redux之后有必要看源码

createStore

redux的store存储了应用的状态树,要改变state只能通过dispatch()方法。

虽然文档中明确指出store只能有一个,但是我在工作的项目中store有可能不止一个,比如多页面应用。

参数

createStore有3个参数,分别是reducerpreloadedStateenhancer

  1. reducer。这里传入的reducer通常是通过combineReducers集成的reducer
  2. preloadedState。初始化store时候很有必要传入一个初始化的state,一来可以给应用页面一个初始值,二来可以让自己或者别人了解整个app的state结构。
  3. enhancer。通常就是一些redux的中间件,中间件的概念有点绕。

createStore做了参数校验和类型检测,除了reducer是必须传入之外,其余的两个参数都不是必须的。 传入的形式包含以下几种。

  • createStore(reducer)
  • createStore(reducer, enhancer)
  • createStore(reducer, preloadedState)
  • createStore(reducer, preloadedState, enhaner)

内部实现

createStore的内部包含了3个重要的变量和4个供外部调用的方法。

变量

  1. currentState。存储了整个store管理的状态树。
  2. currentListeners。redux其实是的一个实践。我们可以把currentListeners看做state变化后待执行的函数列表。
  3. isDispatching。是否正在进行dispatch
  4. nextListners。当进行subscribe操作时候,先把新的listener函数push到nextListners数组中,作为一个最新listener数组快照。

方法

  1. getState 外部访问store内部state的唯一方法。方法直接把currentState内部变量直接返回。
function getState() {  return currentState;}复制代码
  1. subscribe 用于增加一个listener到nextListners。每次进行dispatch方法时候,nextListenrs替换currentListeners,传入的listener会被执行。通常来说我们会把UI的渲染,作为listener传入到subscribe中,每当state变化,redux会通知UI进行render。 listener不会看到每一个所有state的变化,因为state可能会在dispatch中变化多次后,listener才被执行。 subscribe方法返回一个函数闭包,作为取消订阅。
function subscribe() {  let isSubscribed = true  ensureCanMutateNextListeners()  nextListeners.push(listener)  return function unsubscribe() {    if (!isSubscribed) {      return    }    isSubscribed = false    ensureCanMutateNextListeners()    const index = nextListeners.indexOf(listener)    nextListeners.splice(index, 1)  }}复制代码
  1. dispatch 外部唯一能改变state的方法。调用reducer获得最新的state(reducer就是构建state树的函数)并执行每一个listener方法。 dispatch只能传递plain objectaction(普通的JavaScript对象),如果要传递一个,需要使用中间件,redux-thunk。
function dispatch(action) {    try {      isDispatching = true      currentState = currentReducer(currentState, action)    } finally {      isDispatching = false    }    // 把nextListeners替换currentListeners    const listeners = (currentListeners = nextListeners)    for (let i = 0; i < listeners.length; i++) {      const listener = listeners[i]      listener()    }    return action  }复制代码
  1. replaceReducer 更换reducer方法。很少时候会使用这个方法。

combineReducers

把多个reducer合到一个函数中。combineReducers会调用每一个reducer,并把每一个reducer返回的state合并到state树中。

参数

类型为一个object。每个键对应的必须为一个reducer函数。

const params = {  key1: reducerfunc1,  key2: reducerfunc2,};复制代码

假设reducerfunc1和reducerfunc2返回的state形式分别为

const state1 = {  v1: '',  v2: 0,};const state2 = [];复制代码

那么我们的state树就是这样形式,通过getState获取到的state对象如下。

const state = {  key1: {    v1: '',    v2: 0,  },  key2: [],}复制代码

内部实现

闭包之前的代码都是reducer检测

  • 过滤那些不是function的reducer(reducers的每一个key必须对应的reducer方法)
  • 查看每一个reducer方法是否有initState,default type是否返回state

代码很好理解。

export default function combineReducers(reducers) {  return function combination(state = {}, action) {    let hasChanged = false    const nextState = {}    for (let i = 0; i < finalReducerKeys.length; i++) {      const key = finalReducerKeys[i]      const reducer = finalReducers[key]      // reducer调用前的state      const previousStateForKey = state[key]      // reducer调用后的state      const nextStateForKey = reducer(previousStateForKey, action)      // 更新state树快照      nextState[key] = nextStateForKey      // state是否有变动的flag。若reducer执行导致state变动,返回一个全新的state对象,所以可以直接比较对象来判断是否有改变。      hasChanged = hasChanged || nextStateForKey !== previousStateForKey    }    return hasChanged ? nextState : state  }}复制代码

applyMiddleware

中间件,我觉得是整个redux中比较有意思,并且稍微有点难理解的部分。

可以在官网查看的文档。 可以把中间件理解为在dispatch方法的前后做一些操作。也可以类比为java的切面。

export default function applyMiddleware(...middlewares) {  return createStore => (...args) => {    const store = createStore(...args)    let dispatch = () => {      throw new Error(        `Dispatching while constructing your middleware is not allowed. ` +          `Other middleware would not be applied to this dispatch.`      )    }    const middlewareAPI = {      getState: store.getState,      dispatch: (...args) => dispatch(...args)    }    const chain = middlewares.map(middleware => middleware(middlewareAPI))    dispatch = compose(...chain)(store.dispatch)    return {      ...store,      dispatch    }  }}复制代码

其实这里可以理解为俄罗斯套娃,原始的store.dispatch就是套娃的最里面那个,所有的中间件按照数组的顺序,一个把一个套住。而这个套娃的过程由compose完成。

要理解这部分,我们把中间件的源码也拿过来看看。 redux-thunk让我们的action为函数。注意!! 原本的action只能是一个plain object。 以下是redux-thunk中间件的写法,必须传入store,dispatch,action后才会执行真正的函数实体。 这里用的是柯里化,只有参数够了,才会去执行函数体。

// 稍微改动过的redux-thunkconst thunk = ({ dispatch, getState }) => next => action => {  if (typeof action === 'function') {    return action(dispatch, getState);  }  return next(action);};export default thunk;复制代码

我这里一开始看不懂,为什么一开始dispatch赋值为一个空函数? 其实compose完成之后会返回一个新的dispatch,这个dispatch会替换掉那个空函数

const chain = middlewares.map(middleware => middleware(middlewareAPI))dispatch = compose(...chain)(store.dispatch)复制代码

最后返回store变量。

return {  ...store,  dispatch}复制代码

参考

转载于:https://juejin.im/post/5c05254ff265da61327f1261

你可能感兴趣的文章
[2018.12.9]BZOJ2153 设计铁路
查看>>
【转】Ubuntu VI基本用法
查看>>
HTML5之FileReader的使用
查看>>
用户输入和while循环
查看>>
1013: C语言程序设计教程(第三版)课后习题6.3
查看>>
接口测试工具Postman(转)
查看>>
苹果开发准备工作
查看>>
java.util.regex包下的Pattern和Matcher详解(正则匹配)
查看>>
在Java中定义常量
查看>>
php jsonp实例 mip无限滚动组件接口注意事项
查看>>
单元测试
查看>>
springboot整合elasticJob实战(纯代码开发三种任务类型用法)以及分片系统,事件追踪详解...
查看>>
Telegraf+InfluxDB+Grafana快速搭建实时监控系统 监控postgresql
查看>>
find 命令一个命令多参数如何使用,????,perm
查看>>
【luogu 3811】【模板】乘法逆元
查看>>
第8周学习进度情况
查看>>
使用Storm实现累加求和操作
查看>>
禁用树莓派屏幕休眠
查看>>
SQL数据库学习之路(五)
查看>>
人工智能--遗传算法(旅行商问题)
查看>>