mengtuifrontend / Blog

芦叶满汀洲,寒沙带浅流。二十年重过南楼。柳下系船犹未稳,能几日,又中秋。 黄鹤断矶头,故人今在否?旧江山浑是新愁。欲买桂花同载酒,终不似,少年游。
18 stars 5 forks source link

redux原理 #9

Open shenxuxiang opened 5 years ago

shenxuxiang commented 5 years ago

redux 原理

单一数据源,即项目的数据源只有一个,整个应用的数据都保存在一个js对象中,这样我们的页面之间或者不同组件之间可以进行数据的共享。redux的核心就是store,redux中的 createStore 方法会根据 reducer 返回store,而且 createStore 暴露出一些方法方便我们使用

createStore

直接上源码,已经在源码中做了注释

  function createStore(reducer, preloadedState, enhancer) {
    var _ref2;
    // 判断,如果只有两个参数,且第二个参数还是一个function
    if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
      enhancer = preloadedState;
      preloadedState = undefined;
    }
    // 如果三个参数都传了,且第三个参数还是一个function
    if (typeof enhancer !== 'undefined') {
      if (typeof enhancer !== 'function') {
        throw new Error('Expected the enhancer to be a function.');
      }

      return enhancer(createStore)(reducer, preloadedState);
    }

    if (typeof reducer !== 'function') {
      throw new Error('Expected the reducer to be a function.');
    }
    // ... 省略

    // subscribe 注册一个监听,注意 listener 必须是一个function
    function subscribe(listener) {
      if (typeof listener !== 'function') {
        throw new Error('Expected listener to be a function.');
      }
      var isSubscribed = true;

      ensureCanMutateNextListeners();
      // 将 监听方法添加到 nextListeners 队列中
      nextListeners.push(listener);
      // 返回了一个 unsubscribe 的方法,如果我们要取消订阅,那么就可以调用这个 unsubscribe
      return function unsubscribe() {
        if (!isSubscribed) {
          return;
        }

        isSubscribed = false;

        ensureCanMutateNextListeners();
        var index = nextListeners.indexOf(listener);
        nextListeners.splice(index, 1);
      };
    }

    function dispatch(action) {
      // ... 省略
      try {
        isDispatching = true;
        currentState = currentReducer(currentState, action);
      } finally {
        isDispatching = false;
      }

      // 执行注册的每一个监听事件
      var listeners = currentListeners = nextListeners;
      for (var i = 0; i < listeners.length; i++) {
        var listener = listeners[i];
        listener();
      }

      return action;
    }

    // createStore 在初始化的时候就会执行一次 dispatch ,
    // When a store is created, an "INIT" action is dispatched so that every
    // reducer returns their initial state. This effectively populates
    // the initial state tree.
    dispatch({ type: ActionTypes.INIT });

    // ... 省略

其实createStore 在初始化的时候就会执行一次 dispatch,这也是为什么我们可以不传 preloadedState 这个参数,createStore还可以构建出state的结构。 但是这需要一个前提条件:在定义reduer函数的是的时候,在没有匹配到action后,仍然需要返回一个state的默认值(不能位undefined)。如果我们没有返回默认状态的系统会给我们报一个错,让我们必须返回默认状态的state, 看下面实例

  info = (state = {}, action) => {
    const { type } = action;
    switch (type) {
      case: 'increase':
        return { ...state, count: state.count + 1 };
        break;
      case: 'decrease':
        return { ...state, count: state.count - 1 };
        break;
      // 这里必须返回默认状态 state
      default:
        return state;
        break;
    }
  }

redux 中间件原理

我们来看看applyMiddleware.js的源码


function applyMiddleware() {
  for (var _len = arguments.length, middlewares = Array(_len), _key = 0; _key < _len; _key++) {
    middlewares[_key] = arguments[_key];
  }
  return function (createStore) {
    return function (reducer, preloadedState, enhancer) {
      var store = createStore(reducer, preloadedState, enhancer);
      var _dispatch = store.dispatch;
      var chain = [];

      var middlewareAPI = {
        getState: store.getState,
        // 这么写是有特殊用意的,因为在最后会将合成的dispatch重新赋值给_dispatch
        // 这样中间件接受的middlewareAPI中的dispatch方法返回的就是最新合成dispatch
        dispatch: function dispatch(action) {
          return _dispatch(action);
        }
      };
      // 每个中间件都是这种模型 ({dispatch, getState}) => (next) => action
      chain = middlewares.map(function (middleware) {
        return middleware(middlewareAPI);
      });
      // 注意这里,其实是将_dispatch重新赋值了,
      // 所以中间件中只要是调用的middlewareAPI中的dispatch其实就是新合成的_dispatch
      _dispatch = _compose2['default'].apply(undefined, chain)(store.dispatch);

      return _extends({}, store, {
        dispatch: _dispatch
      });
    };
  };
}

我们注意看上面的代码中middlewareAPI里面的 dispatch 方法,该方法又返回了一个 _dispatch 函数,也就是说当中间中使用middlewareAPI.dispatch时候,最后调用的还是 _dispatch 函数 。这就像是一个闭包一样。

接下来在看看compose.js的源码

function compose() {
  // ...
  if (funcs.length === 1) {
    return funcs[0];
  }
  return funcs.reduce(function (a, b) {
    return function () {
      return a(b.apply(undefined, arguments));
    };
  });
}

