不一样的redux源码解读

1、本文不触及redux的运用要领,因而可以更适合运用过 redux 的同砚浏览

2、当前redux版本为4.0.1

3、
更多系列文章请看

Redux作为大型React运用状况治理最经常运用的东西。虽然在日常平凡的工作中很屡次的用到了它,然则一向没有对其道理举行研究。近来看了一下源码,下面是我本身的一些简朴熟悉,若有疑问迎接交换。

1、createStore

连系运用场景我们起首来看一下createStore要领。

// 这是我们寻常运用时建立store
const store = createStore(reducers, state, enhance);

以下源码为去除非常校验后的源码,


export default function createStore(reducer, preloadedState, enhancer) {

// 假如有传入正当的enhance,则经由历程enhancer再挪用一次createStore
 if (typeof enhancer !== 'undefined') {
   if (typeof enhancer !== 'function') {
     throw new Error('Expected the enhancer to be a function.')
   }
   return enhancer(createStore)(reducer, preloadedState) // 这里触及到中间件,背面引见applyMiddleware时在详细引见
 }

 let currentReducer = reducer     //把 reducer 赋值给 currentReducer
 let currentState = preloadedState   //把 preloadedState 赋值给 currentState
 let currentListeners = []     //初始化监听函数列表
 let nextListeners = currentListeners   //监听列表的一个援用
 let isDispatching = false  //是不是正在dispatch

 function ensureCanMutateNextListeners() {}

 function getState() {}

 function subscribe(listener) {}
 
 function dispatch(action) {}

 function replaceReducer(nextReducer) {}
 
// 在 creatorStore 内部没有看到此要领的挪用,就不讲了
 function observable() {}
 //初始化 store 里的 state tree
 dispatch({ type: ActionTypes.INIT })

 return {
   dispatch,
   subscribe,
   getState,
   replaceReducer,
   [$$observable]: observable
 }
}

我们可以看到creatorStore要领除了返回我们经常运用的要领外,还做了一次初始化历程dispatch({ type: ActionTypes.INIT });那末dispatch干了什么事变呢?

/**
  * dispath action。这是触发 state 变化的唯一门路。
  * @param {Object} 一个一般(plain)的对象,对象当中必须有 type 属性
  * @returns {Object} 返回 dispatch 的 action
  */
 function dispatch(action) {
 // 推断 dispahch 正在运转,Reducer在处置惩罚的时刻又要实行 dispatch
   if (isDispatching) {
     throw new Error('Reducers may not dispatch actions.')
   }

   try {
     //标记 dispatch 正在运转
     isDispatching = true
     //实行当前 Reducer 函数返回新的 state
     currentState = currentReducer(currentState, action)
   } finally {
     isDispatching = false
   }
   const listeners = (currentListeners = nextListeners)
   //遍历一切的监听函数
   for (let i = 0; i < listeners.length; i++) {
     const listener = listeners[i]
     listener() // 实行每一个监听函数
   }
   return action
 }

这里dispatch重要做了二件事变

  • 经由历程reducer更新state
  • 实行一切的监听函数,关照状况的变动

那末reducer是怎样转变state的呢?这就触及到下面的combineReducers

2、combineReducers

回到上面建立store的参数reducers,

// 两个reducer
const todos = (state = INIT.todos, action) => {
  // ....
};
const filterStatus = (state = INIT.filterStatus, action) => {
  // ...
};

const reducers = combineReducers({
  todos,
  filterStatus
});
// 这是我们寻常运用时建立store
const store = createStore(reducers, state, enhance);

下面我们来看combineReducers做了什么

