redux 源码浅析

redux 版本号: "redux": "4.0.5"

redux 作为一个非常罕用的状态容器库, 大家都应该见识过, 他很玲珑, 只有 2kb, 然而宝贵的是他的 reducerdispatch 这种思维形式

在浏览此文之前, 先理解/应用 redux 相干知识点, 能力更好地浏览本文

入口文件

入口是在 redux/src/index.js 中, 在入口文件中只做了一件事件, 就是引入文件, 集中导出
当初咱们依据他导出的办法, 来进行剖析

createStore

这个是 redux 最次要的 API

应用

搭配这应用办法一起, 能够更好的浏览源码

createStore(reducer, [preloadedState], [enhancer])

他的次要性能就是创立一个 store, 将 reducer 转换到 store

参数

一共可承受三个参数:

  1. reducer (函数): 一个返回下一个状态树的还原函数,给定以后状态树和一个要解决的动作。
  2. [preloadedState] (任意值): 初始值, 能够是来自于 storage 中的; 如果你用combinedReducers产生了reducer,这必须是一个一般对象,其类型与传递给它的键雷同。
    也能够自在地传递任何你的reducer可能了解的货色。
  3. [enhancer] (函数): store 的增强器, 能够选择性的加强, 用代码来说就是 enhancer(createStore)(reducer, preloadedState), enhancer
    承受的参数就是 createStore, 同样地他也须要 return 一个相似于 createStore 的后果, 也就是说, 只有咱们返回的是 一个像 createStore 的货色,
    他的具体实现咱们就能够有很多微调 这里附上一篇探讨 enhancerapplyMiddleware 的文章 https://juejin.cn/post/684490...
// 简略的例子:function counterReducer(state, action) {    switch (action.type) {        case 'counter/incremented':            return {value: state.value + 1}        case 'counter/decremented':            return {value: state.value - 1}        default:            return state    }}let store = createStore(counterReducer, {    value: 12345})

store

createStore 返回的当然是一个 store, 他有本人的 api

getState

返回应用程序的以后状态树

const state = store.getState()

dispatch(action)

这个其实不必我多说, 会 redux 的都应该晓得这个

store.dispatch({type: 'counter/incremented'})

subscribe(listener)

增加一个监听器, 每当 action dispatch 的时候, 都会调用 listener, 在 listener 中能够应用 getState 来获取以后的状态树

const unsubscribe = store.subscribe(() => {    console.log('listener run')    const current = store.getState()    if (current.value === 12350) {        unsubscribe()    }})

展现一个场景, 监听事件, 当达到某个条件之后, 解除监听事件

replaceReducer(nextReducer)

应用一个 reducer 替换以后的 reducer,对于 reducers 实现动静加载,也能够为 Redux 实现热重载机制

源码解析

createStore 文件是在 redux/src/createStore.js 中, 他承受的参数就是下面咱们说的那三个, 返回的也就是 store

首先是一段参数的判断, 以及 enhancer 的返回

// 为了适配 createStore(reducer, enhancer) 的状况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.')    }    // enhancer 的应用场景    return enhancer(createStore)(reducer, preloadedState)}

接下来定义一些变量和函数

let currentReducer = reducerlet currentState = preloadedStatelet currentListeners = []let nextListeners = currentListenerslet isDispatching = false// 如果相等 , 做了一层浅拷贝  将 currentListeners 同步到 nextListeners 中// 防止相互影响function ensureCanMutateNextListeners() {    if (nextListeners === currentListeners) {        nextListeners = currentListeners.slice()    }}

store.getState

