单一数据源 Single source of truth:整个应用的 state 被储存在一棵 object tree 中,而且这个 object tree 只存在于惟一一个 store 中。redux
state 只读 State is read-only:唯一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象。这样确保了视图和网络请求都不能直接修改 state,相反它们只能表达想要修改的意图。数组
使用纯函数来执行修改 Changes are made with pure functions:为了描述 action 如何改变 state tree ,你须要编写 reducers。Reducer 只是一些纯函数,它接收先前的 state 和 action,并返回新的 state。网络
Action: 是把数据传到 store 的惟一手段,表达修改 state 的意图。它是 store 数据的惟一来源。通常经过 store.dispatch() 将 action 传到 store。session
{ type:types.RECEIVE_CASES, cases }
Action Creator:是一个建立 action 的纯函数。闭包
export function receiveCases(cases) { return { type:types.RECEIVE_CASES, cases } }
Reducer:是一个纯函数,接收旧的 state 和 action,一般包含了switch结构,根据dispatch传来的state和type来生成新的state。app
Store:构建顺序:框架
reducers => 'combineReducers' => rootReducer => 'createStore' (+ 'applyMiddleware') | ===> **Store**
redux.createStore(reducer, initialState) 传入了reducer、initialState,并返回一个store对象。ide
store对象对外暴露了dispatch、getState、subscribe方法函数
store对象经过getState() 获取内部状态ui
initialState为 store 的初始状态,若是不传则为undefined
store对象经过reducer来修改内部状态
store对象建立的时候,内部会主动调用dispatch({ type: ActionTypes.INIT });来对内部状态进行初始化。经过断点或者日志打印就能够看到,store对象建立的同时,reducer就会被调用进行初始化。
/** * Creates a Redux store that holds the state tree. * The only way to change the data in the store is to call `dispatch()` on it. * * There should only be a single store in your app. To specify how different * parts of the state tree respond to actions, you may combine several reducers * into a single reducer function by using `combineReducers`. * * @param {Function} reducer A function that returns the next state tree, given * the current state tree and the action to handle. * --- reducer 是 Function,用于构建 state 树 * * @param {any} [preloadedState] The initial state. You may optionally specify it * to hydrate the state from the server in universal apps, or to restore a * previously serialized user session. * If you use `combineReducers` to produce the root reducer function, this must be * an object with the same shape as `combineReducers` keys. * --- preloadedState 初始化 state 树 * * @param {Function} enhancer The store enhancer. You may optionally specify it * to enhance the store with third-party capabilities such as middleware, * time travel, persistence, etc. The only store enhancer that ships with Redux * is `applyMiddleware()`. * --- enhancer 至关于 AOP 插件 * * @returns {Store} A Redux store that lets you read the state, dispatch actions * and subscribe to changes. * --- 最终返回 Store,能获取 state、分发action、订阅变化 */ export default function createStore(reducer, preloadedState, enhancer) { ... // --- 校验参数,及执行 enhancer var currentReducer = reducer var currentState = preloadedState // --- state 树 var currentListeners = [] // --- 注册的监听器列表,实时处理 dispatch 事件 var nextListeners = currentListeners // --- 注册的监听器列表,实时接收 subscribe 事件 var isDispatching = false // --- 若是 reducer 正在执行,会抛出异常 // --- 将 nextListeners 作为 currentListeners 的副本 function ensureCanMutateNextListeners() { if (nextListeners === currentListeners) { nextListeners = currentListeners.slice() } } // --- 返回 state function getState() { ... // --- 校验 return currentState } // --- 注册监听器 // 很常见的监听函数添加方式,当store.dispatch 的时候被调用 // store.subscribe(listener) 返回一个方法(unscribe),能够用来取消监听 function subscribe(listener) { listeners.push(listener); var isSubscribed = true; return function unsubscribe() { if (!isSubscribed) { return; } isSubscribed = false; var index = listeners.indexOf(listener); listeners.splice(index, 1); }; } // --- 分发 action,修改 state 的惟一方式 function dispatch(action) { // 如下状况会报错 // 1. 传入的action不是一个对象 // 2. 传入的action是个对象,可是action.type 是undefined try { isDispatching = true // 就是这一句啦, 将 currentState 设置为 reducer(currentState, action) 返回的值 currentState = currentReducer(currentState, action) // 执行 reducer,state 被更新 } finally { isDispatching = false } // --- 若是有监听函数,就顺序调用监听器方法 //listeners.slice().forEach(listener => listener()); var listeners = currentListeners = nextListeners for (var i = 0; i < listeners.length; i++) { listeners[i]() } // --- 返回结果仍是 action return action } // --- 替换 reducer,用于热替换、按需加载等场景 function replaceReducer(nextReducer) { ... // --- 校验 currentReducer = nextReducer dispatch({ type: ActionTypes.INIT }) } function observable() { // --- 略 } // --- 建立时初始化应用状态 // redux.createStore(reducer, initialState) 的时候, 内部会 本身调用 dispatch({ type: ActionTypes.INIT }); // 来完成state的初始化 dispatch({ type: ActionTypes.INIT }) return { dispatch, subscribe, getState, replaceReducer, [$$observable]: observable } }
function TodoReducer(state, action) {} function FilterReducer(state, action) {} var finalReducers = redux.combineReducers({ todos: TodoReducer, filter: FilterReducer });
redux.combineReducers(reducerMap) 用来合成多个reducer,其实就是把全部输入的reducer闭包封装,返回一个combination函数,这个函数会接受state和action,依次调用封装的reducer,分发state和action,来生成新的state
combineReducers(reducerMap) 传入一个对象,并返回一个全新的reducer。调用方式跟跟普通的reducer同样,也是传入state、action。
经过combineReducers,对 store 的状态state进行拆分,
reducerMap的key,就是 state 的key,而 调用对应的reducer返回的值,则是这个key对应的值。如上面的例子,state.todos == TodoReducer(state, action)
redux.createStore(finalReducers, initialState) 调用时,一样会对 state 进行初始化。这个初始化跟经过普通的reducer进行初始化没多大区别。举例来讲,若是 initialState.todos = undefined,那么 TodoReducer(state, action) 初始传入的state就是undefined;若是initialState.todos = [],那么 TodoReducer(state, action) 初始传入的state就是[];
store.dispatch(action),finalReducers 里面,会遍历整个reducerMap,依次调用每一个reducer,并将每一个reducer返回的子state赋给state对应的key。
/** * Turns an object whose values are different reducer functions, into a single * reducer function. It will call every child reducer, and gather their results * into a single state object, whose keys correspond to the keys of the passed * reducer functions. * * @param {Object} reducers An object whose values correspond to different * reducer functions that need to be combined into one. One handy way to obtain * it is to use ES6 `import * as reducers` syntax. The reducers may never return * undefined for any action. Instead, they should return their initial state * if the state passed to them was undefined, and the current state for any * unrecognized action. * * @returns {Function} A reducer function that invokes every reducer inside the * passed object, and builds a state object with the same shape. */ export default function combineReducers(reducers) { // --- 过滤 reducer 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) // --- 返回 combination 函数 return function combination(state = {}, action) { // --- 校验 // --- 根据 reducer key 及执行结果构造 state 树 var hasChanged = false var nextState = {} 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) // --- 执行 reducer // --- 校验 nextState[key] = nextStateForKey hasChanged = hasChanged || nextStateForKey !== previousStateForKey } return hasChanged ? nextState : state } }
applyMiddleware 传入 middleware 链,并返回应用这些 middleware 的 store enhancer
export default function applyMiddleware(...middlewares) { return (next) => (reducer, initialState) => { // 内部先建立一个store (至关于直接调用 Redux.createStore(reducer, initialState)) var store = next(reducer, initialState); // 保存最初始的store.dispatch var dispatch = store.dispatch; var chain = []; var middlewareAPI = { getState: store.getState, // 最后面, dispatch 被覆盖, 变成包装后的 dispatch 方法 dispatch: (action) => dispatch(action) }; // 返回一个数组 // 贴个例子在这里作参考,redux-thunk // function thunkMiddleware(store) { // var dispatch = store.dispatch; // var getState = store.getState; // // 这里的next其实就是dispatch // return function (next) { // return function (action) { // return typeof action === 'function' ? action(dispatch, getState) : next(action); // }; // }; //} /* chain 是个数组, 参考上面的 middlleware (redux-thunk),能够看到,chain的每一个元素为以下形式的function 而且, 传入的 store.getState 为原始的 store.getState,而 dispatch则是包装后的 dispatch(不是原始的store.dispatch) 彷佛是为了确保, 在每一个middleware里调用 dispatch(action), 最终都是 用原始的 store.dispatch(action) 避免 store.dispatch 被覆盖, 致使middleware 顺序调用的过程当中, store.dispatch的值变化 --> store.dispatch 返回的值可能会有不一样 违背 redux 的设计理念 这里的 next 则为 原始的 store.dispatch (见下面 compose(...chain)(store.dispatch) ) function (next) { return function (action) { } } */ chain = middlewares.map(middleware => middleware(middlewareAPI)); // compose(...chain)(store.dispatch) 返回了一个function // 伪代码以下, // function (action) { // middleware(store)(store.dispatch); // } dispatch = compose(...chain)(store.dispatch); // 从右到左, middleware1( middleware2( middleware3(dispatch) ) ) // 因而,最终调用 applyMiddleware(...middlewares)(Redux.createStore) // 返回的 store, getState,subscribe 方法都是原始的那个 store.getState, store.subscribe // 至于dispatch是封装过的 return { ...store, dispatch }; }; }
export default function applyMiddleware(...middlewares) { // --- 传入 createStore,返回加强版的 store return (createStore) => (reducer, preloadedState, enhancer) => { var store = createStore(reducer, preloadedState, enhancer) var dispatch = store.dispatch var chain = [] // --- middleware 中的参数 var middlewareAPI = { getState: store.getState, dispatch: (action) => dispatch(action) } // --- 给 middleware 传参 chain = middlewares.map(middleware => middleware(middlewareAPI)) // --- 组合 chain,传入原始的 dispatch dispatch = compose(...chain)(store.dispatch) return { ...store, // --- 保留 subscribe, getState, replaceReducer, [$$observable]: observable dispatch // --- 用新 dispatch 覆盖,以后调用 dispatch 就会触发 chain 内的 middleware 链式执行 } } }
React-Redux是用在链接React和Redux上的。若是你想同时用这两个框架,那么React-Redux基本就是必须的了。
export default class Provider extends Component { getChildContext() { // 将其声明为 context 的属性之一 return { store: this.store } } constructor(props, context) { super(props, context) // 接收 redux 的 store 做为 props this.store = props.store } render() { return Children.only(this.props.children) } } Provider.propTypes = { store: storeShape.isRequired, children: PropTypes.element.isRequired } Provider.childContextTypes = { store: storeShape.isRequired }