export default function combineReducers(reducers) {
   // 第一次挑选,参数reducers为Object
  // 挑选掉reducers中不是function的键值对
  const reducerKeys = Object.keys(reducers)
  const finalReducers = {}
  for (let i = 0; i < reducerKeys.length; i++) {
    const key = reducerKeys[i]
    if (typeof reducers[key] === 'function') {
      finalReducers[key] = reducers[key]
    }
  }
  const finalReducerKeys = Object.keys(finalReducers)
  
  // 二次挑选,推断reducer中传入的值是不是正当(!== undefined)
  // 猎取挑选完以后的一切key
  let shapeAssertionError
  try {
    assertReducerShape(finalReducers)
  } catch (e) {
    shapeAssertionError = e
  }

  return function combination(state = {}, action) {
    let hasChanged = false
    const nextState = {}
    // 遍历一切的key和reducer,分别将reducer对应的key所代表的state,代入到reducer中举行函数挪用
    for (let i = 0; i < finalReducerKeys.length; i++) {
      const key = finalReducerKeys[i]
      const reducer = finalReducers[key]
      // 这里就是reducer function的称号和要和state同名的缘由,传说中的黑魔法
      const previousStateForKey = state[key]
      const nextStateForKey = reducer(previousStateForKey, action)
      if (typeof nextStateForKey === 'undefined') {
        const errorMessage = getUndefinedStateErrorMessage(key, action)
        throw new Error(errorMessage)
      }
      // 将reducer返回的值填入nextState
      nextState[key] = nextStateForKey
      hasChanged = hasChanged || nextStateForKey !== previousStateForKey
    }
    // 发作转变了返回新的nextState,不然返回本来的state
    return hasChanged ? nextState : state
  }
}
  • 这里 reducer(previousStateForKey, action)实行的就是我们上面定义的todosfilterStatus要领。经由历程这二个reducer转变state值。
  • 之前我一向很新鲜我们的reducer里的state是怎样做到取当前reducer对应的数据。看到const previousStateForKey = state[key]这里我就邃晓了。
  • 这里另有一个疑问点就是combineReducers的嵌套,最最先也我不邃晓,看了源码才晓得combineReducers()=> combination(state = {}, action),这里combineReducers返回的combination也是吸收(state = {}, action)也就是一个reducer所以可以一般嵌套。

看到这初始化流程已走完了。这个历程我们熟悉了dispatchcombineReducers;接下来我们来看一下我们本身要怎样更新数据。

用户更新数据时,是经由历程createStore后暴露出来的dispatch要领来触发的。dispatch 要领,是 store 对象供应的变动 currentState 这个闭包变量的唯一发起门路(注重这里是唯一发起门路,不是唯一门路,由于经由历程getState猎取到的是state的援用,所以是可以直接修正的。然则如许就不能更新视图了)。
一般情况下我们只须要像下面如许

//action creator
var addTodo = function(text){
    return {
        type: 'add_todo',
        text: text
    };
};
function TodoReducer(state = [], action){
    switch (action.type) {
        case 'add_todo':
            return state.concat(action.text);
        default:
            return state;
    }
};

// 经由历程 store.dispatch(action) 来到达修正 state 的目标
// 注重: 在redux里,唯一可以修正state的要领,就是经由历程 store.dispatch(action)
store.dispatch({type: 'add_todo', text: '念书'});// 或许下面如许
// store.dispatch(addTodo('念书'));

也就是说dispatch吸收一个包括type的对象。框架为我们供应了一个建立Action的要领bindActionCreators

3、bindActionCreators

下面来看下源码

// 中心代码,并经由历程apply将this绑定起来
function bindActionCreator(actionCreator, dispatch) {
  return function() {
    return dispatch(actionCreator.apply(this, arguments))
  }
}

export default function bindActionCreators(actionCreators, dispatch) {
// 假如actionCreators是一个函数,则申明只要一个actionCreator,就直接挪用bindActionCreator
  if (typeof actionCreators === 'function') {
    return bindActionCreator(actionCreators, dispatch)
  }
  // 遍历对象,然后对每一个遍历项的 actionCreator 天生函数,将函数根据本来的 key 值放到一个对象中,末了返回这个对象
  const keys = Object.keys(actionCreators)
  const boundActionCreators = {}
  for (let i = 0; i < keys.length; i++) {
    const key = keys[i]
    const actionCreator = actionCreators[key]
    if (typeof actionCreator === 'function') {
      boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
    }
  }
  return boundActionCreators
}

bindActionCreators的作用就是运用dispatch action creator包裹起来,如许我们就可以直接挪用他们了。这个在寻常开辟中不经常运用。

