1. 概述
本文带着大家回顾下redux
用法,redux
的API
,createStore
、reducer
、dispatch
等,了解用法后,一起探索原理,难点是redux中间件
的原理局部。
留神:本文只探索redux
,不关注react-redux
,react-redux
的应用和原理前面会有专文解说,redux
的用途不限于react
,任何你想要订阅事件的中央,都能够独自应用redux
。
2. 设计思维
redux
是将整个利用状态存储到到一个中央,称为store
,繁多数据源- 外面保留一棵状态树
state
- 派发
dispatch
行为action
给store
,而不是间接更改state
,只读 用户通过订阅(
subscribe
) store,当有dispatch
行为时,会告诉订阅者更新下图是我画的
redux
流程,其实从图中看redux逻辑也简略,你只能用dispatch
通过派发action
更新state
,action
派发后,外部有两个事件解决,一是要调用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
承受旧的state
和action
作为参数,返回新的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
对应key
的value
,不是整个state
状态数据5. 结尾
到这redux原理就完了,其实发现redux源码简略,外围就是
createStore
办法。
一句话形容redux原理:通过subscribe订阅更新,dispatch扭转state时遍历执行所有的订阅事件。
redux两头前面会写个专文来解说,本篇文章内容不少了,不放在本篇了,先了解好redux原理。
完
如有谬误,请斧正,欢送评论交换,关注我,只写干货