一、Redux的性能是什么?
- 将状态对立放在一个state中,由store来治理这个state
- 这个store依照reducer的“shape”(形态)创立。
- reducer的作用是接管到action后,输入一个新的状态,对应地更新store上的状态。
- 依据redux的准则领导,内部扭转state的最佳形式是通过调用store的dispatch办法,触发一个action,这个action被对应的reducer解决,实现state更新。
- 能够通过subscribe在store上增加一个监听函数。每当调用dispatch办法时,会执行所有的监听函数。
- 能够增加中间件(middleware)解决副作用。
这个过程中,Redux须要提供的性能是:
- 创立store,即:createStore( )
- 创立进去的store提供subscript,dispatch,getState,replaceReducer这些办法
- 将多个reducer合并为一个reducer,即:combineReducers( )
- 利用中间件,即applyMiddleware( )
二、Redux源码构造
这就是Redux的源码构造:
这里有下面所说的applyMiddleware.ts、combineReducers.ts、createStore.ts,至于compose.ts和bindActionCreators.ts则是一些工具,前面会讲到。
1.compose.ts
其实说白了,compose( )这个函数的作用就是:
const func = [f1, f2, f3];compose(...func) //return f1(f2(f3(...args)))//留神函数调用的程序是从左到右,即:f1 -> f2 -> f3
上面是compose( )的源码:
export default function compose(...funcs: Function[]) { if (funcs.length === 0) { // infer the argument type so it is usable in inference down the line return <T>(arg: T) => arg } if (funcs.length === 1) { return funcs[0] } return funcs.reduce((a, b) => (...args: any) => a(b(...args)))}
其实换成这样更好了解:
export default function compose(...funcs) { if (funcs.length === 0) { return arg => arg } if (funcs.length === 1) { return funcs[0] } const last = funcs[funcs.length - 1] const rest = funcs.slice(0, -1) return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args))}
其实最要害就是这句话:
return (...args) => rest.reduceRight((composed, f) => f(composed), last(...args));
reduceRight( )就是reduce( )的倒序执行,也就是reduce( )是从左到右执行,reduceRight( )是从右到左执行。
2.createStore.ts
这个函数大抵是这样的:
function createStore(reducer, preloadedState, enhancer) { if (typeof enhancer !== 'undefined') { if (typeof enhancer !== 'function') { throw new Error('Expected the enhancer to be a function.') } return enhancer(createStore)( reducer, preloadedState as PreloadedState<S> ) as Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext } if (typeof reducer !== 'function') { throw new Error('Expected the reducer to be a function.') } let currentReducer = reducer // 以后store中的reducer let currentState = preloadedState // 以后store中存储的状态 let currentListeners = [] // 以后store中搁置的监听函数 let nextListeners = currentListeners // 下一次dispatch时的监听函数 // 留神:当咱们新增加一个监听函数时,只会在下一次dispatch的时候失效。 //... // 获取state function getState() { //... } // 增加一个监听函数,每当dispatch被调用的时候都会执行这个监听函数 function subscribe() { //... } // 触发了一个action,因而咱们调用reducer,失去的新的state,并且执行所有增加到store中的监听函数。 function dispatch() { //... } //... //dispatch一个用于初始化的action,相当于调用一次reducer //而后将reducer中的子reducer的初始值也获取到 //详见上面reducer的实现。 return { dispatch, subscribe, getState, //上面两个是次要面向库开发者的办法,临时先疏忽 //replaceReducer, //observable }}
能够看出,createStore办法创立了一个store,然而并没有间接将这个store的状态state返回,而是返回了一系列办法,内部能够通过这些办法(getState)获取state,或者间接地(通过调用dispatch)扭转state。
createStore总共接管三个参数:reducer, preloadState, enhancer
- reducer: 一个纯函数,接管上一个(或初始化)state和action,依据action的type返回新的state
- preloadState: 一个初始化的state,能够设置store中的默认值
- enhance: 增强器,用于拓展store的性能,个别是applyMiddleWare(...middleware)
三个要害函数:
1.getState
这个没啥好说的,就是返回currentState
function getState(): S { if (isDispatching) { //当reducer执行中的时候不能应用getState() //state是通过一层一层的reducer传递下来的,每执行一次reducer state就会扭转,不能间接从store中读取 throw new Error( 'You may not call store.getState() while the reducer is executing. ' + 'The reducer has already received the state as an argument. ' + 'Pass it down from the top reducer instead of reading it from the store.' ) } return currentState as S }
这里须要留神的是,当reducer执行中不能应用getState( ),因为state是通过一层一层的reducer传递下来的,每执行一次reducer, state就会扭转,不能间接从store中读取。
2.subscript
//subscribe(listener)函数源码,返回一个勾销订阅的函数,//let unsubscribe = subscribe(listener); //给listener函数增加了订阅//unsubcribe() //给listener函数勾销订阅//实际上就是保护nextListeners数组的过程,订阅就push,勾销订阅就splice(index, 1);function subscribe(listener: () => void) { if (typeof listener !== 'function') { throw new Error('Expected the listener to be a function.') } if (isDispatching) { throw new Error( 'You may not call store.subscribe() while the reducer is executing. ' + 'If you would like to be notified after the store has been updated, subscribe from a ' + 'component and invoke store.getState() in the callback to access the latest state. ' + 'See https://redux.js.org/api/store#subscribelistener for more details.' ) } let isSubscribed = true //回调函数被订阅默认的标记 ensureCanMutateNextListeners() nextListeners.push(listener) //新增订阅在nextListeners中操作 //返回一个勾销订阅的函数 return function unsubscribe() { if (!isSubscribed) { //如果回调函数曾经被勾销订阅,则不须要反复勾销订阅 return } if (isDispatching) { throw new Error( 'You may not unsubscribe from a store listener while the reducer is executing. ' + 'See https://redux.js.org/api/store#subscribelistener for more details.' ) } isSubscribed = false //勾销订阅,这里将回调函数被订阅的标记设置为false ensureCanMutateNextListeners() const index = nextListeners.indexOf(listener) //因为listener曾经被勾销订阅了,所以在nextListeners将listener删除掉 nextListeners.splice(index, 1) currentListeners = null }}
(1)首先看传入的参数,是一个listener的回调函数,当reducer执行结束之后,会执行这个回调。
这个是官网的解释:
listener (_Function_): The callback to be invoked any time an action has been dispatched, and the state tree might have changed. You may call getState() inside this callback to read the current state tree. It is reasonable to expect that the store's reducer is a pure function, so you may compare references to some deep path in the state tree to learn whether its value has changed.
简略解释一下,就是当dispatch(action)实现的时候(应该是reduce执行实现的时候)会去调用listener这个回调函数,状态树可能会扭转。能够在listener外面应用getState( )获取到最新的状态。能够通过比拟状态树中的一些深层门路的援用去判断状态的值是否产生扭转。
(2)如果reducer正在执行,是不能调用subscribe和unsubscribe的
if (isDispatching) { throw new Error( 'You may not call store.subscribe() while the reducer is executing. ' + 'If you would like to be notified after the store has been updated, subscribe from a ' + 'component and invoke store.getState() in the callback to access the latest state. ' + 'See https://redux.js.org/api/store#subscribelistener for more details.' )}if (isDispatching) { throw new Error( 'You may not unsubscribe from a store listener while the reducer is executing. ' + 'See https://redux.js.org/api/store#subscribelistener for more details.' )}
我的了解是:因为dispatch函数会在reducer执行结束后循环执行listeners数组内订阅的更新函数,所以要保障这个时候的listener数组不变,既不能增加更新函数(subscribe)也不能删除更新函数(unsubscribe)。
(3)返回一个勾销订阅的函数
//返回一个勾销订阅的函数return function unsubscribe() { if (!isSubscribed) { return } //如果正在执行reducer则不能执行unsubscribe函数 if (isDispatching) { throw new Error( 'You may not unsubscribe from a store listener while the reducer is executing. ' + 'See https://redux.js.org/api/store#subscribelistener for more details.' ) } isSubscribed = false ensureCanMutateNextListeners() const index = nextListeners.indexOf(listener) //因为listener曾经被勾销订阅了,所以在nextListeners将listener删除掉 nextListeners.splice(index, 1) currentListeners = null}
(4)我对subscribe函数的了解:实际上就是保护nextListeners数组的过程,订阅就push,勾销订阅就splice(index, 1);等到dispatch函数在reducer执行结束之后会循环执行listeners数组内订阅的更新函数。
相应的代码如下:
function ensureCanMutateNextListeners() { if (nextListeners === currentListeners) { nextListeners = currentListeners.slice() }}//新增订阅ensureCanMutateNextListeners()nextListeners.push(listener) //新增订阅在nextListeners中操作//勾销订阅ensureCanMutateNextListeners()const index = nextListeners.indexOf(listener)//因为listener曾经被勾销订阅了,所以在nextListeners将listener删除掉nextListeners.splice(index, 1)currentListeners = null
可见,新增订阅和勾销订阅都是在nextListener外面操作,不会间接在currentListener外面操作。
咱们会发现,在每次新增订阅和勾销订阅的时候,都会先调用一个ensureCanMutateNextListeners函数。这个函数的语义就是确保能够转变nextListeners。顾名思义,这个函数本质的作用是确保能够扭转nextListeners,如果nextListeners与currentListeners统一的话,将currentListeners做一个拷贝赋值给nextListeners,而后所有的操作都会集中在nextListeners。
3.dispatch
function dispatch(action: A) { //action不是原生的对象,报错 if (!isPlainObject(action)) { throw new Error( 'Actions must be plain objects. ' + 'Use custom middleware for async actions.' ) } //对于一个action来说,action中的type是必须的 if (typeof action.type === 'undefined') { throw new Error( 'Actions may not have an undefined "type" property. ' + 'Have you misspelled a constant?' ) } //reducer正在执行的时候不能调用 if (isDispatching) { throw new Error('Reducers may not dispatch actions.') } try { isDispatching = true //currentState与action会流通到所有的reducer //所有的reducer的返回值整合后,替换掉以后的currentState currentState = currentReducer(currentState, action) //这句话就是触发相应的reducer } finally { isDispatching = false } //最初一一触发回调函数,回调函数的执行是在dispatch外面 const listeners = (currentListeners = nextListeners) for (let i = 0; i < listeners.length; i++) { const listener = listeners[i] listener() } //为了不便链式调用,dispatch执行结束后,返回action return action}
(1)传入的参数:一个action
(2)接下来就是一系列的判断检测,有三点要求:
- action必须得是原生的对象
- 对于一个action来说,action中的type是必须的
- reducer正在执行的时候不能调用dispatch
相应的代码如下:
//action不是原生的对象,报错if (!isPlainObject(action)) { throw new Error( 'Actions must be plain objects. ' + 'Use custom middleware for async actions.' )}//对于一个action来说,action中的type是必须的if (typeof action.type === 'undefined') { throw new Error( 'Actions may not have an undefined "type" property. ' + 'Have you misspelled a constant?' )}//reducer正在执行的时候不能调用if (isDispatching) { throw new Error('Reducers may not dispatch actions.')}
(3)要害局部代码:
try { isDispatching = true //currentState与action会流通到所有的reducer //所有的reducer的返回值整合后,替换掉以后的currentState currentState = currentReducer(currentState, action) //这句话就是触发相应的reducer} finally { isDispatching = false}//最初一一触发回调函数,回调函数的执行是在dispatch外面const listeners = (currentListeners = nextListeners)for (let i = 0; i < listeners.length; i++) { const listener = listeners[i] listener()}//为了不便链式调用,dispatch执行结束后,返回actionreturn action
由下面的dispatch函数可见,先执行currentReducer,再别离执行listeners外面的回调。
//先执行currentReducercurrentState = currentReducer(currentState, action);//...//将nextListener赋值给currentListener,为的是后续的dispatch执行回调函数(listener)过程中,//能够将这一轮dispatch新增的回调函数(newListener)执行const listeners = (currentListeners = nextListeners);//最初一一触发回调函数,回调函数的执行是在dispatch外面for (let i = 0; i < listeners.length; i++) { const listener = listeners[i] listener()}
(4)总之,dispatch函数就干了两件事,执行了相应的reducer,执行了currentListener注册的回调函数(listener)。
3.combineReducers.ts
(1)外围局部源码加解释:
export default function combineReducers(reducers: ReducersMapObject) { //获取到所有的reducer的名字,组成数组 const reducerKeys = Object.keys(reducers) //这个finalReducers是最终无效的reducers const finalReducers: ReducersMapObject = {} for (let i = 0; i < reducerKeys.length; i++) { const key = reducerKeys[i] if (process.env.NODE_ENV !== 'production') { //不是生产环境且存在对应key的reducer if (typeof reducers[key] === 'undefined') { warning(`No reducer provided for key "${key}"`) } } if (typeof reducers[key] === 'function') { finalReducers[key] = reducers[key] } } //做了一系列的判断之后失去了最终的finalReducerKeys数组 const finalReducerKeys = Object.keys(finalReducers) // This is used to make sure we don't warn about the same // keys multiple times. let unexpectedKeyCache: { [key: string]: true } if (process.env.NODE_ENV !== 'production') { unexpectedKeyCache = {} } let shapeAssertionError: Error //assertReducerShape用来查看每个reducer有没有默认返回的state try { assertReducerShape(finalReducers) } catch (e) { shapeAssertionError = e } return function combination( state: StateFromReducersMapObject<typeof reducers> = {}, action: AnyAction ) { if (shapeAssertionError) { throw shapeAssertionError } if (process.env.NODE_ENV !== 'production') { const warningMessage = getUnexpectedStateShapeWarningMessage( state, finalReducers, action, unexpectedKeyCache ) if (warningMessage) { warning(warningMessage) } } let hasChanged = false //nextState寄存所有的state const nextState: StateFromReducersMapObject<typeof reducers> = {} for (let i = 0; i < finalReducerKeys.length; i++) { //获取每个reducer的名字 const key = finalReducerKeys[i] //获取每个reducer const reducer = finalReducers[key] //获取每个reducer对应的旧状态 const previousStateForKey = state[key] //调用该reducer,依据这个reducer的旧状态和以后action来生成新的state const nextStateForKey = reducer(previousStateForKey, action) //执行各子reducer以获取子nextState if (typeof nextStateForKey === 'undefined') { const errorMessage = getUndefinedStateErrorMessage(key, action) throw new Error(errorMessage) } //以各自的reducerName为键,新生成的state为值,生成最终的state object。将子nextState挂载到对应的键名,将所有的子state合并到一个对象中 nextState[key] = nextStateForKey //比拟通过reducer之后的下一个状态和上一个状态是否雷同判断是否hasChanged,只有有一个子state产生扭转,整个state就会产生扭转 hasChanged = hasChanged || nextStateForKey !== previousStateForKey } hasChanged = hasChanged || finalReducerKeys.length !== Object.keys(state).length //变动了,返回新的state,否则,返回旧的state return hasChanged ? nextState : state //combineReducers返回的是下一个状态的汇合(如果状态扭转的话) }}
(1)传入的参数:reducers:是一个由泛滥的reducer组成的对象
(2)这个函数的大体思路就是:取到所有的reducers的key值 -> 通过筛选得出最终的reducers的key值组成的数组finalReducerKeys和所有reducer组成的数组finalReducers -> 应用finalReducers外面的reducer去操作对应的state -> 比拟reducer前后的state是否发生变化 -> 如果state产生了扭转,返回一个由reducer名字作为键名,reducer之后的nextStateForKey作为value的对象。
4.applyMiddleware.ts
redux本来的dispatch办法只能承受一个对象作为action
用户操作 -> dispatch(action) -> reducer(prevState, action) -> 新的state -> 界面
如果页面要发送申请数据,而且要把数据放在action外面,最终通过reducer的解决,放到store中。这时就须要用到middleware了。
用户操作 -> dispatch(action) -> middleware(action) -> 真正的action -> reducer(prevState, action) -> 新的state -> 界面
这里是在dispatch(action)之后增加了中间件解决,将数据放在action中,返回一个新的action。
applyMiddleware大抵会这样应用:
const store = createStore(rootReducer, applyMidddleWare(thunk))
applyMiddleWare(thunk)就相当于一个enhancer,负责拓展redux,说白了就是拓展store的dispatch办法。这个又怎么解释呢?到底是通过什么样的形式去拓展dispatch呢?
上面我应用一个革新版的applyMiddleware办法和redux-thunk的外围代码createThunkMiddleware一起来形容一下是如何实现dispatch的革新的
(1)redux-thunk的外围代码:createThunkMiddleware
//其实这是一个middlewarefunction createThunkMiddleware(extraArgument) { return ({ dispatch, getState }) => next => action => { if (typeof action === 'function') { return action(dispatch, getState, extraArgument); } //next是被thunk革新之前的dispatch,也就是说有可能是最原始的dispatch,也有可能是被其余的中间件革新过的dispatch return next(action); };}//thunk是真正的中间件函数const thunk = createThunkMiddleware();//将函数柯里化写成嵌套的模式function createThunkMiddleware(extraArgument) { //真正的中间件函数 return function({dispatch, getState}) { //革新dispatch的函数,这里的next是内部传进来的dispatch,可能是被其余的中间件解决过的,也可能是最本来的 return function(next) { //返回一个新革新的dispatch函数 return function(action) { //如果action是函数,那么执行它,并且将store的dispatch和getState传入, //便于咱们dispatch的函数外部逻辑执行完之后dispatch真正的action if(typeOf action === 'function') { return action(dispatch, getState, extraArgument); } //如果是一个一般的action(也就是对象),间接dispatch return next(action); } } }}const thunk = createThunkMiddleware();
- 真正调用的时候,实践上是这样:thunk({ dispatch, getState })(next)(action),其中,thunk({ dispatch, getState})(next)这部分,返回的是一个承受参数为action的革新过后dispatch函数,而这部分会在applyMiddleware中去调用。
- next则是被以后中间件革新之前的dispatch。能够是最原始的dispatch,也有可能是被其余中间件革新过的dispatch。
(2)再看一下简易版的applyMiddleware办法:
function applyMiddleware(store, middlewares) { middlewares = middlewares.slice() middlewares.reverse() let dispatch = store.dispatch //感觉这里十分奇妙,相当于是链式调用了middleware函数,返回的dispatch是通过所有的中间件革新过后的dispatch办法 //相似于这样dispatch = middleware2(store)(middleware1(store)(store.dispatch)) middlewares.forEach(middleware => (dispatch = middleware(store)(dispatch))) //返回一个dispatch更新的store return Object.assign({}, store, { dispatch })}
这与 Redux 中 applyMiddleware() 的实现曾经很靠近了,然而有三个重要的不同之处:
- 它只裸露一个 store API 的子集给 middleware:dispatch(action) 和 getState();
- 它用了一个十分奇妙的形式,以确保如果你在 middleware 中调用的是 store.dispatch(action) 而不是 next(action),那么这个操作会再次遍历蕴含以后 middleware 在内的整个 middleware 链。这对异步的 middleware 十分有用;
- 为了保障你只能利用 middleware 一次,它作用在 createStore() 上而不是 store 自身。因而它的签名不是 (store, middlewares) => store, 而是 (...middlewares) => (createStore) => createStore。
(3)最初回归到applyMiddleware的源码
export default function applyMiddleware( ...middlewares: Middleware[]): StoreEnhancer<any> { return (createStore: StoreCreator) => <S, A extends AnyAction>( reducer: Reducer<S, A>, ...args: any[] ) => { const store = createStore(reducer, ...args) let dispatch: Dispatch = () => { throw new Error( 'Dispatching while constructing your middleware is not allowed. ' + 'Other middleware would not be applied to this dispatch.' ) } const middlewareAPI: MiddlewareAPI = { getState: store.getState, dispatch: (action, ...args) => dispatch(action, ...args) } //假如咱们只是用了redux-thunk,那么此时的middleware就相当于thunk,就是function({dispatch, getState}) const chain = middlewares.map(middleware => middleware(middlewareAPI)) //这里的compose的作用就是,将所有的中间件函数串联起来,中间件1完结,作为参数传入中间件2,被它解决, //以此类推,最终返回的是被所有的中间件解决完的函数,最开始承受store.dispatch为参数,层层革新后被赋值到新的dispatch变量中 dispatch = compose<typeof dispatch>(...chain)(store.dispatch) //返回的是一个将通过中间件革新的dispatch笼罩原有的dispatch的store对象 return { ...store, dispatch } }}
这里要留神的点是:
- 假如咱们只是用了一个middleware(redux-thunk),就能够临时抛开compose,那么这里的逻辑就相当于
dispatch = thunk(middlewareAPI)(store.dispatch);
在这里,thunk(middlewareAPI)其实返回的是一个革新dispatch的函数,thunk(middlewareAPI)(store.dispatch)返回的就是被革新后的dispatch。
- 加上compose也很好了解,假如有三个middleware,别离命名为middleware1,middleware2, middleware3,其实:dispatch = compose<typeof dispatch>(...chain)(store.dispatch)执行的过程如下:
//先转化为jsdispatch = compose(...chain)(store.dispatch);//假如middlewares为[middleware1, middleware2, middleware3],则chains为://[middleware1(getState, dispatch), middleware2(getState, dispatch), middleware3(getState, dispatch)]//则下面这句代码其实是这样执行//chains简化为:[chains1, chains2, chain3]chain1(chain2(chain3(store.dispatch))) //返回的是通过chain3, chain2, chain1解决过的dispatch函数
返回的是通过chain3, chain2, chain1解决过的dispatch函数。
所以,这里就将store的dispatch办法革新实现了,最初用革新好的dispatch笼罩原来store中的dispatch。
总结一下middleware和applyMiddleware的关系:
- 中间件(middleware)会帮忙咱们革新原来store的dispatch办法;
- applyMiddleware会将多个middleware串联起来,链式调用,返回一个通过泛滥middleware革新过的最终的dispatch办法。最初将这个革新好的dispatch利用到store上(相当于是将原来的dispatch替换为革新好的dispatch),返回这个更新的store对象。
5.bindActionCreators.ts
这个API没啥好讲的,其实也就是做了一件事:就是给每一个ActionCreator加上了dispatch,就是这样:dispatch(ActionCreator(xxx))
外围局部源码:
const boundActionCreators: ActionCreatorsMapObject = {} for (const key in actionCreators) { const actionCreator = actionCreators[key] if (typeof actionCreator === 'function') { boundActionCreators[key] = bindActionCreator(actionCreator, dispatch) //为actioncreator一一加装上dispatch技能 //调用:actionCreators.addTodo(content) 相当于 dispatch(addTodo); } }return boundActionCreators
应用
bindActionCreator(actionCreators, dispatch) //actionCreators是一个蕴含actionCreator的对象,dispatch是指store.dispatch
- 参数:
actionCreators (_Function_ or _Object_): 一个 action creator,或者一个 value 是 action creator 的对象。
dispatch (_Function_): 一个由 Store 实例提供的 dispatch 函数。
- 返回值:
(_Function_ or _Object_): 一个与原对象相似的对象,只不过这个对象的 value 都是会间接 dispatch 原 action creator 返回的后果的函数。如果传入一个独自的函数作为 actionCreators,那么返回的后果也是一个独自的函数。
三、redux和flux的区别
redux:
次要解决了组件间状态共享的问题,原理是集中式治理,次要有三个外围办法:action,store,reducer。工作流程是view调用store的dispatch接管action传入store,reducer进行state操作,view通过store提供的getState获取最新的数据。
flux:
flux 也是用来进行数据操作的,有四个组成部分 action,dispatch,view,store,工作流程是 view 收回一个 action,派发器接管 action,让 store 进行数据更新,更新实现当前 store 收回 change,view 承受 change 更新视图。
二者区别:
次要区别在于flux有多个能够扭转利用状态的store,在Flux中dispatcher被用来传递数据到注册的回调事件,然而在redux中只能定义一个可更新状态的store,redux把store和dispatcher合并,构造更加简略清晰。
三、总结
我对redux的了解:redux就是创立一个store来治理所有状态,触发action来扭转store。