码迷,mamicode.com
首页 > 其他好文 > 详细

react-redux源码解析

时间:2016-04-15 00:40:52      阅读:1159      评论:0      收藏:0      [点我收藏+]

标签:

  有理解不对的地方,欢迎大家指正!!!

  react为什么需要redux辅助???react是view层的单向数据流框架,数据需要一层一层往子组件传递(子组件并不会自动继承)。子组件需要操作父组件的数据时,需要父组件给子组件传递一个方法,子组件调用该方法才能改变父组件的数据。如果组件的层次太深会这种传递会很繁琐,令代码沉余。用redux能很好解决这个问题,redux+react-redux可以使一个容器内的数据通过this.props共享,避免了一层层传递数据繁琐的操作。

redux使用了纯函数写法,这种编程模式就是函数式编程(简称:fb)。rudex使用了大量的设计模式比较,如:装饰者模式(包装),工厂模式,桥接模式,代理模式,观察者模式。

装饰者模式:包装了action、dispatch、createStore,扩展本身满足需求;

工厂模式:action包装器也是一个工厂,通过该方法生产新的action;

代理模式:applyMiddleware返回一个方法回去取需要的createStore方法;

桥接模式:isPlainObject通过连接isHostObject与isObjectLike方法来完成新的功能;

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

 

模块1:其实模块1并没有什么好介绍的,主体就一个compose方法为模块5的applyMiddleware方法服务,把applyMiddleware的参数串联执行,最后返回包装的dispatch。

 