4、applyMiddleware

末了我们转头来看一下之前调到的中间件,

import thunkMiddleware from 'redux-thunk';
// 两个reducer
const todos = (state = INIT.todos, action) => {
  // ....
};
const filterStatus = (state = INIT.filterStatus, action) => {
  // ...
};

const reducers = combineReducers({
  todos,
  filterStatus
});
// 这是我们寻常运用时建立store
const store = createStore(reducers, state, applyMiddleware(thunkMiddleware));

为了下文好明白这个放一下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;

可以看出thunk返回了一个吸收({ dispatch, getState })为参数的函数
下面我们来看一下applyMiddleware源码剖析

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)
    }
    // 每一个 middleware 都以 middlewareAPI 作为参数举行注入,返回一个新的链。此时的返回值相当于挪用 thunkMiddleware 返回的函数: (next) => (action) => {} ,吸收一个next作为其参数
    const chain = middlewares.map(middleware => middleware(middlewareAPI))
    // 并将链代入进 compose 构成一个函数的挪用链
    // compose(...chain) 返回形如(...args) => f(g(h(...args))),f/g/h都是chain中的函数对象。
    // 在现在只要 thunkMiddleware 作为 middlewares 参数的情况下,将返回 (next) => (action) => {}
    // 以后以 store.dispatch 作为参数举行注入注重这里这里的store.dispatch是没有被修正的dispatch他被传给了next;
    dispatch = compose(...chain)(store.dispatch)

    return {
      ...store,
      dispatch
    }
  }
}

// 定义一个代码组合的要领
// 传入一些function作为参数,返回其链式挪用的形状。比方,
// compose(f, g, h) 终究返回 (...args) => f(g(h(...args)))
export default function compose(...funcs) {
  if (funcs.length === 0) {
    return arg => arg
  } else {
    const last = funcs[funcs.length - 1]
    const rest = funcs.slice(0, -1)
    return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args))
  }
}

我第一眼看去一脸闷逼,咋这么庞杂。然则静下心来看也就是一个三级柯里化的函数,我们从头来剖析一下这个历程

// createStore.js
if (typeof enhancer !== 'undefined') {
  if (typeof enhancer !== 'function') {
    throw new Error('Expected the enhancer to be a function.')
  }
  return enhancer(createStore)(reducer, preloadedState)
}

也就是说,会变成如许

applyMiddleware(thunkMiddleware)(createStore)(reducer, preloadedState)
  • applyMiddleware(thunkMiddleware)

applyMiddleware吸收thunkMiddleware作为参数,返回形如(createStore) => (...args) => {}的函数。

  • applyMiddleware(thunkMiddleware)(createStore)

createStore 作为参数,挪用上一步返回的函数(...args) => {}

  • applyMiddleware(thunkMiddleware)(createStore)(reducer, preloadedState)

(reducer, preloadedState)为参数举行挪用。 在这个函数内部,thunkMiddleware被挪用,其作用是监测typefunctionaction

因而,假如dispatch的action返回的是一个function,则证实是中间件,则将(dispatch, getState)作为参数代入个中,举行action 内部下一步的操纵。不然的话,以为只是一个一般的action,将经由历程next(也就是dispatch)进一步分发

也就是说,applyMiddleware(thunkMiddleware)作为enhance,终究起了如许的作用:

dispatch挪用的action举行检查,假如action在第一次挪用以后返回的是function,则将(dispatch, getState)作为参数注入到action返回的要领中,不然就一般对action举行分发,如许一来我们的中间件就完成喽~

因而,当action内部须要猎取state,或许须要举行异步操纵,在操纵完成以后举行事宜挪用分发的话,我们就可以让action 返回一个以(dispatch, getState)为参数的function而不是一般的Object,enhance就会对其举行检测以便准确的处置惩罚

到此redux源码的重要部份讲完了,若有感兴趣的同砚可以去看一下我没讲到的一些东西转送门redux;

    原文作者:Alan
    原文地址: https://segmentfault.com/a/1190000018452378
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