redux 源码浅析
redux 版本号: “redux”: “4.0.5”
redux 作为一个非常罕用的状态容器库, 大家都应该见识过, 他很玲珑, 只有 2kb, 然而宝贵的是他的 reducer
和 dispatch
这种思维形式
在浏览此文之前, 先理解 / 应用 redux 相干知识点, 能力更好地浏览本文
入口文件
入口是在 redux/src/index.js
中, 在入口文件中只做了一件事件, 就是引入文件, 集中导出
当初咱们依据他导出的办法, 来进行剖析
createStore
这个是 redux 最次要的 API
应用
搭配这应用办法一起, 能够更好的浏览源码
createStore(reducer, [preloadedState], [enhancer])
他的次要性能就是创立一个 store, 将 reducer
转换到 store
参数
一共可承受三个参数:
- reducer (函数): 一个返回下一个状态树的还原函数,给定以后状态树和一个要解决的动作。
- [preloadedState] (任意值): 初始值, 能够是来自于 storage 中的; 如果你用 combinedReducers 产生了 reducer,这必须是一个一般对象,其类型与传递给它的键雷同。
也能够自在地传递任何你的 reducer 可能了解的货色。 - [enhancer] (函数): store 的增强器, 能够选择性的加强, 用代码来说就是
enhancer(createStore)(reducer, preloadedState)
,enhancer
承受的参数就是createStore
, 同样地他也须要return
一个相似于createStore
的后果, 也就是说, 只有咱们返回的是 一个像createStore
的货色,
他的具体实现咱们就能够有很多微调 这里附上一篇探讨enhancer
和applyMiddleware
的文章 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 = 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/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))
源码解析
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(/* 省略 */)
}
// 定义变量
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