/* 1 */
/***/ function(module, exports) {

    "use strict";

    exports.__esModule = true;
    exports["default"] = compose;
    /**
     * Composes single-argument functions from right to left.
     *
     * @param {...Function} funcs The functions to compose.
     * @returns {Function} A function obtained by composing functions from right to
     * left. For example, compose(f, g, h) is identical to arg => f(g(h(arg))).
     */
    //compose为模块5的applyMiddleware方法服务,把applyMiddleware的参数串联执行返回包装的dispatch
    function compose() {
      //复制参数
      for (var _len = arguments.length, funcs = Array(_len), _key = 0; _key < _len; _key++) {
        funcs[_key] = arguments[_key];
      }return function () {
        if (funcs.length === 0) {
          return arguments.length <= 0 ? undefined : arguments[0];
        }

        var last = funcs[funcs.length - 1]; //最后一个参数
        var rest = funcs.slice(0, -1); //除了最后一个参数外的所有参数
     //从右到左串联执行rest参数列表里的方法
return rest.reduceRight(function (composed, f) { return f(composed); }, last.apply(undefined, arguments)); } /***/ },

 

 



模块2:主体createStore方法,createStore方法里主要包含:subscribe--订阅事件,dispatch---发布事件

其实他们做的事都很简单:subscribe把接受的push(入栈)进一个数组,dispatch被调用时则依次执行数组里的方法

  1 /* 2 */
  2 /***/ function(module, exports, __webpack_require__) {
  3 
  4     ‘use strict‘;
  5 
  6     exports.__esModule = true;
  7     exports.ActionTypes = undefined;
  8     exports["default"] = createStore;
  9 
 10     var _isPlainObject = __webpack_require__(4);
 11 
 12     var _isPlainObject2 = _interopRequireDefault(_isPlainObject);
 13   //初始化
 14     function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
 15 
 16     /**
 17      * These are private action types reserved by Redux.
 18      * For any unknown actions, you must return the current state.
 19      * If the current state is undefined, you must return the initial state.
 20      * Do not reference these action types directly in your code.
 21      */
 22     var ActionTypes = exports.ActionTypes = {
 23       INIT: ‘@@redux/INIT‘
 24     };
 25   //创建store树
 51     function createStore(reducer, initialState, enhancer) {
      //参数匹配
52 if (typeof initialState === ‘function‘ && typeof enhancer === ‘undefined‘) { 53 enhancer = initialState; 54 initialState = undefined; 55 } 56 57 if (typeof enhancer !== ‘undefined‘) { 58 if (typeof enhancer !== ‘function‘) { 59 throw new Error(‘Expected the enhancer to be a function.‘); 60 } 61 //enhancer扩展createStore进行扩展 62 return enhancer(createStore)(reducer, initialState); 63 } 64 65 if (typeof reducer !== ‘function‘) { 66 throw new Error(‘Expected the reducer to be a function.‘); 67 } 68 69 var currentReducer = reducer; 70 var currentState = initialState; 71 var currentListeners = []; //存储事件队列 72 var nextListeners = currentListeners; //存储备份事件队列 73 var isDispatching = false; 74 //备份事件队列---此方法存在的意义:防止在队列中操作事件队列(对事件队列增删)导致数据混乱 75 function ensureCanMutateNextListeners() { 76 if (nextListeners === currentListeners) { 77 nextListeners = currentListeners.slice(); 78 } 79 } 80
      //获取state
86 function getState() { 87 return currentState; 88 } 89 113 //订阅事件 114 function subscribe(listener) { 115 if (typeof listener !== ‘function‘) { 116 throw new Error(‘Expected listener to be a function.‘); 117 } 118 //保证事件只能被卸载一次 119 var isSubscribed = true; 120 121 ensureCanMutateNextListeners(); 122 nextListeners.push(listener); 123 //闭包缓存正在监听的事件,可以通过:var unsub=subscribe(listener); unsub()来卸载此事件 124 return function unsubscribe() { 125 if (!isSubscribed) { 126 return; 127 } 128 isSubscribed = false; 129 //备份事件队列再进行卸载操作 130 ensureCanMutateNextListeners(); 131 var index = nextListeners.indexOf(listener); 132 nextListeners.splice(index, 1); 133 }; 134 } 135 161 //发布事件 162 function dispatch(action) { 163 //检测action是否是字面量对象 164 if (!(0, _isPlainObject2["default"])(action)) { 165 throw new Error(‘Actions must be plain objects. ‘ + ‘Use custom middleware for async actions.‘); 166 } 167 168 if (typeof action.type === ‘undefined‘) { 169 throw new Error(‘Actions may not have an undefined "type" property. ‘ + ‘Have you misspelled a constant?‘); 170 } 171 172 if (isDispatching) { 173 throw new Error(‘Reducers may not dispatch actions.‘); 174 } 175 176 try { 177 isDispatching = true; 178 //执行reducer更新state 179 currentState = currentReducer(currentState, action); 180 } finally { 181 isDispatching = false; 182 } 183 //同步事件队列---执行最新的事件队列 184 var listeners = currentListeners = nextListeners; 185 for (var i = 0; i < listeners.length; i++) { 186 listeners[i](); 187 } 188 189 return action; 190 } 191 202 //替换reducer 203 function replaceReducer(nextReducer) { 204 if (typeof nextReducer !== ‘function‘) { 205 throw new Error(‘Expected the nextReducer to be a function.‘); 206 } 207 208 currentReducer = nextReducer; 209 dispatch({ type: ActionTypes.INIT }); 210 } 211 212 //初始化state 215 dispatch({ type: ActionTypes.INIT }); 216 217 return { 218 dispatch: dispatch, 219 subscribe: subscribe, 220 getState: getState, 221 replaceReducer: replaceReducer 222 }; 223 } 224 225 /***/ },




模块4:模块3很简单就此跳过,咱们进入模块4。模块4也比较简单主体:isPlainObject方法主要是检测是否是字面量对象或者是直接实例化Object构造函数的实例对象
 1 /* 4 */
 2 /***/ function(module, exports, __webpack_require__) {
 3 
 4     var isHostObject = __webpack_require__(8),
 5         isObjectLike = __webpack_require__(9);
 6 
 7     /** `Object#toString` result references. */
 8     var objectTag = ‘[object Object]‘;
 9 
10     /** Used for built-in method references. */
11     var objectProto = Object.prototype;
12 
13     /** Used to resolve the decompiled source of functions. */
14     var funcToString = Function.prototype.toString;
15 
16     /** Used to infer the `Object` constructor. */
17     var objectCtorString = funcToString.call(Object);
18 
19     /**
20      * Used to resolve the [`toStringTag`](http://ecma-international.org/ecma-262/6.0/#sec-object.prototype.tostring)
21      * of values.
22      */
23     var objectToString = objectProto.toString;
24 
25     /** Built-in value references. */
26     var getPrototypeOf = Object.getPrototypeOf;
27 
28     /**
29      * Checks if `value` is a plain object, that is, an object created by the
30      * `Object` constructor or one with a `[[Prototype]]` of `null`.
31      *
32      * @static
33      * @memberOf _
34      * @category Lang
35      * @param {*} value The value to check.
36      * @returns {boolean} Returns `true` if `value` is a plain object, else `false`.
37      * @example
38      *
39      * function Foo() {
40      *   this.a = 1;
41      * }
42      *
43      * _.isPlainObject(new Foo);
44      * // => false
45      *
46      * _.isPlainObject([1, 2, 3]);
47      * // => false
48      *
49      * _.isPlainObject({ ‘x‘: 0, ‘y‘: 0 });
50      * // => true
51      *
52      * _.isPlainObject(Object.create(null));
53      * // => true
54      */
55     //判断是否由Object直接构造出来的实例
56     function isPlainObject(value) {
57       if (!isObjectLike(value) || objectToString.call(value) != objectTag || isHostObject(value)) {
58         return false;
59       }
60       var proto = objectProto;
61       if (typeof value.constructor == ‘function‘) {
62         proto = getPrototypeOf(value);
63       }
64     //参数的构造函数时function且原型是null
65       if (proto === null) {
66         return true;
67       }
68       var Ctor = proto.constructor;
69       return (typeof Ctor == ‘function‘ &&
70         Ctor instanceof Ctor && funcToString.call(Ctor) == objectCtorString);
71     }
72 
73     module.exports = isPlainObject;
74 
75 
76 /***/ },

 

模块5:此模块的逻辑比较复杂,但是实现的东西却很简单:包装了createStore方法与createStore里的dispatch方法,使dispath支持异步。
  applyMiddleware参数是redux提供的两个中间件:redux-thunks、redux-logger,两个模块提供方法对dispatch进行了包装。
  
 1 /* 5 */
 2 /***/ function(module, exports, __webpack_require__) {
 3 
 4     ‘use strict‘;
 5 
 6     var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
 7 
 8     exports.__esModule = true;
 9     exports["default"] = applyMiddleware;
10 
11     var _compose = __webpack_require__(1);
12 
13     var _compose2 = _interopRequireDefault(_compose);
14 
15     function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
16   //包装createStore与dispatch
17     function applyMiddleware() {
18       for (var _len = arguments.length, middlewares = Array(_len), _key = 0; _key < _len; _key++) {
19         middlewares[_key] = arguments[_key];
20       }
21       return function (createStore) {
22     //返回一个包装的createStore
23         return function (reducer, initialState, enhancer) {
24           var store = createStore(reducer, initialState, enhancer);
25           var _dispatch = store.dispatch;
26           var chain = [];
27 
28           var middlewareAPI = {
29             getState: store.getState,
30             dispatch: function dispatch(action) {
31               return _dispatch(action);
32             }
33           };
34           chain = middlewares.map(function (middleware) {
35             return middleware(middlewareAPI);
36           })
37           //下面的代码其实就是执行:thunkMiddleware(middlewareAPI)(createLogger()(middlewareAPI)(dispatch))返回被包装的dispatch
38           _dispatch = _compose2["default"].apply(undefined, chain)(store.dispatch);
39           //将包装好的dispatch写入store
40           return _extends({}, store, {
41             dispatch: _dispatch
42           });
43         };
44       };
45     } 

 

redux-thunks模块里的thunkMiddleware方法:
 1     function thunkMiddleware(_ref) {
 2       var dispatch = _ref.dispatch;
 3       var getState = _ref.getState;
 4 
 5       return function (next) {
 6       //返回一个包装的dispacth
 7         return function (action) {
 8           if (typeof action === ‘function‘) {
 9             return action(dispatch, getState);
10           }
11           return next(action);  //由于闭包next一直存在于包装的dispatch里,next其实是一个普通的dispatch,虽然经过了createLogger方法(redux-logger里面内置的一个方法)的包装, 但是主要作用与createStore定义时的dispatch方法是一样的
12         };
13       };
14     }

 模块6:把action与dispatch方法绑定在一起,即把每个action包装着一个dispatch方法,然后执行action时就会自动dispath

 1 /* 6 */
 2 /***/ function(module, exports) {
 3 
 4     ‘use strict‘;
 5 
 6     exports.__esModule = true;
 7     exports["default"] = bindActionCreators;
//包装器---返回一个自动执行dispatch的方法
8 function bindActionCreator(actionCreator, dispatch) { 9 return function () { 10 return dispatch(actionCreator.apply(undefined, arguments)); 11 }; 12 } 13 35 36 function bindActionCreators(actionCreators, dispatch) { 37 if (typeof actionCreators === ‘function‘) { 38 return bindActionCreator(actionCreators, dispatch); 39 } 40 //判断是否是对象,不是对象则报错 41 if (typeof actionCreators !== ‘object‘ || actionCreators === null) { 42 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"?‘); 43 } 44 //获取键名数组 45 var keys = Object.keys(actionCreators); 46 var boundActionCreators = {}; 47 for (var i = 0; i < keys.length; i++) { 48 var key = keys[i]; 49 var actionCreator = actionCreators[key]; 50 if (typeof actionCreator === ‘function‘) { 51 //收集包装器返回的新的action方法 52 boundActionCreators[key] = bindActionCreator(actionCreator, dispatch); 53 } 54 } 55 return boundActionCreators; 56 }

 

 

模块7:执行reducers返回state

  1 /* 7 */
  2 /***/ function(module, exports, __webpack_require__) {
  3 
  4     ‘use strict‘;
  5 
  6     exports.__esModule = true;
  7     exports["default"] = combineReducers;
  8 
  9     var _createStore = __webpack_require__(2);
 10 
 11     var _isPlainObject = __webpack_require__(4);
 12 
 13     var _isPlainObject2 = _interopRequireDefault(_isPlainObject);
 14 
 15     var _warning = __webpack_require__(3);
 16 
 17     var _warning2 = _interopRequireDefault(_warning);
 18 
 19     function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
 20 
 21     function getUndefinedStateErrorMessage(key, action) {
 22       var actionType = action && action.type;
 23       var actionName = actionType && ‘"‘ + actionType.toString() + ‘"‘ || ‘an action‘;
 24 
 25       return ‘Reducer "‘ + key + ‘" returned undefined handling ‘ + actionName + ‘. ‘ + ‘To ignore an action, you must explicitly return the previous state.‘;
 26     }
 27 
 28     function getUnexpectedStateShapeWarningMessage(inputState, reducers, action) {
 29       var reducerKeys = Object.keys(reducers);
 30       var argumentName = action && action.type === _createStore.ActionTypes.INIT ? ‘initialState argument passed to createStore‘ : ‘previous state received by the reducer‘;
 31 
 32       if (reducerKeys.length === 0) {
 33         return ‘Store does not have a valid reducer. Make sure the argument passed ‘ + ‘to combineReducers is an object whose values are reducers.‘;
 34       }
 35 
 36       if (!(0, _isPlainObject2["default"])(inputState)) {
 37         return ‘The ‘ + argumentName + ‘ has unexpected type of "‘ + {}.toString.call(inputState).match(/\s([a-z|A-Z]+)/)[1] + ‘". Expected argument to be an object with the following ‘ + (‘keys: "‘ + reducerKeys.join(‘", "‘) + ‘"‘);
 38       }
 39 
 40       var unexpectedKeys = Object.keys(inputState).filter(function (key) {
 41         return !reducers.hasOwnProperty(key);
 42       });
 43 
 44       if (unexpectedKeys.length > 0) {
 45         return ‘Unexpected ‘ + (unexpectedKeys.length > 1 ? ‘keys‘ : ‘key‘) + ‘ ‘ + (‘"‘ + unexpectedKeys.join(‘", "‘) + ‘" found in ‘ + argumentName + ‘. ‘) + ‘Expected to find one of the known reducer keys instead: ‘ + (‘"‘ + reducerKeys.join(‘", "‘) + ‘". Unexpected keys will be ignored.‘);
 46       }
 47     }
 48     //初始化reducers并检测时候会出错
 49     function assertReducerSanity(reducers) {
 50       Object.keys(reducers).forEach(function (key) {
 51         var reducer = reducers[key];
 52         var initialState = reducer(undefined, { type: _createStore.ActionTypes.INIT });
 53 
 54         if (typeof initialState === ‘undefined‘) {
 55           throw new Error(‘Reducer "‘ + key + ‘" returned undefined during initialization. ‘ + ‘If the state passed to the reducer is undefined, you must ‘ + ‘explicitly return the initial state. The initial state may ‘ + ‘not be undefined.‘);
 56         }
 57 
 58         var type = ‘@@redux/PROBE_UNKNOWN_ACTION_‘ + Math.random().toString(36).substring(7).split(‘‘).join(‘.‘);
 59         if (typeof reducer(undefined, { type: type }) === ‘undefined‘) {
 60           throw new Error(‘Reducer "‘ + key + ‘" returned undefined when probed with a random type. ‘ + (‘Don\‘t try to handle ‘ + _createStore.ActionTypes.INIT + ‘ or other actions in "redux/*" ‘) + ‘namespace. They are considered private. Instead, you must return the ‘ + ‘current state for any unknown actions, unless it is undefined, ‘ + ‘in which case you must return the initial state, regardless of the ‘ + ‘action type. The initial state may not be undefined.‘);
 61         }
 62       });
 63     }
 64 

 81     function combineReducers(reducers) {
 82       var reducerKeys = Object.keys(reducers);
 83       var finalReducers = {};
 84       //过滤参数---把reducers里的方法放进finalReducers
 85       for (var i = 0; i < reducerKeys.length; i++) {
 86         var key = reducerKeys[i];
 87         if (typeof reducers[key] === ‘function‘) {
 88           finalReducers[key] = reducers[key];
 89         }
 90       }
 91       var finalReducerKeys = Object.keys(finalReducers);
 92 
 93       var sanityError;
 94       try {
 95         assertReducerSanity(finalReducers);
 96       } catch (e) {
 97         sanityError = e;
 98       }
 99       //把state,action分发给每一个reducer,并执行返回新的state,如果state没变化则返回原来的state
100       return function combination() {
101         var state = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
102         var action = arguments[1];
103 
104         if (sanityError) {
105           throw sanityError;
106         }
107 
108         if (true) {
109           var warningMessage = getUnexpectedStateShapeWarningMessage(state, finalReducers, action);
110           if (warningMessage) {
111             (0, _warning2["default"])(warningMessage);
112           }
113         }
114 
115         var hasChanged = false;
116         var nextState = {};
117         for (var i = 0; i < finalReducerKeys.length; i++) {
118           var key = finalReducerKeys[i];
119           var reducer = finalReducers[key];//获取一个reducer方法
120           var previousStateForKey = state[key]; //获取上次的state
121           var nextStateForKey = reducer(previousStateForKey, action);//执行reducer
122           if (typeof nextStateForKey === ‘undefined‘) {
123             var errorMessage = getUndefinedStateErrorMessage(key, action);
124             throw new Error(errorMessage);
125           }
126           nextState[key] = nextStateForKey; 
127           hasChanged = hasChanged || nextStateForKey !== previousStateForKey;//判断上次的state与现在的state是否相等
128         }
129         return hasChanged ? nextState : state;
130       };
131     }

模块8:检测ie9以下的宿主对象,即dom与bom,就不上源码了

模块9:检测是否是对象

 

总结一下:

redux是不是很简单?就那么几个方法:createStore,subscribe,dispatch,getState,applyMiddleware,bindActionCreators,combineReducers。

createStore:创建store树;

createStore->subscribe:订阅事件,把监听的执行的方法放进来,其实就是一个数组;

createStore->dispatch:发布,执行所有的监听事件,且执行reducer更新state;

createStore->getState:获取state;

applyMiddleware:包装createStore与dispatch;

bindActionCreators:包装所有的action方法,给每个action包装一个dispatch方法,使执行action方法就会自动触发dispatch方法

combineReducers:合并多个reducer;

 

 



 

 

react-redux源码解析

标签:

原文地址:http://www.cnblogs.com/dudeyouth/p/5391262.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!