其实compose函数中最重要最难懂的地方就是 reduce 高阶函数了。可以点这里学习一下。每个中间件都是这种模型 ({dispatch, getState}) => (next) => action ,在applyMiddleware方法中每个中间件都会接受 middlewareAPI 作为第一个参数返回一个新的函数(接下来我用one,two,three这三个函数来模拟返回的函数),并作为变量 chain 集合中的一个元素,最后传给compose函数。

  // 注意我这里只是模拟的中间件接受middlewareAPI后返回的函数部分
  function one(next) {
    // 函数foo 在这里只是我作为一个例子,用来说明的。实际的中间件中并不一定这样
    return function foo(action) {
      console.log('one:' + action);
      return next(action);
    };
  };
  function two(next) {
    return function bar(action) {
      console.log('two:' + action);
      return next(action);
    };
  };
  function three(next) {
    return function baz(action) {
      console.log('three:' + action);
      return next(action);
    };
  };
  // 模拟redux中的compose函数
  function compose(...middleware) {
    return middleware.reduce((a,b) => {
      return function() {
        return a(b.apply(undefined, arguments));
      };
    });
  };
  // 模拟 store.dispatch 方法
  function dispatch(action) {
    console.log('dispatch:' + action);
  };
  // 看下面的解释
  var disp = compose(one, two, three)(dispatch);
  disp('shen') // one:shen; two:shen; three:shen; dispatch: shen;

最后disp其实就是函数 foo ,且函数接受 action 作为参数,中间件执行的原理就是这样,action会从在中间件之间传递,一直传达到store.dispatch,然后触发action。

我们再来看看redux-thunk

function createThunkMiddleware(extraArgument) {
  return ({ dispatch, getState }) => next => action => {
    // 判断如果action是一个方法那么就直接执行
    if (typeof action === 'function') {
      return action(dispatch, getState, extraArgument);
    }
    // 否则就执行下一个中间件,并将action传给这个中间件,这个时候action就是一个对象{type, data,…}
    return next(action);
  };
}
const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;
export default thunk;

我们再看看,如何定义action行为

  // 同步
  export function addCount() {
    return {type: ADD_COUNT}
  }
  // 发送一个异步的请求
  export function addCountAsync() {
    // 上面的红色部分就是在执行下面的方法,参数就是中间件提供的 dispatch, getState, extraArgument
    return dispatch => {
      setTimeout( () => { dispatch(addCount()) },2000)
    }
  }

注意上面有个问题,就是action想要在中间件之间依次传递,那么必须是由中间件合成的dispatch触发的action才可以。否则就会立即出发这个action行为(注意:传递给store.dispatch函数的action必须是一个简单对象)。

combineReducers

我们先来看看它是怎么使用的

reducers = combineReducers({
  users: function getUsersReducer(){},
  userInfo: function getUserInfoReducer(){}
});

const store = createStore(reducers);

也就是说combineReducers可以将多个子reducers合并成一个reducer,现在我们来看看它的源码

const combineReducers = (reducers) => {
  const finalReducers = {};
  // 这里可以看出来参数 reducers 是一个对象
  const reducerKey = Object.keys(reducers);
  reducerKey.forEach((item) => {
    // 重新刷选出有用的reducer,reducer必须是一个function
    if (typeof reducers[item] !== 'function') throw new TypeError('reducer is must be a function');
    finalReducers[item] = reducers[item];
  });
  // 返回一个函数 其实combineReducers函数返回就是一个reducer,而且这个reducer返回一个新的state
  return (state = {}, action) => {
    // 创建一个新的state
    const nextState = {};
    // 标记新的nextState和原先的state是否相等
    let hasChanged = false;
    const finalReducersKey = Object.keys(finalReducers);
    finalReducersKey.forEach((item) => {
      // 遍历每一个子reducer

      // 获取当前的这个reducer
      const reducer = finalReducers[item];
      // 获取当前这个reducer对应的initState下的属性
      const prevStateForKey = state[item];
      // 重新获取这个initState下属性的值
      const nextStateForKey = reducer(prevStateForKey = {}, action);
      if (typeof nextStateForKey === 'undefined') throw new TypeError(
          'reducer is must a pure function and must return a result'
      );
      // 重新赋值
      nextState[item] = nextStateForKey;
      // 如果前后不相等,hasChanged就为true 否则就是false
      // 一旦 hasChanged 为true,那么就一直为true
      hasChanged = hasChanged || nextStateForKey !== prevStateForKey;
    });
    // 重新返回state
    return hasChanged ? nextState : state;
  };
}

bindActionCreators

var bindActionCreator = (action, dispatch) => {
    return (...args) => dispatch(action(...args));
};

var bindActionCreators = (actionCreators, dispatch) => {
  if (typeof actionCreators === 'function') {
    return bindActionCreator(actionCreators, dispatch);
  }
  // 判断action是不是一个undefined
  if (typeof actionCreators !== 'object' || actionCreators === null) {
    throw new Error('bindActionCreators expected an object or a function, instead received ' + (actionCreators === null ? 'null' : typeof actionCreators) + '. ' + 'Did you write "import ActionCreators from" instead of "import * as ActionCreators from"?');
  }

  var keys = Object.keys(actionCreators);
  var boundActionCreators = {};
  for (var i = 0; i < keys.length; i++) {
    var key = keys[i];
    var actionCreator = actionCreators[key];
    if (typeof actionCreator === 'function') {
      // key 其实就是 我们定义的action的方法名
      boundActionCreators[key] = bindActionCreator(actionCreator, dispatch);
    }
  }
  // 最后返回一个对象
  return boundActionCreators;
}