Redux源码分析
Hope is not lost today… it is found
-leia
redux的实现是非常简洁的,本文旨在通过对源码的解读和简易实现加深对code的理解。
![image-20210310235527758](http
源码的目录结构非常清晰,文件名即是API的名字。
createStore 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
通过函数签名可以看出createStore包含哪些信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 if (typeof preloadedState === 'function' && typeof enhancer === 'undefined' ) { enhancer = preloadedState preloadedState = undefined } if (typeof enhancer !== 'undefined' ) { if (typeof enhancer !== 'function' ) { throw new Error ('Expected the enhancer to be a function.' ) } return enhancer (createStore)(reducer, preloadedState) } if (typeof reducer !== 'function' ) { throw new Error ('Expected the reducer to be a function.' ) }
这一部分主要看对enhancer的设计
因为createStore创建store的时候主要有以下几种形式
createStore(reducer, initialState)
createStore(reducer)
createStore(reducer, enhancer)
createStore(reducer, initialState, enhancer)
在代码中都分别做了防御性的编写,进行了params参数的交接
compose 一个非常有趣的函数 用于把函数改造成 **a(b(c()))**嵌套调用的形式。
1 2 3 4 5 6 7 8 9 10 11 function compose (...funcs ) { if (funcs.length === 0 ) { return arg => arg } if (funcs.length === 1 ) { return funcs[0 ] } return funcs.reduce ((a, b ) => (...args ) => a (b (...args))) }
同理的还有pipe
1 2 3 function pipe (...funcs ) { return (args ) => funcs.reduce ((acc, cur ) => cur (acc), args); }
以下是我用ts实现的版本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 type IAction = { type: string | symbol, payload?: any } type IReducer<S extends object> = (state: S, action: IAction) => S; type ISyncAction<S extends object> = ( getState: Store<S>["getState"], dispatch: Store<S>["dispatch"]) => void; type IApplyMiddleware<S extends object> = (store: Store<S>) => Store<S> interface Store<S extends object> { getState: () => S; subscribe: (listener: Function) => () => void; dispatch(params: IAction | ISyncAction<S>): void; } const noop = () => { }; export function createStore<S extends object = {}> ( reducer: IReducer<S>, initialState?: S, applyMiddleware?: IApplyMiddleware<S> ): Store<S> { let subscriptions: Function[] = []; let onSubscribe: boolean = false; let state = initialState || {} as S; if (applyMiddleware) { return applyMiddleware(createStore(reducer, initialState)); } function getState() { return state; } function dispatch(params: IAction | ISyncAction<S>) { state = reducer(state, params as IAction); onSubscribe && subscriptions.forEach(f => f()); } function subscribe(listener: Function) { if (typeof listener !== "function") { console.error("you subscribe a function!"); return noop; } onSubscribe = true; subscriptions.push(listener); return function unsubscribe() { onSubscribe = false; subscriptions.length = 0 console.log('unsubscribe done!'); } } return { getState, dispatch, subscribe } } function compose(...fn) { if (fn.length === 0) { return args => args; } if (fn.length === 1) { return fn[0] } return fn.reduce((f1, f2) => (...args) => f1(f2(args))); } function applyMiddleware(...middlewares) { return store => { let dispatch = store.dispatch; let getState = store.getState; const p = {getState, dispatch} const middlewaresWithApi = middlewares.map(md => md(p)); dispatch = compose(middlewaresWithApi)(dispatch); return { ...store, dispatch } } } export function combineReducers<S extends object>(reducers) { const combinedReducerMap = {}; Object.keys(reducers).forEach(key => { if (typeof reducers[key] === "function") { combinedReducerMap[key] = reducers[key]; } }); return function(state = {}, action: IAction) { const nextState = {}; let hasChanged = false; Object.keys(combinedReducerMap).forEach(key => { const reducer = combinedReducerMap[key]; const preState = state[key]; const currentState = reducer(preState, action); if (typeof currentState === void 0) { console.error("key is not exist!"); } nextState[key] = currentState; hasChanged = hasChanged || currentState !== preState; }); return hasChanged ? nextState : state; }; }
react-redux redux是框架无关的一个库,要想在react中使用就必须借助于react-redux,而结合的关键就在于 react-redux的 connect的方法
1 2 3 4 5 /** * mapStateToProps(state, ownProps) : stateProps * mapDispatchToProps(dispatch, ownProps) : dispatchProps */ connect(mapStateToProps, mapDispatchTOProps, mergeProps, options)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 class Provider extends Component { constructor (props ) { super (props) const { store } = props this .notifySubscribers = this .notifySubscribers .bind (this ) const subscription = new Subscription (store)的subscrption对象上的更新函数 subscription.onStateChange = this .notifySubscribers this .state = { store, subscription } this .previousState = store.getState () } componentDidMount ( ) { this ._isMounted = true this .state .subscription .trySubscribe () if (this .previousState !== this .props .store .getState ()) { this .state .subscription .notifyNestedSubs () } } componentWillUnmount ( ) { if (this .unsubscribe ) this .unsubscribe () this .state .subscription .tryUnsubscribe () this ._isMounted = false } componentDidUpdate (prevProps ) { if (this .props .store !== prevProps.store ) { this .state .subscription .tryUnsubscribe () const subscription = new Subscription (this .props .store ) subscription.onStateChange = this .notifySubscribers this .setState ({ store : this .props .store , subscription }) } } notifySubscribers ( ) { this .state .subscription .notifyNestedSubs () } render ( ) { const Context = this .props .context || ReactReduxContext return ( <Context.Provider value ={this.state} > {this.props.children} </Context.Provider > ) } }
工作流程
get store
使用发布订阅,notify所有的subscriber
didmount阶段如果有更新重新赋值
检查store是否equal否则 重新2
传递context
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 subscribe (listener ) { let isSubscribed = true if (next === current) next = current.slice () next.push (listener) return function unsubscribe ( ) { if (!isSubscribed || current === CLEARED ) return isSubscribed = false if (next === current) next = current.slice () next.splice (next.indexOf (listener), 1 ) } } } trySubscribe ( ) { if (!this .unsubscribe ) { this .unsubscribe = this .parentSub ? this .parentSub .addNestedSub (this .handleChangeWrapper ) : this .store .subscribe (this .handleChangeWrapper ) this .listeners = createListenerCollection () } } function selector (stateOrDispatch, ownProps ) { return props }
总的来说: react-redux做的就是 将provider的value放入context,connect阶段取出store获取 mapStateToProps, mapDispatchTOProps,然后通过select create props传入组件并且通过一个subscripation 订阅当前state的dependency,通过react unstable_batchUpdate 来做差量的更新