整个工作流程:
三大原则
1.单一数据源(Store) 整个应用的State被存放在一棵Object tree中,并且这个Object tree只存在唯一一个Store中;
2.State是只读的 唯一改变 State 的方法就是触发 Action,Action 是一个用于描述已发生事件的普通对象。 确保了所有的修改都能被集中化处理。
3.通过纯函数Reducer来修改Store, Reducer 只是一些纯函数,它接收先前的 State 和 Action,并返回新的 State。 即reducer(state, action) => new state
export default function createStore(reducer, preloadedState, enhancer) { if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') { // 第二个参数是一个函数,没有第三个参数的情况 enhancer = preloadedState; preloadedState = undefined; } // 如果第三个参数是函数走下面的逻辑,返回一个新的createStore if (typeof enhancer !== 'undefined') { if (typeof enhancer !== 'function') { // enhancer 不是函数就报错 throw new Error('Expected the enhancer to be a function.'); } // enhancer就是高阶函数,强化了本身这个createStore的函数,拿到增强后的createStore函数去处理 // applyMiddleware这个函数还会涉及到这个 return enhancer(createStore)(reducer, preloadedState); } if (typeof reducer !== 'function') { // reducer不是函数报错 throw new Error('Expected the reducer to be a function.'); } // 其他代码省略 return { dispatch, subscribe, getState, replaceReducer, [$$observable]: observable, }; }
export default function applyMiddleware(...middlewares) { return (createStore) => (...args) => { const store = createStore(...args); let dispatch = () => { throw new Error( 'Dispatching while constructing your middleware is not allowed. ' + 'Other middleware would not be applied to this dispatch.', ); }; const middlewareAPI = { getState: store.getState, dispatch: (...args) => dispatch(...args), }; const chain = middlewares.map((middleware) => middleware(middlewareAPI)); dispatch = compose(...chain)(store.dispatch); return { ...store, dispatch, }; }; } // 其实就是修改了dispatch
let store = applyMiddleware(middleware1,middleware2)(createStore)(rootReducer);
从执行结果看,这时候 state 已经变成了一个以这些 reducer 为 key 的对象;
reducer 也变成了一个合并的 reducer;
遍历执行所有的 reducer 的时候把 action 传进去,返回新的 state;
export default function combineReducers(reducers) { const reducerKeys = Object.keys(reducers); const finalReducers = {}; for (let i = 0; i < reducerKeys.length; i++) { const key = reducerKeys[i]; if (typeof reducers[key] === 'function') { finalReducers[key] = reducers[key]; } } const finalReducerKeys = Object.keys(finalReducers); /* 返回一个整合后的reducers */ return function combination(state = {}, action) { let hasChanged = false; const nextState = {}; for (let i = 0; i < finalReducerKeys.length; i++) { const key = finalReducerKeys[i]; const reducer = finalReducers[key]; const previousStateForKey = state[key]; const nextStateForKey = reducer(previousStateForKey, action); if (typeof nextStateForKey === 'undefined') { const errorMessage = getUndefinedStateErrorMessage(key, action); throw new Error(errorMessage); } nextState[key] = nextStateForKey; hasChanged = hasChanged || nextStateForKey !== previousStateForKey; } return hasChanged ? nextState : state; }; }
function dispatch(action) { if (!isPlainObject(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++) { var listener = listeners[i]; listener(); } return action; }
function logger(store) { return function (next) { return function (action) { // 新的 dispatch 函数 console.group(action.type); console.info('dispatching', action); let result = next(action); console.log('next state', store.getState()); console.groupEnd(); return result; }; }; }
import React from 'react'; import { createStore, applyMiddleware } from 'redux'; function createLogger({ getState, dispatch }) { return (next) => (action) => { const prevState = getState(); console.log('createLogger1'); const returnValue = next(action); const nextState = getState(); const actionType = String(action.type); const message = `action ${actionType}`; console.log(`%c prev state`, `color: #9E9E9E`, prevState); console.log(`%c action`, `color: #03A9F4`, action); console.log(`%c next state`, `color: #4CAF50`, nextState); return returnValue; }; } function createLogger2({ getState }) { return (next) => (action) => { const console = window.console; const prevState = getState(); console.log('createLogger2'); const returnValue = next(action); const nextState = getState(); const actionType = String(action.type); const message = `action ${actionType}`; console.log(`%c prev state2`, `color: #9E9E9E`, prevState); console.log(`%c action2`, `color: #03A9F4`, action); console.log(`%c next state2`, `color: #4CAF50`, nextState); return returnValue; }; } const reducer = function (state = { number: 0 }, action) { switch (action.type) { case 'add': return { number: state.number + action.number, }; default: return state; } }; const store = createStore( reducer, applyMiddleware(createLogger, createLogger2), ); store.subscribe(function () { console.log(1111); }); const { dispatch } = store; const App = () => { const handler = () => { dispatch({ type: 'add', number: 10 }); }; return ( <div> <button onClick={handler}>触发redux</button> </div> ); }; export default App;
store 的属性如下:
dispatch: ƒ dispatch(action)
getState: ƒ getState()
replaceReducer: ƒ replaceReducer(nextReducer)
subscribe: ƒ subscribe(listener)
Redux 的数据流是这样的:
界面 => action => reducer => store => react => virtual dom => 界面
将action对象转为一个带dispatch的方法
比如connect接收的mapDispatchToProps 是对象,会使用 bindActionCreators 处理; 接收 actionCreator 和 dispatch,返回一个函数;
function bindActionCreator(actionCreator, dispatch) { // 返回一个函数 return function() { return dispatch(actionCreator.apply(this, arguments)) } } function bindActionCreators(actionCreators, dispatch) { if (typeof actionCreators === 'function') { return bindActionCreator(actionCreators, dispatch) } const boundActionCreators = {} for (const key in actionCreators) { const actionCreator = actionCreators[key] if (typeof actionCreator === 'function') { boundActionCreators[key] = bindActionCreator(actionCreator, dispatch) } } return boundActionCreators } const mapDispatchToProps = { // actionCreators 这是个集合, onClick: (filter) => { type: 'SET_VISIBILITY_FILTER', filter: filter }; } 转换为: const mapDispatchToProps = { // actionCreators 这是个集合, onClick:function(filter) { return dispatch({ // dispatch 是闭包中的方法 type: 'SET_VISIBILITY_FILTER', filter: filter }) } }
函数套函数,compose(...chain)(store.dispatch)结果返回一个加强了的 dispatch;
这点和koa比较相似,这个 dispatch 在执行的时候会调用中间件。
function compose(...funcs) { if (funcs.length === 0) { return (arg) => arg; } if (funcs.length === 1) { return funcs[0]; } // 每一次reduce迭代都会返回一个加强版的dispatch return funcs.reduce( (a, b) => (...args) => a(b(...args)), ); }
加强版 dispatch(一个方法,接收 action 参数),在中间件中用 next 表示,执行 next 之后,会形成一个链条。
// 以createStore为参数 (createStore) => (...args) => {};