1. 概述

本文带着大家回顾下redux用法,reduxAPIcreateStorereducerdispatch等,了解用法后,一起探索原理,难点是redux中间件的原理局部。
留神:本文只探索redux,不关注react-reduxreact-redux的应用和原理前面会有专文解说,redux的用途不限于react,任何你想要订阅事件的中央,都能够独自应用redux

2. 设计思维

  • redux是将整个利用状态存储到到一个中央,称为store,繁多数据源
  • 外面保留一棵状态树state
  • 派发dispatch行为actionstore,而不是间接更改state,只读
  • 用户通过订阅(subscribe) store,当有dispatch行为时,会告诉订阅者更新

    下图是我画的redux流程,其实从图中看redux逻辑也简略,你只能用dispatch通过派发action更新stateaction派发后,外部有两个事件解决,一是要调用reducer更新state,二是触发之前订阅过的事件执行,就完了。

3. redux根本应用

本文重点讲原理,适宜有redux根底的同学,用法简略回顾。

3.1 createStore

用来创立store

import { createStore } from 'redux';const defaultState = 0;const reducer = (state = defaultState, action) => {  switch (action.type) {    case 'ADD':      return state + action.payload;    default:       return state;  }};const store = createStore(reducer);

3.2 getState

获取以后state数据

const state = store.getState();

3.3 action

action 是一个对象。其中的type属性是必须的,示意 action 的名称。其余属性能够自在设置。

const action = {  type: 'ADD_TODO',  payload: 'Learn Redux'};

3.3 dispatch

派发action的惟一路径

import { createStore } from 'redux';const store = createStore(fn);store.dispatch({  type: 'ADD_TODO',  payload: 'Learn Redux'});

3.4 reducer

承受旧的stateaction作为参数,返回新的state

const defaultState = 0;const reducer = (state = defaultState, action) => {  switch (action.type) {    case 'ADD':      return state + action.payload;    default:       return state;  }};

3.5 subscribe

设置订阅,dispatch时会主动执行所有订阅的事件

import { createStore } from 'redux';const store = createStore(reducer);let unsubscribe = store.subscribe(() => {    console.log("state更新了")});// subscribe的返回值能够勾销订阅// unsubscribe();

3.6 combineReducers

把多个reducer合并成一个,因为在理论我的项目中,不同的业务数据不可能全副写在一个reducer里,保护老本高,通常把reducer按业务离开,最初用combineReducers合并后,再传给createStore

import { combineReducers } from 'redux';const reducer = combineReducers({  chatLog,  statusMessage,  userName})export default reducer;

4. 原理

我看的redux版本是4.1.2的源码

4.1 createStore源码剖析

