今天來討論一下redux源碼部分
先附上demo 如果大家有問題 可以直接加我QQ:469373256
demo地址:https://github.com/fangkyi03/redux-demo.git
- demo
function funtest(state,action){
console.log('state',state,action)
switch (action.type) {
case 'test':
return Object.assign(state,{test:action.payload})
default:
return state || {}
}
}
const fun1 = (store) => {
console.log('store',store);
return (next) =>{
console.log('next',next)
return (action) =>{
next(action)
console.log('action',action);
}
}
}
const fun2 = (store) => {
console.log('store',store);
return (next) =>{
console.log('next',next)
return (action) =>{
next(action)
console.log('action',action);
}
}
}
const store = createStore(combineReducers({funtest}),{a:1},applyMiddleware(fun1,fun2,fun1))
console.log('輸出state',store.getState());
console.log('輸出action',store.dispatch({type:'test',payload:'asdasd'}));
console.log('輸出state',store.getState())
store.subscribe(()=>{
console.log('輸出store最新變化',store.getState());
})
- 暴露的接口部分
export {
createStore,
combineReducers,
bindActionCreators,
applyMiddleware,
compose
}
redux核心暴露出來的接口只有這么幾個(gè) 接下來將會(huì)按照這個(gè)demo的執(zhí)行順序一一的給大家進(jìn)行一下解釋 先來看一下combineReducers這個(gè)函數(shù)的使用方法
- combineReducers
先附上完整代碼 然后在一一講解
export default function combineReducers(reducers) {
const reducerKeys = Object.keys(reducers)
const finalReducers = {}
for (let i = 0; i < reducerKeys.length; i++) {
const key = reducerKeys[i]
if (process.env.NODE_ENV !== 'production') {
if (typeof reducers[key] === 'undefined') {
warning(`No reducer provided for key "${key}"`)
}
}
if (typeof reducers[key] === 'function') {
finalReducers[key] = reducers[key]
}
}
const finalReducerKeys = Object.keys(finalReducers)
let unexpectedKeyCache
if (process.env.NODE_ENV !== 'production') {
unexpectedKeyCache = {}
}
let shapeAssertionError
try {
assertReducerShape(finalReducers)
} catch (e) {
shapeAssertionError = e
}
return function combination(state = {}, action) {
if (shapeAssertionError) {
throw shapeAssertionError
}
if (process.env.NODE_ENV !== 'production') {
const warningMessage = getUnexpectedStateShapeWarningMessage(state, finalReducers, action, unexpectedKeyCache)
if (warningMessage) {
warning(warningMessage)
}
}
let hasChanged = false
const nextState = {}
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)
if (typeof nextStateForKey === 'undefined') {
const errorMessage = getUndefinedStateErrorMessage(key, action)
throw new Error(errorMessage)
}
nextState[key] = nextStateForKey
hasChanged = hasChanged || nextStateForKey !== previousStateForKey
}
return hasChanged ? nextState : state
}
}
combineReducers主要可以分為兩個(gè)部分
1.效驗(yàn)部分 效驗(yàn)?zāi)爿斎氲膮?shù)是否符合要求,如果不符合就會(huì)在使用之前直接拋出異常
- combination部分 也就是在每次dispatch的時(shí)候 都會(huì)執(zhí)行的這個(gè)
在這里會(huì)傳入最新的state跟action 每次都會(huì)重新的去執(zhí)行一遍 并且返回最新的一個(gè)currentState值 這里也是比較詬病的地方 因?yàn)槿绻愕膔educe比較多的情況下 你每次dispatch都會(huì)去將所有的reduce重新去走一遍 其實(shí)是比較耗費(fèi)性能的
將效驗(yàn)可以分成如下幾種情況
1.當(dāng)執(zhí)行reduce以后 返回的值為undefinde的時(shí)候
在這里會(huì)調(diào)用兩種模式 一種是直接傳遞type為init的 一種是傳遞type為隨機(jī)數(shù)的
其實(shí)主要就是確保你在switch的時(shí)候 都加了default類型 避免 出現(xiàn)返回undefined的情況
function assertReducerShape(reducers) {
Object.keys(reducers).forEach(key => {
const reducer = reducers[key]
const initialState = reducer(undefined, { type: ActionTypes.INIT })
if (typeof initialState === 'undefined') {
throw new Error(
`Reducer "${key}" returned undefined during initialization. ` +
`If the state passed to the reducer is undefined, you must ` +
`explicitly return the initial state. The initial state may ` +
`not be undefined. If you don't want to set a value for this reducer, ` +
`you can use null instead of undefined.`
)
}
const type = '@@redux/PROBE_UNKNOWN_ACTION_' + Math.random().toString(36).substring(7).split('').join('.')
if (typeof reducer(undefined, { type }) === 'undefined') {
throw new Error(
`Reducer "${key}" returned undefined when probed with a random type. ` +
`Don't try to handle ${ActionTypes.INIT} or other actions in "redux/*" ` +
`namespace. They are considered private. Instead, you must return the ` +
`current state for any unknown actions, unless it is undefined, ` +
`in which case you must return the initial state, regardless of the ` +
`action type. The initial state may not be undefined, but can be null.`
)
}
})
}
2.如果發(fā)現(xiàn)state不等于對(duì)象的時(shí)候報(bào)錯(cuò)
if (!isPlainObject(inputState)) {
return (
`The ${argumentName} has unexpected type of "` +
({}).toString.call(inputState).match(/\s([a-z|A-Z]+)/)[1] +
`". Expected argument to be an object with the following ` +
`keys: "${reducerKeys.join('", "')}"`
)
}
3.如果在新傳入的state與第一次初始化時(shí)init返回的節(jié)點(diǎn)不一致的時(shí)候 會(huì)直接進(jìn)行錯(cuò)誤提醒 但是并不會(huì)中斷程序的運(yùn)行
const unexpectedKeys = Object.keys(inputState).filter(key =>
!reducers.hasOwnProperty(key) &&
!unexpectedKeyCache[key]
)
unexpectedKeys.forEach(key => {
unexpectedKeyCache[key] = true
})
if (unexpectedKeys.length > 0) {
return (
`Unexpected ${unexpectedKeys.length > 1 ? 'keys' : 'key'} ` +
`"${unexpectedKeys.join('", "')}" found in ${argumentName}. ` +
`Expected to find one of the known reducer keys instead: ` +
`"${reducerKeys.join('", "')}". Unexpected keys will be ignored.`
)
}
}
4.如果在combination的運(yùn)算過程中發(fā)現(xiàn)返回的state如果是一個(gè)undefined的話會(huì)直接報(bào)錯(cuò) 這個(gè)錯(cuò)誤會(huì)直接中斷程序的運(yùn)行 所以reduce的要求就是 任何情況下 都不能返回一個(gè)空數(shù)據(jù)
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)
if (typeof nextStateForKey === 'undefined') {
const errorMessage = getUndefinedStateErrorMessage(key, action)
throw new Error(errorMessage)
}
nextState[key] = nextStateForKey
hasChanged = hasChanged || nextStateForKey !== previousStateForKey
}
return hasChanged ? nextState : state
}
這里正常應(yīng)該講中間件了 但是這邊稍微等一下 因?yàn)橐瓤匆幌耤reateStore中是如何處理的 才能繼續(xù)講applyMiddleware
- createStore部分
參數(shù)定義
export default function createStore(reducer, preloadedState, enhancer)
如果preloadedState初始化state是一個(gè)函數(shù) 并且enhancer值為空的情況下 那么就將preloadedState當(dāng)做中間件處理來使用
if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
enhancer = preloadedState
preloadedState = undefined
}
如果enhancer中間件函數(shù)不等于空 并且不等于函數(shù)的話 就直接報(bào)錯(cuò)
if (typeof enhancer !== 'undefined') {
if (typeof enhancer !== 'function') {
throw new Error('Expected the enhancer to be a function.')
}
return enhancer(createStore)(reducer, preloadedState)
}
如果reducer不等于函數(shù)則直接報(bào)錯(cuò)
if (typeof reducer !== 'function') {
throw new Error('Expected the reducer to be a function.')
}
從這里可以看到createStore有兩種寫法
createStore(reducer, preloadedState, enhancer)
createStore(reducer, enhancer)
兩者的區(qū)別是一種帶了默認(rèn)state一種不帶
當(dāng)然一般用了ssr的話 都會(huì)采用第一種 因?yàn)檫@樣會(huì)更加方便初始化state導(dǎo)入
上面我們看到 如果你有傳入applyMiddleware中間件的話 默認(rèn)會(huì)走中間件 并且將我們自身給傳遞過去 那么我們再看一下中間件的寫法
- 中間件完整代碼
export default function applyMiddleware(...middlewares) {
return (createStore) => (reducer, preloadedState, enhancer) => {
這里傳遞了原有的createStore過來以后 又使用它去進(jìn)行了一次初始化
const store = createStore(reducer, preloadedState, enhancer)
let dispatch = store.dispatch
let chain = []
const middlewareAPI = {
getState: store.getState,
dispatch: (action) => dispatch(action)
}
這里兩句話 才是redux最為經(jīng)典的地方 將這兩個(gè)地方就得先看一下中間件的結(jié)構(gòu)
chain = middlewares.map(middleware => middleware(middlewareAPI))
dispatch = compose(...chain)(store.dispatch)
return {
...store,
dispatch
}
}
}
- 中間件代碼結(jié)構(gòu)
const fun1 = (store) => {
console.log('store',store);
return (next) =>{
console.log('next',next)
return (action) =>{
next(action)
console.log('action',action);
}
}
}
const fun2 = (store) => {
console.log('store',store);
return (next) =>{
console.log('next',next)
return (action) =>{
next(action)
console.log('action',action);
}
}
}
按照執(zhí)行順序來的話 如果你執(zhí)行了這個(gè)以后
chain = middlewares.map(middleware => middleware(middlewareAPI))
實(shí)際上返回的是中間件的這部分以后的代碼 并且會(huì)給所有的中間件傳遞兩個(gè)函數(shù)一個(gè)dispatch 一個(gè)getState
return (next) =>{
console.log('next',next)
return (action) =>{
next(action)
console.log('action',action);
}
}
- compose(...chain)(store.dispatch)
這個(gè)部分其實(shí)分為幾種類型 1.沒有數(shù)據(jù),2.單條數(shù)據(jù) 3.多條數(shù)據(jù)
來看一下compose的源碼
export default function compose(...funcs) {
if (funcs.length === 0) {
return arg => arg
}
if (funcs.length === 1) {
return funcs[0]
}
return funcs.reduce((a, b) => (...reset) => a(b(...reset)))
}
1.如果你中間件里面沒有數(shù)據(jù)的話 最走這里返回的arg其實(shí)就是你傳進(jìn)去的dispatch
if (funcs.length === 0) {
return arg => arg
}
dispatch = compose(...chain)(store.dispatch)
2.如果你傳進(jìn)去的長度為1的話 說明只有一條數(shù)據(jù) 那么這里就直接返回對(duì)應(yīng)的那個(gè)數(shù)據(jù)
if (funcs.length === 1) {
return funcs[0]
}
剛才我們上面已經(jīng)提到了 我們在
middlewares.map(middleware => middleware(middlewareAPI))
執(zhí)行完以后 我們所有返回的值 其實(shí)都已經(jīng)在next子函數(shù)下面了
所以當(dāng)我們在這里直接return funcs[0]的時(shí)候 實(shí)際上就是返回我們的next
dispatch = compose(...chain)(store.dispatch)
因?yàn)檫@句代碼的關(guān)系 我們返回的next被執(zhí)行了 并且傳入了store.dispatch這個(gè)參數(shù) 然后返回了next里面包裹的action函數(shù) 作為新的dispatch函數(shù)
所以從這里可以看到 單條數(shù)據(jù)的是next等于傳入的store.dispatch
所以你可以在接收到action以后 直接next(action)這種方式 直接讓他走對(duì)應(yīng)的reduce
3.多條數(shù)據(jù)跟單條的有一點(diǎn)差別 單條數(shù)據(jù)的時(shí)候 next 永遠(yuǎn)是dispatch 但是如果多條的時(shí)候 next就成為了 控制你下一個(gè)中間件是否執(zhí)行的關(guān)鍵 因?yàn)閚ext在多條里面 傳入的不單單是dispatch了 還有可能是你上一個(gè)中間件的next部分
return funcs.reduce((a, b) => (...reset) => a(b(...reset)))
如果你只有兩條數(shù)據(jù)的時(shí)候 這個(gè)...reset依舊是你傳遞進(jìn)去的store.dispatch
如果你在多余兩條數(shù)據(jù)傳遞進(jìn)去以后 這里的...reset在第一次還是store.dispatch
在第二次就變成了
return (next) =>{
console.log('next',next)
return (action) =>{
next(action)
console.log('action',action);
}
}
在這里是不是覺得有點(diǎn)繞 感覺這樣的操作 最后是這么返回一個(gè)dispatch的呢
funcs.reduce((a, b) => (...reset) => a(b(...reset)))
在這個(gè)階段 其實(shí)永遠(yuǎn)不知道傳進(jìn)去的action到底是什么 這里只是生成了一個(gè)嵌套的函數(shù)鏈 最后生成的一個(gè)函數(shù) 會(huì)在每次dispatch的時(shí)候 都將所有的中間件全部的執(zhí)行一次 知道最后一層使用了dispatch的時(shí)候 才算是結(jié)束 了
dispatch((action)=>{
return a(b(...arg))(action)
})
ok 現(xiàn)在redux最核心的部分就講完了 接下來講一下回調(diào)監(jiān)聽的部分
- subscribe
這個(gè)部分其實(shí)比較簡單 就是你傳入的任何一個(gè)listener只要是一個(gè)函數(shù)就會(huì) 被加入到監(jiān)聽的數(shù)組里面 然后返回一個(gè)卸載的函數(shù) 那么這個(gè)監(jiān)聽函數(shù)又是在哪里被使用的呢 這里就得看一下createStore中關(guān)于dispatch的定義了
function subscribe(listener) {
if (typeof listener !== 'function') {
throw new Error('Expected listener to be a function.')
}
let isSubscribed = true
ensureCanMutateNextListeners()
nextListeners.push(listener)
return function unsubscribe() {
if (!isSubscribed) {
return
}
isSubscribed = false
ensureCanMutateNextListeners()
const index = nextListeners.indexOf(listener)
nextListeners.splice(index, 1)
}
}
- dispatch定義
我們在講剛才的compose的時(shí)候 有說過 你可以在中間件里面直接用dispatch 也可以直接使用傳進(jìn)去的那個(gè)store.dispatch 甚至 你也可以不執(zhí)行next來阻止接下來的中間件執(zhí)行 不管如何情況 如果在中間件所有都符合的情況下 這個(gè)dispatch肯定會(huì)被執(zhí)行 如果你當(dāng)前中間件在運(yùn)行過程中 在currentReducer還沒執(zhí)行完畢的情況下 你又去發(fā)起了一條dispatch的話 是會(huì)導(dǎo)致奔潰報(bào)錯(cuò)的
if (isDispatching) {
throw new Error('Reducers may not dispatch actions.')
}
function dispatch(action) {
if (!isPlainObject(action)) {
throw new Error(
'Actions must be plain objects. ' +
'Use custom middleware for async actions.'
)
}
if (typeof action.type === 'undefined') {
throw new Error(
'Actions may not have an undefined "type" property. ' +
'Have you misspelled a constant?'
)
}
if (isDispatching) {
throw new Error('Reducers may not dispatch actions.')
}
try {
isDispatching = true
currentState = currentReducer(currentState, action)
} finally {
isDispatching = false
}
const listeners = currentListeners = nextListeners
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i]
listener()
}
return action
}
- dispatch 主要看兩個(gè)部分 currentReducer與listeners
在這里要說兩點(diǎn) 這里其實(shí)也是redux性能最大的一個(gè)問題所在
當(dāng)你每次執(zhí)行dispatch的時(shí)候 都會(huì)去生成一個(gè)最新的reducer并且還會(huì)將所有的監(jiān)聽都去執(zhí)行一遍 其實(shí)這里耗費(fèi)的性能是很大的 這里的currentReducer就是combination
try {
isDispatching = true
currentState = currentReducer(currentState, action)
} finally {
isDispatching = false
}
const listeners = currentListeners = nextListeners
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i]
listener()
}
- 參數(shù)定義部分
let currentReducer = reducer
let currentState = preloadedState
let currentListeners = []
let nextListeners = currentListeners
let isDispatching = false
在這里 redux源碼 基本分析完畢 如果大家有問題 可以直接加我QQ:469373256