function getState() {    // isDispatching 默认为 false, 示意以后 store 是否正在 dispatch    if (isDispatching) {        throw new Error('//...')    }    // 间接返回以后 state , 默认为入参 preloadedState    return currentState}

store.subscribe

// 疏忽了错误判断function subscribe(listener) {    let isSubscribed = true    // 同步 nextListeners , currentListeners    ensureCanMutateNextListeners()    // 将 listener 退出 nextListeners     nextListeners.push(listener)    // 返回解除监听函数    return function unsubscribe() {        if (!isSubscribed) {            // 如果 isSubscribed 曾经为 false 了 则 return            // 状况 1, 曾经执行过unsubscribe了一次            return        }        // flag        isSubscribed = false        // 同步 nextListeners , currentListeners        ensureCanMutateNextListeners()        const index = nextListeners.indexOf(listener)        nextListeners.splice(index, 1)        // 搜寻 监听器, 删除        currentListeners = null    }}

store.dispatch

  function dispatch(action) {    // 省略了 action 的 谬误抛出    // 总结:  action  必须是一个 Object  且 action.type 必须有值存在    // 如果以后正在 isDispatching 则抛出 谬误(一般来说不存在    try {        isDispatching = true        // 执行 reducer, 须要留神的是 currentReducer 不能为异步函数        currentState = currentReducer(currentState, action)    } finally {        isDispatching = false    }    //  将 nextListeners 赋值给 currentListeners 执行 nextListeners 外面的监听器    const listeners = (currentListeners = nextListeners)    for (let i = 0; i < listeners.length; i++) {        const listener = listeners[i]        listener()    }    // 返回 action    return action}

store.replaceReducer

function replaceReducer(nextReducer) {    // 如果 nextReducer 不是函数则抛出谬误    // 间接替换    currentReducer = nextReducer    // 相似 ActionTypes.INIT.  替换值    dispatch({type: ActionTypes.REPLACE})}

store.observable

还有一个额定的 observable 对象:

// 一个 Symbol.observable 的 polyfillimport $$observable from 'symbol-observable'function observable() {    // subscribe 就是 store.subscribe    const outerSubscribe = subscribe    return {        subscribe(observer) {            // 如果 observer 不是对象或者为 null 则抛出谬误            function observeState() {                if (observer.next) {                    // next 的入参为 当然 reducer 的值                    observer.next(getState())                }            }            observeState()            // 增加了监听            const unsubscribe = outerSubscribe(observeState)            return {unsubscribe}        },        // 获取到以后 对象, $$observable 值是一个 symbol        [$$observable]() {            return this        }    }}

这里应用了 tc39 里未上线的规范代码 Symbol.observable, 如果你应用或者理解过 rxjs, 那么这个对于你来说就是很简略的, 如果不相熟,
能够看看这篇文章: https://juejin.cn/post/684490...

残余代码

function createStore() {    // 省略    // 初始化了下值    dispatch({type: ActionTypes.INIT})    // 返回    return {        dispatch,        subscribe,        getState,        replaceReducer,        [$$observable]: observable    }}

combineReducers

应用

// 能够承受多个 reducer, 实现一种 module 的性能rootReducer = combineReducers({potato: potatoReducer, tomato: tomatoReducer})// 返回值{    potato: {        // 某些属性    },    tomato: {        // 某些属性    }}const store = createStore(rootReducer, {    potato: {        // 初始值    }})

有一点须要留神的是, reducer 都是须要默认值的,如:

function counterReducer(state = {value: 0}, action) {    //...}

源码解析

combineReducers

先看 combineReducers 执行之后产生了什么

function combineReducers(reducers) {    // 第一步是获取 key, 他是一个数组    const reducerKeys = Object.keys(reducers)    const finalReducers = {}    // 遍历 reducers, 赋值到 finalReducers 中, 确保 reducer 是一个函数, 不是函数则过滤    for (let i = 0; i < reducerKeys.length; i++) {        const key = reducerKeys[i]        // 省略 reducers[key] 如果是 undefined 抛出谬误        if (typeof reducers[key] === 'function') {            finalReducers[key] = reducers[key]        }    }    // finalReducerKeys 一般来说是和 reducerKeys 雷同的    const finalReducerKeys = Object.keys(finalReducers)    //定义了两个遍历    let unexpectedKeyCache    let shapeAssertionError    try {        // 此函数前面会具体讲述        // 答题作用就是确认 finalReducers 中都是有初始值的        assertReducerShape(finalReducers)    } catch (e) {        shapeAssertionError = e    }    //...}

再看他又返回了什么(记住后果必然也是一个 reducer)

function combineReducers(reducers) {    //...    return function combination(state = {}, action) {        // 如果 assertReducerShape 出错则抛出谬误        if (shapeAssertionError) {            throw shapeAssertionError        }        // 疏忽非 production 代码        // 事后定义一些变量        let hasChanged = false        const nextState = {}        // 循环 finalReducerKeys         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) // 通过 reducer 再次生成值            // 如果 nextStateForKey === undefined 则再次抛出异样            // 给 nextState 赋值            nextState[key] = nextStateForKey            // 判断是否扭转 (初始值是 false)  判断简略的应用 !== 来比拟            // 如果曾经为 true   就始终为 true 了            hasChanged = hasChanged || nextStateForKey !== previousStateForKey        }        // 循环后再次对 true 做出判断        // 是否少了 reducer 而造成误判        hasChanged =            hasChanged || finalReducerKeys.length !== Object.keys(state).length        // 如果扭转了 返回新值, 否则返回旧值        return hasChanged ? nextState : state    }}

combineReducers 根本就是上述两个函数的联合, 通过循环遍历所有的 reducer 计算出值

assertReducerShape

function assertReducerShape(reducers) {    Object.keys(reducers).forEach(key => {        // 遍历参数里的 reducer        const reducer = reducers[key]        //执行初始操作 产生初始值都有初始值        const initialState = reducer(undefined, {type: ActionTypes.INIT})        //...   如果 initialState 是 undefined 则抛出谬误        // 如果 reducer 执行未知操作  返回的是 undefined 则抛出谬误        // 情景: 以后 reducer 应用了 ActionTypes.INIT 来产生值, 这可能通过上一步        // 但在这一步就会被检测进去        if (            typeof reducer(undefined, {                type: ActionTypes.PROBE_UNKNOWN_ACTION()            }) === 'undefined'        ) {            //... 抛出谬误        }    })}

这里咱们能够晓得一点, 所有 reducer 咱们都必须要有一个初始值, 而且他不能是 undefined, 能够是 null

compose

这里须要先讲 compose 的应用 能力顺带过渡到上面

应用

就如他的名字, 是用来组合函数的, 承受刀哥函数, 返回执行的最终函数:

// 这里罕用来 链接多个中间件const store = createStore(    reducer,    compose(applyMiddleware(thunk), DevTools.instrument()))

源码解析

他的源码也很简略:

function compose(...funcs) {    if (funcs.length === 0) {        return arg => arg    }    if (funcs.length === 1) {        return funcs[0]    }    // 下面都是 管制, 参数数量为 0 和 1 的状况    // 这里是重点, 将循环接管到的函数数组    return funcs.reduce((a, b) => (...args) => a(b(...args)))}

咱们将 reduce 的运行再度装璜下:

// reduce 中没有初始值的时候, 第一个 `prevValue` 是取  `funcs[0]` 的值funcs.reduce((prevValue, currentFunc) => (...args) => prevValue(currentFunc(...args)))

reducer 返回的就是 这样一个函数 (...args) => prevValue(currentFunc(...args)), 一层一层嵌套成一个函数

举一个简略的输出例子:

var foo = compose(val => val + 10, () => 1)

foo 打印:

(...args) => a(b(...args))

执行 foo() , 返回 11

applyMiddleware

应用

applyMiddleware 是应用在 createStore 中的 enhancer 参数来加强 redux 的作用

可兼容多种三方插件, 例如 redux-thunk, redux-promise, redux-saga 等等

这里应用官网的一个例子作为展现:

function logger({getState}) {    // next 就是真正的 store.dispatch    return next => action => {        console.log('will dispatch', action)        const returnValue = next(action)        console.log('state after dispatch', getState())        return returnValue    }}const store = createStore(rootReducer, {    counter: {value: 12345}}, applyMiddleware(logger))

源码解析

defaultfunction applyMiddleware(...middlewares) {    return createStore => (...args) => {        // 因为应用了 enhancer 参数, 他的外部没有 createStore 的货色, 所以这里须要从新 createStore        const store = createStore(...args)        let dispatch = () => {            // 在中间件中 不容许应用 dispatch            throw new Error(                // 省略报错...            )        }        // 这是要传递的参数        const middlewareAPI = {            getState: store.getState,            dispatch: (...args) => dispatch(...args)        }        // 从新 map 所有 middlewares 返回须要的后果        const chain = middlewares.map(middleware => middleware(middlewareAPI))        // 这里就是咱们下面的 compose 相干的代码, 返回的后果 再次执行 失去真正的 dispatch        dispatch = compose(...chain)(store.dispatch)        // 返回 store 和 dispatch        return {            ...store,            dispatch        }    }}

这里咱们须要从新捋一捋函数的执行, 中间件以上述的 logger 为例子

applyMiddleware(logger) -> 返回的是一个函数(createStore) => (...args) => {/*省略*/} 我把他记为中间件函数 1
也就是说 applyMiddleware(logger) === (createStore) => (...args) => {/*省略*/}

这个函数将在 createStore 中应用 enhancer(createStore)(reducer, preloadedState) 这里的 enhancer 就是中间件函数 1 通过 createStore
的执行咱们能够发现
store === createStore(reducer, preloadedState, enhancer) === enhancer(createStore)(reducer, preloadedState)
=== applyMiddleware(logger)(createStore)(reducer, preloadedState)
=== ((createStore) => (...args) => {/*省略*/})(createStore)(reducer, preloadedState)
=== 中间件函数 1 中的{/*省略*/} 返回后果 通过这一层的推论咱们能够得出 store === 中间件函数 1中的 {/*省略*/} 返回后果

bindActionCreators

应用

这个 API 次要是用来不便 dispatch 的 他承受 2 个参数 , 第一个是对象或函数, 第二个就是 dispatch 返回值的类型很第一个参数雷同

首先咱们要定义创立 action 的函数

function increment(value) {    return {        type: 'counter/incremented',        payload: value    }}function decrement(value) {    return {        type: 'counter/decremented',        payload: value    }}

应用状况 1:

function App(props) {    const {dispatch} = props    // 因为在 hooks 中应用 加上了 useMemo    const fn = useMemo(() => bindActionCreators(increment, dispatch), [dispatch])    return (        <div className="App">            <div>                val: {props.value}            </div>            <button onClick={() => {                fn(100)            }}>plus            </button>        </div>    );}

应用状况 2:

function App(props) {    const {dispatch} = props    const fn = useMemo(() => bindActionCreators({        increment,        decrement    }, dispatch), [dispatch])    // 如果想用 decrement 也是这样调用 fn.decrement(100)    return (        <div className="App">            <div>                val: {props.value}            </div>            <button onClick={() => {                fn.increment(100)            }}>plus            </button>        </div>    );}

源码解析

function bindActionCreator(actionCreator, dispatch) {    return function () {        // 执行 dispatch(actionCreator()) === dispatch({type:''})         return dispatch(actionCreator.apply(this, arguments))    }}function bindActionCreators(actionCreators, dispatch) {    if (typeof actionCreators === 'function') {        // 如果是函数间接执行 bindActionCreator        return bindActionCreator(actionCreators, dispatch)    }    if (typeof actionCreators !== 'object' || actionCreators === null) {        throw new Error(/*省略*/)    }    // 定义变量    const boundActionCreators = {}        // 因为是对象 循环遍历, 然而 for in 效率太差    for (const key in actionCreators) {        const actionCreator = actionCreators[key]              // 过滤        if (typeof actionCreator === 'function') {            // 和函数同样 执行 bindActionCreator 并且赋值到 boundActionCreators 中            boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)        }    }    return boundActionCreators}

bindActionCreators 的源码绝对简略一点, 了解起来绝对也容易很多

总结

redux 中设计的很多中央都是很奇妙的,并且短小精悍, 值得大家作为首次源码浏览的抉择

如果我讲的有什么问题, 还望不吝指教

相干文章: react-redux 源码浅析

本文代码仓库: https://github.com/Grewer/rea...

参考文档:

  • https://redux.js.org/
  • https://github.com/reduxjs/redux