/** * 创立store * @param {*} reducer  * @param {*} preloadedState  * @param {*} enhancer  * @returns  */unction createStore(reducer, preloadedState, enhancer) {  if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {    enhancer = preloadedState;    preloadedState = undefined;  }  if (typeof enhancer !== 'undefined') {    if (typeof enhancer !== 'function') {      throw new Error(process.env.NODE_ENV === "production" ? formatProdErrorMessage(1) : "Expected the enhancer to be a function. Instead, received: '" + kindOf(enhancer) + "'");    }    return enhancer(createStore)(reducer, preloadedState);  }  // 上局部redux中间件再讲,咱们先只关注createStore第一个参数reducer  // reducer必须是个函数  if (typeof reducer !== 'function') {    throw new Error("Expected the root reducer to be a function.");  }  // 定义外部变量  var currentReducer = reducer;  // 以后state数据,初始值是preloadedState  var currentState = preloadedState;  // 保留所有订阅函数  var currentListeners = [];  // 保留的订阅函数快照  var nextListeners = currentListeners;  // 是否dispatch正在执行  var isDispatching = false;  // 为subscribe执行时,提供备份,具体在subscribe函数细看  function ensureCanMutateNextListeners() {    if (nextListeners === currentListeners) {      nextListeners = currentListeners.slice();    }  }    /**   * getState源码,获取以后最新state   * @returns 返回state   */  function getState() {    return currentState;  }    /**   * subscribe源码,用来收集订阅   * @param {*} listener 订阅办法   * @returns 返回勾销此订阅的办法   */  function subscribe(listener) {    // listener 必须是函数    if (typeof listener !== 'function') {      throw new Error("Expected the listener to be a function");    }    // 闭包变量,用来标识勾销订阅办法只能执行一次    var isSubscribed = true;    // 为每次订阅提供快照备份nextListeners,    // 避免在dispatch里遍历currentListeners过程中,触发了订阅/勾销订阅性能。    // 若间接更新currentListeners将造成以后循环体逻辑凌乱,    // 因而所有订阅/勾销订阅的listeners都是在nextListeners中存储的,并不会影响以后的dispatch(action)    ensureCanMutateNextListeners();    nextListeners.push(listener);    return function unsubscribe() {      // 曾经勾销了,间接跳出      if (!isSubscribed) {        return;      }      // 勾销胜利,没有此订阅了      isSubscribed = false;      ensureCanMutateNextListeners();      var index = nextListeners.indexOf(listener);      nextListeners.splice(index, 1);      currentListeners = null;    };  }  /**   * dispatch源码   * @param {*} action 承受action对象   * @returns 返回action   */  function dispatch(action) {    // action是个纯对象    if (!isPlainObject(action)) {      throw new Error("Actions must be plain objects.");    }    // action对象必须有type属性    if (typeof action.type === 'undefined') {      throw new Error('Actions may not have an undefined "type" property. You may have misspelled an action type string constant.');    }    // 执行reducer时,不容许有其余dispatch操作    if (isDispatching) {      throw new Error('Reducers may not dispatch actions.');    }    try {      // 开始执行reducer      isDispatching = true;      // reducer承受以后state和action对象,返回新的state      currentState = currentReducer(currentState, action);    } finally {      // reducer执行实现,state更新结束      isDispatching = false;    }    // 而后再执行所有subscribe订阅过的listeners    var listeners = currentListeners = nextListeners;    // 遍历执行    for (var i = 0; i < listeners.length; i++) {      var listener = listeners[i];      listener();    }    // 返回action    return action;  }  // 首先派发初始化action,目标是给state赋初始值  // 因为reducer函数,在初始化时,能够传个state初始值,所以在外部调用下,reducer的action没有匹配时,返回默认值  // 留神:如果createStore传了第二个参数是个对象,也代表初始state,见代码:var currentState = preloadedState;  // 此时会笼罩reducer的默认值,因为在调用currentReducer(currentState, action)形式传的currentState就是preloadedState,所以reducer函数的默认值有效了  dispatch({    type: ActionTypes.INIT  });  return {    dispatch: dispatch,    subscribe: subscribe,    getState: getState  }}

4.2 combineReducers源码解析

对照combineReducers应用办法,再看原理
import { combineReducers } from 'redux';function chatLog(state, action) {  switch (action.type) {    case 'ADD':      return state + action.payload;    default:       return state;  }}function statusMessage(state, action) {  // 同chatLog}function userName(state, action) {  // 同chatLog}const reducer = combineReducers({  chatLog,  statusMessage,  userName})const store = createStore(reducer);

原理

function combineReducers(reducers) {  var reducerKeys = Object.keys(reducers);  var finalReducers = {};  for (var i = 0; i < reducerKeys.length; i++) {    var key = reducerKeys[i];    if (typeof reducers[key] === 'function') {      finalReducers[key] = reducers[key];    }  }  var finalReducerKeys = Object.keys(finalReducers); // This is used to make sure we don't warn about the same    // 返回还是reducer函数的类型  // dispatch理论执行的reducer  return function combination(state, action) {    // state没有默认值时,初始化一个对象    if (state === void 0) {      state = {};    }    // 标识state中每个key的值更新前后是否有变动    var hasChanged = false;    // 更新之后的state    var nextState = {};    // 遍历每个小的reducer    for (var _i = 0; _i < finalReducerKeys.length; _i++) {      // combineReducers参数对象的key      var _key = finalReducerKeys[_i];      // key对应的reducer      var reducer = finalReducers[_key];      // 通过 combineReducers参数对象的key 取出之前的状态      var previousStateForKey = state[_key];      // 每个reducer都会调用      var nextStateForKey = reducer(previousStateForKey, action);      // 保留每个key的返回的新的state      nextState[_key] = nextStateForKey;      hasChanged = hasChanged || nextStateForKey !== previousStateForKey;    }    hasChanged = hasChanged || finalReducerKeys.length !== Object.keys(state).length;    // 有变动返回新的state,没变动返回旧的state    return hasChanged ? nextState : state;  };}

总结下

  • combineReducers的返回值还是个reducer模式的函数
  • 因为createStore办法内,会初始执行一次dispatch,所以源码中combination(组合后的reducer)会首先调用一次,返回与combineReducers参数对象一样构造的state,即state的构造同combineReducers参数对象,对象的属性值是每个reducer对应的返回值
  • dispatch派发时,所有的reducer都会执行一次,reducer接管的是state对应keyvalue,不是整个state状态数据

    5. 结尾

    到这redux原理就完了,其实发现redux源码简略,外围就是createStore办法。
    一句话形容redux原理:通过subscribe订阅更新,dispatch扭转state时遍历执行所有的订阅事件。

redux两头前面会写个专文来解说,本篇文章内容不少了,不放在本篇了,先了解好redux原理。

如有谬误,请斧正,欢送评论交换,关注我,只写干货