redux源码分析

142 阅读3分钟

redux

项目目录:image-20200513202554642

redux基本数据流:image-20200513204117899

createStore

createStore(reducer:any,preloadedState?:any,enhancer?:middleware),最终返回一个 state tree 实例。可以进行getStatesubscribe 监听和 dispatch 派发。(第二个参数可以传一个enhancer)

createStore 接收3个参数

  • reducer:给定当前state tree和要执行的action,返回下一个state tree
  • preloadedState,initial state tree
  • enhancer, 增强器,若干个中间件可以通过 redux自带的applymiddleware 产生一个增强器enhancer,多个增强器可以通过 compose 函数合并成一个增强器。

enhancer实际作用就是自定义一个dispatch覆盖createStore返回的dispatch

  const store = {
    dispatch,
    subscribe,
    getState,
    replaceReducer,
    [?observable]: observable
  }

image-20200520191841942

image-20200513211214234

getState

image-20200517145612215

subscribe

image-20200520193917711

这个函数实质的作用是确保可以改变nextListeners,如果nextListenerscurrentListeners一致的话,将currentListeners做一个拷贝赋值给nextListeners,然后所有的操作都会集中在nextListeners,比如我们看订阅的函数subscribe

image-20200522154148526

subscribe函数以及返回的unsubcibe函数都是在nextListeners中进行操作。

const store = createStore(reducers.todos)
const unsubscribe1 = store.subscribe(() => {
    const unsubscribe2 = store.subscribe(()=>{})
})

增加nextListener这个副本是为了避免在遍历listeners的过程中由于subscribe或者unsubscribe对listeners进行的修改而引起的某个listener被漏掉了。比如你在遍历到某个listener的时候,在这个listener中unsubscribe了一个在当前listener之前的listener,这个时候继续i ++的时候就会直接跳过当前listener的下一个listener,不知道有没有描述清楚

dispatch

  • 状态位控制流程,在reducer过程中不允许dispatch
  • 用快照的形式存储监听器列表,避免在监听器函数中调用subscribe函数引发的不可预期行为

image-20200517180537268

combineReducers

combineReducers用来将若干个reducer合并成一个reducers,使用方式:

combineReducers({
    key:(state = {}, action)=>{
        return state
    },
    post:(state = {}, action)=>{
        return state
    }
})

image-20200520201359607

applyMiddleware

  • 如何让中间件都可以获取到state
  • 如何让中间件可链式使用

compose

image-20200517211038451

const a = str => str + 'a'
const b = str => str + 'b'
const c = str => str + 'c'

const compose = (...funcs) => {
    return funcs.reduce((a,b)=>(...args)=>a(b(...args)))
}
compose(a,b,c)('初始参数')
// 约等于
a(b(c('初始参数')))// 初始参数cba

中间件的开发应该是这样的形式

// 返回的第一个函数是为了保证中间件可以取到全局状态,返回的第二个函数是为了保证中间件可以依次调用。
const someMidware = (store) => {
	return (next) => {
		return (action) => {
		// dosometing
    // next(action)
    // dosometing
		}
	}
}

源码:

 其中applyMiddleware形参和实参的对应关系是:

形参 实参
middlewares [middleware1,middleware2]
createStore Redux原生createStore
reducer, preloadedState, enhancer 原生createStore需要填入的参数
再看函数体:

image-20200519163525854

所以经过applyMiddleware改造后的dispatch,用一个例子

const mid1 = store => next => action => { console.log(store); next(action) }
const mid2 = store => next => action => { console.log(store); next(action) }
middlewares = [mid1, mid2]
enhancer = (...middlewares) => (createStore) => (reducer, preloadedState, enhancer) => {
    // 初始化store
    var store = createStore(reducer, preloadedState, enhancer)
    var chain = []
      // 暂存改造前的store
    var middlewareAPI = {
      getState: store.getState,
      dispatch: (action) => dispatch(action)
    }
    // 通过闭包,每个中间件得到的都是同一个store即middlewareAPI。这样就保证了数据的迭代变化
    chain = [mid1, mid2].map(middleware => middleware(middlewareAPI))
  	// chain = [mid1(middlewareAPI),mid2(middlewareAPI)]
    // middleware(middlewareAPI) 应用中间件,chain中的每个中间件就变成
    // 相当于 mid1 =(next)=>(action) =>{console.log(store); next(action)}
    // mid2 =(next)=>(action) =>{console.log(store); next(action)}

    dispatch = compose(...chain)(store.dispatch)
  // dispatch === mid1(mid2(store.dispatch))
 
    // 返回改造后的store
    return {
      ...store,
      dispatch
    }
}

// 所以经过applyMiddleware改造后,每次dispatch一个actio的流程是这样的
mid1(mid2(store.dispatch))(action)

redux-Space

image-20200520201318578

image-20200521195006467

image-20200521195532368