redux源码分析

本分析既有redux的源码架构流程等,也包括自己对js基础的补课((┬_┬))

redux简介

Redux is a predictable state container for JavaScript apps.
It helps you write applications that behave consistently, run in different environments (client, server, and native), and are easy to test.
On top of that, it provides a great developer experience, such as live code editing combined with a time traveling debugger.
Rudex其实就是一个js应用的状态管理器,当状态间的转移可被预测时,就可以构建状态集合,动作集合,并确定动作对状态的影响,构建Redux。

redux整体结构

redux应该是借用了webpack的加载器

挂载位置

(function webpackUniversalModuleDefinition(root, factory) {
  if(typeof exports === 'object' && typeof module === 'object')
        module.exports = factory();//cmd
    else if(typeof define === 'function')
        define([], factory);//amd
    else if(typeof exports === 'object')
        exports["redux"] = factory();//CommonJS,主要是nodejs环境
    else
        root["redux"] = factory();
})(this,function(){})

检测是amd、cmd还是CommonJS环境,如果都不是就挂到输入的this对象下面。

模块构造

(function webpackUniversalModuleDefinition(root, factory) {

})(this,function(){
  return (function(modules) { 
      function __webpack_require__(moduleId) {} //加载函数
    })
    ([function(module, exports, __webpack_require__){
      //模块0
    },function(module, exports, __webpack_require__){
      //模块1
    }])
})

一共有十三个模块,第0个是总输出,其余十二个是功能模块。

redux方法

基础方法

以下代码对应于模块2

createStore

var store = createStore(reducer,initialState,enhancer);
这个方法创建了store树
reducer(state,action),通常包含了switch结构,根据dispatch传来的state和type来生成新的state

封装了以下闭包变量:

var currentReducer = reducer;//动作类型->方法,switch结构
var currentState = initialState;//原始状态集合,在disptch里面更改
var currentListeners = [];//通过subscribe订阅的方法
var nextListeners = currentListeners;//订阅方法的备份
var isDispatching = false;//是否正处于dispatching状态

过程中触发一个action

dispatch({ type: ActionTypes.INIT });

返回如下的store对象:

 return _ref2 = {
     dispatch: dispatch,
     subscribe: subscribe,
     getState: getState,
     replaceReducer: replaceReducer
     }, _ref2[_symbolObservable2["default"]] = observable, _ref2;

PS:
reducer是一个纯函数,它保证了状态的转移结果之后输入的参数state和action有关;

subscribe

subscribe(function () {})

订阅每次state发生变化时的方法,返回一个函数,用来取消本次订阅的方法
这里采用了闭包去封装listener,以保证这个listener的取消,不被其他listener的加入与取消影响
还封装了参数isSubscribed,保证对应的listener只被取消一次

function subscribe(listener) {
    if (typeof listener !== 'function') {
    throw new Error('Expected listener to be a function.');
}

    var isSubscribed = true;
    //备份事件队列---此方法存在的意义:防止在队列中操作事件队列(对事件队列增删)导致数据混乱
    ensureCanMutateNextListeners();
    nextListeners.push(listener);

    return function unsubscribe() {
        if (!isSubscribed) {
            return;
        }

        isSubscribed = false;
        ensureCanMutateNextListeners();
        var index = nextListeners.indexOf(listener);
        nextListeners.splice(index, 1);
    };
   }
function ensureCanMutateNextListeners() {
    if (nextListeners === currentListeners) {
        nextListeners = currentListeners.slice();//slice返回一模一样的一个副本
    }
}

PS:
观察者模式:通过subscribe添加监听事件队列,dispatch执行事件队列与更新state;

dispatch

store.dispatch({ type: ‘add’ });

执行reducer,生成新的状态,调用所有保存在nextListeners中的订阅方法

function dispatch(action) {
     if (!(0, _isPlainObject2["default"])(action)) {
     throw new Error('Actions must be plain objects. ' + 'Use custom middleware for async actions.');
     }

    if (typeof action.type === 'undefined') {
        throw new Error('Actions may not have an undefined "type" property. ' + 'Have you misspelled a constant?');
    }

    if (isDispatching) {
       throw new Error('Reducers may not dispatch actions.');
    }

    try {
        isDispatching = true;
        currentState = currentReducer(currentState, action);
    } finally {
       isDispatching = false;
    }

    var listeners = currentListeners = nextListeners;//执行订阅的状态变化后的方法
    for (var i = 0; i < listeners.length; i++) {
         listeners[i]();
    }

    return action;
}

