redux 源码浅析

参数
一共可接受三个参数:

reducer (函数): 一个返回下一个状态树的还原函数,给定当前状态树和一个要处理的动作。

[preloadedState] (任意值): 初始值, 可以是来自于 storage 中的; 如果你用combinedReducers产生了reducer,这必须是一个普通对象,其类型与传递给它的键相同。
也可以自由地传递任何你的reducer能够理解的东西。

[enhancer] (函数): store 的增强器, 可以选择性的增强, 用代码来说就是 enhancer(createStore)(reducer, preloadedState), enhancer
接受的参数就是 createStore, 同样地他也需要 return 一个类似于 createStore 的结果, 也就是说, 只有我们返回的是 一个像 createStore 的东西,
他的具体实现我们就可以有很多微调 这里附上一篇探讨 enhancer 和 applyMiddleware 的文章 https://juejin.cn/post/6844903543502012429

// 简单的例子:

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 = reducer
let currentState = preloadedState
let currentListeners = []
let nextListeners = currentListeners
let 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 的 polyfill
import $$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/6844903714998730766

剩余代码
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))

源码解析
default

function 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(/*省略*/)
}
USB Microphone  https://www.soft-voice.com/

Wooden Speakers https://www.zeshuiplatform.com/
亚马逊测评 www.yisuping.cn
深圳网站建设www.sz886.com

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值