源码系列:javascript
下面是 Redux v4.x API 源码的解读。java
createStore(reducer, [preloadedState], enhancer)react
createStore
方法接受三个参数,第一个参数为 reducer
,第二个参数是 preloadedState
(可选),第三个是参数是 enhancer
。返回一个 对象 store
。store
中包含方法 dispatch
、getState
、subscribe
、replaceReducer
、[$$observable]
。git
参数 reducer
有多是一个 reducer
,若是有个多个 reducer
,则经过 combineReducer
函数执行后返回函数做为 reducer
。github
参数 preloadedState
表明初始状态,不少时候都不传,不传该参数时候且 enhancer
是函数状况,createStore
函数内部会把 enhancer
,做为第二个参数使用,源码以下:编程
if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
enhancer = preloadedState
preloadedState = undefined
}
复制代码
参数 enhancer
是 store
加强器,是一个函数, createStore
做为 enhancer
函数的参数,这里用到了函数式编程,返回函数又传递 reducer
和 preloadState
参数,执行最终返回一个加强的 store
。enhancer
常有的是 applyMiddleware()
返回值,react-devrools()
等工具,源码以下:redux
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
throw new Error('Expected the enhancer to be a function.')
}
return enhancer(createStore)(reducer, preloadedState)
}
复制代码
介绍方法以前,下面是一些变量:api
let currentReducer = reducer // 当前的 reducer
let currentState = preloadedState // 当前的 state
let currentListeners = [] // 当前的监听者
let nextListeners = currentListeners // 监听者的备份
let isDispatching = false // 是否正在执行 dispatch 动做
复制代码
dispatch(action)数组
dispatch
方法,接受一个参数action
,action
是一个对象,该对象必须包括 type
属性,不然会报错。dispatch
函数内部,主要是执行了 reducer()
函数,获得最新 state
,赋值给 currentState
,执行订阅者,dispatch
函数返回一个 action
对象。源码以下:promise
function dispatch(action) {
if (typeof action.type === 'undefined') {
throw new Error(
'Actions may not have an undefined "type" property. ' +
'Have you misspelled a constant?'
)
}
if (isDispatching) { // 若是正在 dispatch,再次触发 dispatch,则报错
throw new Error('Reducers may not dispatch actions.')
}
try {
isDispatching = true // 表示正在执行 `dispatch`
currentState = currentReducer(currentState, action) // 执行最新的 Reducer,返回最新的 state
} finally {
isDispatching = false
}
const listeners = (currentListeners = nextListeners) // 每次 dispatch 执行,订阅者都会执行
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i]
listener()
}
return action
}
复制代码
getState()
获取最新的 state,就是返回 currentState。源码以下:
function getState() {
return currentState
}
复制代码
添加事件订阅者,每次触发 dispatch(action)
时候,订阅者都会执行。返回取消订阅方法 unSubscribe
。这里采用了观察者模式/发布-订阅者模式。源码以下:
注意:订阅者必须是函数,不然报错。
function subscribe(listener) {
if (typeof listener !== 'function') { // 订阅者必须是函数
throw new Error('Expected the listener to be a function.')
}
if (isDispatching) {
throw new Error(
'You may not call store.subscribe() while the reducer is executing. ' +
'If you would like to be notified after the store has been updated, subscribe from a ' +
'component and invoke store.getState() in the callback to access the latest state. ' +
'See https://redux.js.org/api-reference/store#subscribe(listener) for more details.'
)
}
let isSubscribed = true
nextListeners.push(listener) // 订阅消息,每次 dispatch(action) 时候,订阅者都会接受到消息
return function unsubscribe() { // 返回取消订阅方法
if (!isSubscribed) {
return
}
if (isDispatching) {
throw new Error(
'You may not unsubscribe from a store listener while the reducer is executing. ' +
'See https://redux.js.org/api-reference/store#subscribe(listener) for more details.'
)
}
isSubscribed = false
const index = nextListeners.indexOf(listener)
nextListeners.splice(index, 1) // 取消订阅者
}
}
复制代码
让一个对象可被观察。被观察对象必须是对象,不然报错。这里做者用到了第三方库 symbol-observable
,对 currentState
进行观察。源码以下:
ECMAScript Observables
目前只是草案,还没正式使用。
function observable() {
const outerSubscribe = subscribe
return {
subscribe(observer) {
if (typeof observer !== 'object' || observer === null) {
throw new TypeError('Expected the observer to be an object.')
}
function observeState() {
if (observer.next) {
observer.next(getState()) // 对 currentState 进行监听
}
}
observeState()
const unsubscribe = outerSubscribe(observeState)
return { unsubscribe }
},
[$$observable]() {
return this
}
}
}
复制代码
替换reducer
,通常用不到。源码也简单,把 nextReducer
赋值给 currentReducer
。源码以下:
function replaceReducer(nextReducer) {
if (typeof nextReducer !== 'function') {
throw new Error('Expected the nextReducer to be a function.')
}
currentReducer = nextReducer
}
复制代码
上面 dispatch
、getState
、subscribe
、replaceReducer
、[$$observable]
这些方法使用了闭包,一直保持对 currentState
、currentReducer
、currentListeners
、nextListeners
、isDispatching
等变量的引用。好比,dispatch
改变 currentState
,相应的其余方法中,currentState
也会跟着变,因此这些方法之间是存在联系的。
执行中间件的方法,中间件能够有不少,有自定义的如:打印日志,也有比较知名的 如:redux-chunk
、redux-promise
、redux-saga
等,后续会分析。
applyMiddleware()
一般是上面提到的 createStore
方法的第三个参数 enhancer
,源码以下:
enhancer(createStore)(reducer, preloadedState)
复制代码
结合上面代码能够等价于
applyMiddleware(...middlewares)(createStore)(reducer, preloadedState)
复制代码
applyMiddleware
使用了函数式编程,接收第一个参数元素为 middlewares
的数组,返回值接收createStore
为参数的函数,返回值接收 dispatch
函数,接收一个 action
如 (reducer, state)
,最终返回加强版的 store
。
源码以下:
function applyMiddleware(...middlewares) {
return createStore => (...args) => {
const store = createStore(...args) // args 为 (reducer, initialState)
let dispatch = () => {
throw new Error(
'Dispatching while constructing your middleware is not allowed. ' +
'Other middleware would not be applied to this dispatch.'
)
}
const middlewareAPI = { // 给 middleware 的 store
getState: store.getState,
dispatch: (...args) => dispatch(...args)
}
const chain = middlewares.map(middleware => middleware(middlewareAPI)) // chain 是一个参数为 next 的匿名函数的数组(中间件执行返回的函数)
dispatch = compose(...chain)(store.dispatch) // 通过 compose 函数后返回的结果,是通过 middlewares 包装后的 dispatch
return { // 返回 dispatch 加强后的 store
...store,
dispatch
}
}
}
复制代码
源码以下:
function compose(...funcs) {
if (funcs.length === 0) {
return arg => arg
}
if (funcs.length === 1) {
return funcs[0]
}
return funcs.reduce((a, b) => (...args) => a(b(...args)))
}
复制代码
虽然源代码没几行,做者实现方式太厉害了,若是理解起来有点费劲,能够看看下面例子分析:
var arr = [
function fn1(next){
return action => {
// next 为 fn2 return 函数(dispatch 函数)
const returnValue = next(action)
return returnValue
}
},
function fn2(next) {
return action => {
// next 为 fn3 return 函数(dispatch 函数)
const returnValue = next(action)
return returnValue
}
},
function fn3(next3) {
return action => {
// next 为 dispatch。
const returnValue = next3(action)
return returnValue
}
},
]
var fn = arr.reduce((a, b) => (args) => a(b(args)))
fn // (args) => a(b(args)) 等价于 (dispatch) => fn1(fn2(fn3(dispatch)))
// reduce函数 执行顺序:
// 第一次:
// 变量 a: ƒ fn1(args)
// 变量 b: ƒ fn2(args)
// 返回值: (args) => fn1(fn2(args))
// 第一次返回值赋值给 a
// 变量 a: (args) => fn1(fn2(args))
// 变量 b: ƒ fn3(args)
// 第二次:
// 变量 a : (args) => fn1(fn2(args))
// 变量 b: ƒ fn3(args)
// 返回值: (args) => fn1(fn2(fn3(args)))
复制代码
因此做者的用意是 compose(f, g, h)
返回 (...args) => f(g(h(...args)))
,其中 f、g、h
是一个个中间件 middleware
。
结合上面 applyMiddleware
源码有一段 dispatch = compose(...chain)(store.dispatch)
,因此 args
为 store.dispatch
,(store.dispatch) => f(g(h(store.dispatch)))
,执行第一个中间件h(store.dispatch)
,返回 dispatch
函数(参数为 action 的函数)做为下一个中间件的参数,经过一层层的传递,最终返回一个通过封装的 dispatch
函数。
下面是打印日志中间件例子:
const logger = store => next => action => {
console.log('dispatch', action)
next(action)
console.log('finish', action)
}
const logger2 = store => next2 => action2 => {
console.log('dispatch2', action2)
next2(action2)
console.log('finish2', action2)
}
...
const store = createStore(rootReducer, applyMiddleware(logger, logger2))
复制代码
注意:但若是在某个中间件中使用了 store.dipsatch()
,而不是 next()
,那么就会回到起始位置,会形成无限循环了。
每次触发一个 dispatch,中间件都会执行,打印的顺序为 dispatch distapch2 finish2 finish
。
这个 API 理解为:若是有多个 reducer,那么须要把它们合成一个 reducer,在传给函数 createStore(reducer)
。
核心源码以下:
function combineReducers(reducers) {
const reducerKeys = Object.keys(reducers)
const finalReducers = {}
for (let i = 0; i < reducerKeys.length; i++) {
const key = reducerKeys[i]
if (typeof reducers[key] === 'function') {
finalReducers[key] = reducers[key] // reducer 必定是函数
}
}
const finalReducerKeys = Object.keys(finalReducers)
return function combination(state = {}, action) { // 返回一个新的 reducer 函数
let hasChanged = false
const nextState = {}
for (let i = 0; i < finalReducerKeys.length; i++) { // 每次执行一次 dipatch(action),全部的 reducer 都会执行
const key = finalReducerKeys[i]
const reducer = finalReducers[key]
const previousStateForKey = state[key]
const nextStateForKey = reducer(previousStateForKey, action)
nextState[key] = nextStateForKey
hasChanged = hasChanged || nextStateForKey !== previousStateForKey
}
return hasChanged ? nextState : state // 判断 state 是否有变化,若是有则返回新的 state
}
}
复制代码
这个 API 能够理解为:生成 action 的方法。主要是把 dispatch 也封装进 bindActionCreator(actionCreator, dispatch)
方法里面去,因此调用时候能够直接触发 dispatch(action)
,不须要在手动调用 dispatch,好比 dispatch(fetchPeople({type: TYPE, text: 'fetch people'}))
,使用这个 API 后,则直接 fetchPeople({type: TYPE, text: 'fetch people'})
源码以下:
function bindActionCreator(actionCreator, dispatch) {
return function() {
return dispatch(actionCreator.apply(this, arguments))
}
}
function bindActionCreators(actionCreators, dispatch) {
if (typeof actionCreators === 'function') {
return bindActionCreator(actionCreators, dispatch)
}
const boundActionCreators = {}
for (const key in actionCreators) {
const actionCreator = actionCreators[key]
if (typeof actionCreator === 'function') {
boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
}
}
return boundActionCreators
}
复制代码
下面是博客地址,以为不错点个Star,谢谢~