PS:
* redux中涉及(0,code)这样的代码,其中逗号运算符会在依次执行每个表达式后返回最后一个表达式的运算结果
* plain objects 下面有解释~

getState();

简单的返回封装的参数currentState

replaceReducer

替换reducer,会在替换后触发{ type: ActionTypes.INIT }

function replaceReducer(nextReducer) {
    if (typeof nextReducer !== 'function') {
        throw new Error('Expected the nextReducer to be a function.');
    }

     currentReducer = nextReducer;//替换reducer
     dispatch({ type: ActionTypes.INIT });
}

observable

看代码就是对subscribe的再次封装,感觉像是为某些应用场景提供接口的

function observable() {
    var _ref;
    var outerSubscribe = subscribe;
    return _ref = {
       subscribe: function subscribe(observer) {
           if (typeof observer !== 'object') {
              throw new TypeError('Expected the observer to be an object.');
           }
           function observeState() {
               if (observer.next) {
                    observer.next(getState());
               }
           }
           observeState();
           var unsubscribe = outerSubscribe(observeState);//对订阅事件的取消
           return { unsubscribe: unsubscribe };
       }
    }, _ref[_symbolObservable2["default"]] = function (){
           return this;
    }, _ref;
 }

多reducer

combineReducers

属于模块7
用来合成多个reducer,其实就是把所有输入的reducer闭包封装,返回一个combination函数,这个函数会接受state和action,依次调用封装的reducer,分发state和action,来生成新的state

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);

   var sanityError;
   try {
   //此方法遍历每个reducer函数,尝试输入{ type: _createStore.ActionTypes.INIT}和一个随机的type来检测reducer是否可以输出一个state的
       assertReducerSanity(finalReducers);
   } catch (e) {
       sanityError = e;
   }

//返回组合后的reducer函数
   return function combination() {
      var state = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
      var action = arguments[1];

      if (sanityError) {
         throw sanityError;
      }

      if (true) {
          var warningMessage = getUnexpectedStateShapeWarningMessage(state, finalReducers, action);
          if (warningMessage) {
              (0, _warning2["default"])(warningMessage);
          }
      }

      var hasChanged = false;
      var nextState = {};
      //依次调用reducer,输入对应的state生成对应这个reducer的新state,返回state集合对象
      for (var i = 0; i < finalReducerKeys.length; i++) {
         var key = finalReducerKeys[i];
         var reducer = finalReducers[key];
         var previousStateForKey = state[key];
         var nextStateForKey = reducer(previousStateForKey, action);
         if (typeof nextStateForKey === 'undefined') {
             var errorMessage = getUndefinedStateErrorMessage(key, action);
             throw new Error(errorMessage);
         }
         nextState[key] = nextStateForKey;
         hasChanged = hasChanged || nextStateForKey !== previousStateForKey;
     }
     return hasChanged ? nextState : state;
   };
}

自动dispacth

对应模块6
要执行state的改变,需要手动dispatch,比如demo中那样
var add = { type: ‘add’ }; store.dispatch(add);
bindActionCreators就是解决这个问题的,原理很简单,闭包了dispatch和actionCreator方法,生成一个新的函数,这个函数可以接收和actionCreator一样的参数,调用actionCreator后,将结果作为参数传递给dispatch,并返会dispatch的结果。

function bindActionCreator(actionCreator, dispatch) {
   return function () {
      return dispatch(actionCreator.apply(undefined, arguments));
      };
}
function bindActionCreators(actionCreators, dispatch) {
    if (typeof actionCreators === 'function') {
        return bindActionCreator(actionCreators, dispatch);
    }

    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') {
          boundActionCreators[key] = bindActionCreator(actionCreator, dispatch);
       }
    }
    return boundActionCreators;
}

中间件

compose()

以下代码对应于模块1
compose为applyMiddleware方法服务,把applyMiddleware的参数串联执行返回包装的dispatch

function compose() {
    for (var _len = arguments.length, funcs = Array(_len), _key = 0; _key < _len; _key++) {
         funcs[_key] = arguments[_key];
    }

    if (funcs.length === 0) {
         return function (arg) {
              return arg;
         };
    } else {
         var _ret = function () {
             var last = funcs[funcs.length - 1];//最后一个元素
             var rest = funcs.slice(0, -1);//除最后一个元素外的所有元素
             return {
                 v: function v() {
                      return rest.reduceRight(function (composed, f) {//按降序顺序对数组中的所有元素调用指定的回调函数
                           return f(composed);
               }, last.apply(undefined, arguments));
            }
         };
    }();

     if (typeof _ret === "object") return _ret.v;
    }
}

PS
1.array.reduceRight(callbackfn[, initialValue])
按降序顺序对数组中的所有元素调用指定的回调函数。该回调函数的返回值为累积结果,并且此返回值在下一次调用该回调函数时作为参数提供。

2.rest.reduceRight这部分操作涉及到了几个概念:复合函数、柯里化。

复合函数是如下所示

var compose_fn = function(f,g) {
  return function(x) {
    return f(g(x));
  };
};

在 compose_fn 的定义中,g 将先于 f 执行,因此就创建了一个从右到左的数据流。类比redux的compose实现,可以推测出这个compose会将其参数从右向左依次封装,返回一个复合函数,这个函数接收执行时输入的参数arguments。

柯里化
只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数。柯里化的要点在于它返回的是一个函数,这个函数通过闭包的方式封装了已经传入的参数,并将接受剩下的参数。
在compose,最终返回的函数是_ret.v,它通过闭包的方式封装的参数是rest、last(其实就是compose的arguments),而它将接收的剩下的参数,就是调用返回的 _ret.v时输入的参数

applyMiddleware

以下代码对应于模块5

applyMiddleware封装了传入的作为中间件方法的arguments,在创建store时,用封装的中间件对生成的store中的原始dispatch进行包裹,生成新的dispatch

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, initialState, enhancer) {
     //创建store
        var store = createStore(reducer, initialState, enhancer);
        var _dispatch = store.dispatch;
        var chain = [];
        var middlewareAPI = {
           getState: store.getState,
           dispatch: function dispatch(action) {
               return _dispatch(action);
           }
        };
        //代码将 getState 和调用原始的 dispatch 函数注入给所有的中间件
        chain = middlewares.map(function (middleware) {
           return middleware(middlewareAPI);
        });
        //根据中间件链创建一个加工过的dispatch实现,middleware1(middleware2(middlewareN(store.dispatch)))(action)
        _dispatch = _compose2["default"].apply(undefined, chain)(store.dispatch);
        //返回对象拥有store的所有属性,并增加一个dispatch函数属性,store里自带的那个原始dispatch函数会被覆盖。
        return _extends({}, store, {
           dispatch: _dispatch
        });
     };
   };
}

辅助方法

isPlainObject

以下代码来自模块3

isPlainObject结合模块8、9、10(即以下列出的getPrototype、isHostObject和isObjectLike),共同完成对于一个变量是否为plain object的检测。

redux对plain object的解释:
an object created by the ‘Object’ constructor or one with a [[Prototype]] of null
自己找的对plain object的解释:
1.指这个对象的没有任何属性是从 proto 继承过来;2.简单对象,通过 “{}” 或者 “new Object” 创建的;


function isPlainObject(value) {
//排除null、undefined、数字、字符串、布尔值、函数和宿主对象
    if (!isObjectLike(value) ||
       objectToString.call(value) != objectTag || isHostObject(value)) {
           return false;
       }
//获取 __proto__,如果 __proto__为null,就确定是plain object 
    var proto = getPrototype(value);
    if (proto === null) {
        return true;
    }
//获取原型链上的构造函数,如果为Object的构造函数,就确定是plain object 
    var Ctor = hasOwnProperty.call(proto, 'constructor') && proto.constructor;
    return (typeof Ctor == 'function' &&
    Ctor instanceof Ctor && funcToString.call(Ctor) == objectCtorString);
}
getPrototype

以下代码来自模块8

var nativeGetPrototype = Object.getPrototypeOf;
function getPrototype(value) {
    return nativeGetPrototype(Object(value));
}
isHostObject

以下代码来自模块9
检查value是否为IE9以下的宿主对象

function isHostObject(value) {
    var result = false;
    if (value != null && typeof value.toString != 'function') {
        try {
            result = !!(value + '');
        } catch (e) {}
     }
    return result;
}
isObjectLike

以下代码来自模块10

function isObjectLike(value) {
    return !!value && typeof value == 'object';
}

Symbol.observable

模块11和12检测全局环境的Symbol.observable这个变量,看代码和上面的observable函数有关,先留着,再研究

几个小问题

1
var _isPlainObject = __webpack_require__(4);
var _isPlainObject2 = _interopRequireDefault(_isPlainObject);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }

多次出现的这种写法是为了什么?

2
 if (!(0, _isPlainObject2["default"])(inputState)){}

这样写是为了什么

(好像是Babel做es6转es5时写的,没什么意义)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值