redux 版本号: "redux": "4.0.5"
redux 做为一个十分经常使用的状态容器库, 你们都应该见识过, 他很小巧, 只有 2kb, 可是珍贵的是他的 reducer
和 dispatch
这种思想方式react
在阅读此文以前, 先了解/使用 redux 相关知识点, 才能更好地阅读本文git
入口是在 redux/src/index.js
中, 在入口文件中只作了一件事件, 就是引入文件, 集中导出
如今咱们根据他导出的方法, 来进行分析github
这个是 redux 最主要的 APIredux
搭配这使用方法一块儿, 能够更好的浏览源码segmentfault
createStore(reducer, [preloadedState], [enhancer])
api
他的主要功能就是建立一个 store, 将 reducer
转换到 store
数组
一共可接受三个参数:promise
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 })
createStore
返回的固然是一个 store
, 他有本身的 api
app
返回应用程序的当前状态树异步
const state = store.getState()
这个其实不用我多说, 会 redux
的都应该知道这个
store.dispatch({type: 'counter/incremented'})
添加一个监听器, 每当 action
dispatch
的时候, 都会调用 listener
, 在 listener
中可使用 getState
来获取当前的状态树
const unsubscribe = store.subscribe(() => { console.log('listener run') const current = store.getState() if (current.value === 12350) { unsubscribe() } })
展现一个场景, 监听事件, 当达到某个条件以后, 解除监听事件
使用一个 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() } }
function getState() { // isDispatching 默认为 false, 表示当前 store 是否正在 dispatch if (isDispatching) { throw new Error('//...') } // 直接返回当前 state , 默认为入参 preloadedState return currentState }
// 忽略了错误判断 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 } }
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 }
function replaceReducer(nextReducer) { // 若是 nextReducer 不是函数则抛出错误 // 直接替换 currentReducer = nextReducer // 相似 ActionTypes.INIT. 替换值 dispatch({type: ActionTypes.REPLACE}) }
还有一个额外的 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 } }
// 能够接受多个 reducer, 实现一种 module 的功能 rootReducer = combineReducers({potato: potatoReducer, tomato: tomatoReducer}) // 返回值 { potato: { // 某些属性 } , tomato: { // 某些属性 } } const store = createStore(rootReducer, { potato: { // 初始值 } })
有一点须要注意的是, reducer 都是须要默认值的,如:
function counterReducer(state = {value: 0}, action) { //... }
先看 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 计算出值
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 的使用 才能顺带过渡到下面
就如他的名字, 是用来组合函数的, 接受刀哥函数, 返回执行的最终函数:
// 这里经常使用来 连接多个中间件 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
是使用在 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中的 {/*省略*/}
返回结果
这个 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...
参考文档: