成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Redux源碼解析系列 (二)牛鼻的createStore

開發 后端
本篇文章,我們來解析createStore

 

前言
在上一章了解了【Redux 源碼解析系列(一) -- Redux的實現思想】之后,我們正式進入源碼解析~

Redux 其實是用來幫我們管理狀態的一個框架,它暴露給我們四個接口,分別是:

  • createStore
  • combineReducers
  • bindActionCreators
  • applyMiddleware
  • compose

本篇文章,我們來解析createStore
下面我來對其進行解析~

INIT
這個方法是redux保留用的,用來初始化reducer的狀態 

  1. export const ActionTypes = { 
  2.   INIT: '@@redux/INIT' 

前面說 createStore的作用就是:創建一個store來管理app的狀態,唯一改變狀態的方式就是dispatch一個action,最終返回一個object。

  1. return { 
  2.     dispatch, 
  3.     subscribe, 
  4.     getState, 
  5.     replaceReducer, 
  6.     [$$observable]: observable 

不過replaceReducer,跟[$$observable]:都不常用~ ,所以這里只對前三的接口做解析。

createStore
在一個app里,只能有一個store,如果你想指明不同的state對應不同的action,可以用combineReducers去合并不同的reducer。

參數:

  • reducer(function):就是通過傳入當前State,還有action,計算出下一個state,返回回來。
  • preloadedState(any):initial state
  • enhancer(function):增強store的功能,讓它擁有第三方的功能,比如middleware.Redux里面唯一的enhancer就是applyMiddleware()
  1. export default function createStore(reducer, preloadedState, enhancer) { 
  2. // 第一段說的就是當第二個參數沒有傳preloadedState,而直接傳function的話,就會直接把這個function當成enhancer 
  3.   if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') { 
  4.     enhancer = preloadedState 
  5.     preloadedState = undefined 
  6.   } 
  7.   // 當第三個參數傳了但是不是function也會報錯 
  8.   if (typeof enhancer !== 'undefined') { 
  9.     if (typeof enhancer !== 'function') { 
  10.       throw new Error('Expected the enhancer to be a function.'
  11.     } 
  12.     //關鍵的一個就在于這里了,在前一篇講applyMiddleware的時候介紹了這么做的意義, 
  13.     //實際就是把createStore這件事在applyMiddleware里面做,轉移了鍋。 
  14.     return enhancer(createStore)(reducer, preloadedState) 
  15.   } 
  16.  
  17.   if (typeof reducer !== 'function') { 
  18.     throw new Error('Expected the reducer to be a function.'
  19.   } 
  20.  
  21.   let currentReducer = reducer 
  22.   let currentState = preloadedState 
  23.   let currentListeners = [] 
  24.   let nextListeners = currentListeners 
  25.   let isDispatching = false 

上面是第一個part,在校驗完參數的正確之后,終于可以干點正事兒了。createStore最終會返回一個Object.

  1.  dispatch, 
  2.  subscribe, 
  3.  getState 

接下來看看里面都做了什么:

getState
getState作用就是將當前state的狀態返回回來,沒啥好說的~

  1. function getState() { 
  2.    return currentState 

subscribe
作用:添加監聽函數listener,它會在每次dispatch action的時候調用。

參數:listener(function): 在每一次dispatch action的時候都會調用的函數

返回:返回一個移除listener的函數

  1. // 這個函數的作用就是,如果發現nextListeners,nextListeners指向同一個堆棧的話,就淺復制一份,這樣改nextListeners就不會改到currentListeners 
  2. function ensureCanMutateNextListeners() { 
  3.     if (nextListeners === nextListeners) { 
  4.       nextListeners = currentListeners.slice() 
  5.     } 
  6.  
  7. function subscribe(listener) { 
  8.     if (typeof listener !== 'function') { 
  9.       throw new Error('Expected listener to be a function.'
  10.     } 
  11.  
  12.     let isSubscribed = true 
  13.  
  14.     ensureCanMutateNextListeners() 
  15.     // 直接將監聽的函數放進nextListeners里 
  16.     nextListeners.push(listener) 
  17.  
  18.     return function unsubscribe() { 
  19.     // 如果已經移除了就直接返回 
  20.       if (!isSubscribed) { 
  21.         return 
  22.       } 
  23.  
  24.       isSubscribed = false 
  25.  
  26.       ensureCanMutateNextListeners() 
  27.       // 沒有移除的話,先找到位置,通過splice移除 
  28.       const index = nextListeners.indexOf(listener) 
  29.       nextListeners.splice(index, 1) 
  30.     } 
  31.   } 

在使用的時候就可以:

  1. const unsubscribe = store.subscribe(() => 
  2.   console.log(store.getState()) 
  3.  
  4. unsubscribe() 
  5. dispatch 

dispatch
dispatch 作為一個重點函數~ 其實它的作用就是觸發狀態的改變。

參數:action(object),它是一個描述發生了什么的對象,其中type是必須的屬性。

返回:這個傳入的object

  1. function dispatch(action) { 
  2.     if (!isPlainObject(action)) { 
  3.       throw new Error( 
  4.         'Actions must be plain objects. ' + 
  5.         'Use custom middleware for async actions.' 
  6.       ) 
  7.     } 
  8.     // 
  9.     if (typeof action.type === 'undefined') { 
  10.       throw new Error( 
  11.         'Actions may not have an undefined "type" property. ' + 
  12.         'Have you misspelled a constant?' 
  13.       ) 
  14.     } 
  15.     // 防止多次dispatch請求同時改狀態,一定是前面的dispatch結束之后,才dispatch下一個 
  16.     if (isDispatching) { 
  17.       throw new Error('Reducers may not dispatch actions.'
  18.     } 
  19.  
  20.     try { 
  21.       isDispatching = true 
  22.       currentState = currentReducer(currentState, action
  23.     } finally { 
  24.       isDispatching = false 
  25.     } 
  26.     // 在dispatch的時候,又將nextListeners 賦值回currentListeners, 
  27.     const listeners = currentListeners = nextListeners 
  28.     for (let i = 0; i < listeners.length; i++) { 
  29.       const listener = listeners[i] 
  30.       listener() 
  31.     } 
  32.  
  33.     return action 
  34.   } 

在上面一系列完成之后,需要初始化appState的狀態。當INIT action被dispatched 的時候,每個reducer都會return回它的初始狀態。

  1. dispatch({ type: ActionTypes.INIT }) 

自問自答環節
為什么createStore中既存在currentListeners也存在nextListeners?
在上面的源碼中,createStore函數為了保存store的訂閱者,不僅保存了當前的訂閱者currentListeners而且也保存了nextListeners。createStore中有一個內部函數ensureCanMutateNextListeners:

  1. function ensureCanMutateNextListeners() { 
  2.     if (nextListeners === currentListeners) { 
  3.       nextListeners = currentListeners.slice() 
  4.     } 

這個函數實質的作用是確保可以改變nextListeners,如果nextListeners與currentListeners一致的話,將currentListeners做一個拷貝賦值給nextListeners,然后所有的操作都會集中在nextListeners,比如我們看訂閱的函數subscribe:

  1. function subscribe(listener) { 
  2. // ...... 
  3.     let isSubscribed = true 
  4.  
  5.     ensureCanMutateNextListeners() 
  6.     nextListeners.push(listener) 
  7.  
  8.     return function unsubscribe() { 
  9.         // ...... 
  10.         ensureCanMutateNextListeners() 
  11.         const index = nextListeners.indexOf(listener) 
  12.         nextListeners.splice(index, 1) 

我們發現訂閱和解除訂閱都是在nextListeners做的操作,然后每次dispatch一個action都會做如下的操作:

  1. function dispatch(action) { 
  2.     try { 
  3.       isDispatching = true 
  4.       currentState = currentReducer(currentState, action
  5.     } finally { 
  6.       isDispatching = false 
  7.     } 
  8.     // 相當于currentListeners = nextListeners const listeners = currentListeners 
  9.     const listeners = currentListeners = nextListeners 
  10.     for (let i = 0; i < listeners.length; i++) { 
  11.       const listener = listeners[i] 
  12.       listener() 
  13.     } 
  14.     return action 
  15.   } 

我們發現在dispatch中做了const listeners = currentListeners = nextListeners,相當于更新了當前currentListeners為nextListeners,然后通知訂閱者,到這里我們不禁要問為什么要存在這個nextListeners?     其實代碼中的注釋也是做了相關的解釋:

The subscriptions are snapshotted just before every dispatch() call.If you subscribe or unsubscribe while the listeners are being invoked, this will not have any effect on the dispatch() that is currently in progress.However, the next dispatch() call, whether nested or not, will use a more recent snapshot of the subscription list.

來讓我這個六級沒過的渣渣翻譯一下: 訂閱者(subscriptions)在每次dispatch()調用之前都是一份快照(snapshotted)。如果你在listener被調用期間,進行訂閱或者退訂,在本次的dispatch()過程中是不會生效的,然而在下一次的dispatch()調用中,無論dispatch是否是嵌套調用的,都將使用最近一次的快照訂閱者列表。用圖表示的效果如下:

我們從這個圖中可以看見,如果不存在這個nextListeners這份快照的話,因為dispatch導致的store的改變,從而進一步通知訂閱者,如果在通知訂閱者的過程中發生了其他的訂閱(subscribe)和退訂(unsubscribe),那肯定會發生錯誤或者不確定性。例如:比如在通知訂閱的過程中,如果發生了退訂,那就既有可能成功退訂(在通知之前就執行了nextListeners.splice(index, 1))或者沒有成功退訂(在已經通知了之后才執行了nextListeners.splice(index, 1)),這當然是不行的。因為nextListeners的存在所以通知訂閱者的行為是明確的,訂閱和退訂是不會影響到本次訂閱者通知的過程。

還是看不懂是什么意思?????一個簡單粗俗的例子:

當在執行這段代碼到第三個listener的時候:

  1. for (let i = 0; i < listeners.length; i++) { 
  2.   const listener = listeners[i] 
  3.   listener() 

你突然把第2個listener給splice了。這樣的話此時上面的循環本來是執行完第三個要執行第四個了,但是由于數組中的第2個listener被splice掉了,所以數組后面的元素都要往前移動一個位置,這時數組的第四個listener就移動到原先第三個的位置了,數組的第五個listener就移動到原先第四個的位置了,因此循環本要執行第四個的,結果由于第四個往前移動了,實際執行的是原先的第五個,所以導致原先的第四個沒有被執行。。

沒錯,上面講的就是這樣的!?。。」靼琢税桑。。。?/p>

但是這里又有一個問題了:

JavaScript不是單線程的嗎?為啥在執行循環的時候,會執行unsubscribe()操作
百思不得其解的情況下,去Redux項目下開了一個issue,得到了維護者的回答:

得了,我們再來看看測試相關的代碼吧??赐曛笪伊私獾搅恕5拇_,因為JavaScript是單線程語言,不可能出現出現想上述所說的多線程場景,但是我忽略了一點,執行訂閱者函數時,在這個回調函數中可以執行退訂或者訂閱事件。例如:

  1. const store = createStore(reducers.todos) 
  2. const unsubscribe1 = store.subscribe(() => { 
  3.     const unsubscribe2 = store.subscribe(()=>{}) 
  4. }) 

這不就實現了在通知listener的過程中混入訂閱subscribe與退訂unsubscribe嗎?

為什么Reducer中不能進行dispatch操作?
我們知道在reducer函數中是不能執行dispatch操作的。一方面,reducer作為計算下一次state的純函數是不應該承擔執行dispatch這樣的操作。另一方面,即使你嘗試著在reducer中執行dispatch,也并不會成功,并且會得到"Reducers may not dispatch actions."的提示。因為在dispatch函數就做了相關的限制:

  1. function dispatch(action) { 
  2.     if (isDispatching) { 
  3.       throw new Error('Reducers may not dispatch actions.'
  4.     } 
  5.     try { 
  6.       isDispatching = true 
  7.       currentState = currentReducer(currentState, action
  8.     } finally { 
  9.       isDispatching = false 
  10.     } 
  11.  
  12.     //...notice listener 

在執行dispatch時就會將標志位isDispatching置為true。然后如果在currentReducer(currentState, action)執行的過程中由執行了dispatch,那么就會拋出錯誤('Reducers may not dispatch actions.')。之所以做如此的限制,是因為在dispatch中會引起reducer的執行,如果此時reducer中又執行了dispatch,這樣就落入了一個死循環,所以就要避免reducer中執行dispatch。

參考文獻:

  1. https://github.com/MrErHu/blog/issues/18
  2. https://zhuanlan.zhihu.com/p/57316118

 【編輯推薦】

 

責任編輯:姜華 來源: 前端陽光
相關推薦

2016-09-22 15:50:38

JavascriptRedux源碼解析

2012-02-02 15:56:48

Android 4.0Launcher源碼分析

2021-07-09 06:48:30

注冊源碼解析

2021-07-03 08:51:30

源碼Netty選擇器

2023-07-19 14:00:50

OverlayC語言

2021-11-08 15:06:15

鴻蒙HarmonyOS應用

2015-08-18 08:55:03

redux核心

2023-04-12 15:09:25

Overlay fs鴻蒙

2022-05-20 10:32:49

事件循環器事件隊列鴻蒙

2015-09-16 09:10:27

Java源碼解析

2019-04-28 16:10:50

設計Redux前端

2021-06-17 09:36:07

鴻蒙HarmonyOS應用

2018-07-19 15:57:46

ViewStub源碼方法

2010-11-08 09:04:31

IBMPower

2016-10-27 21:33:46

ReduxFlux異步方案

2023-05-11 07:25:57

ReduxMiddleware函數

2024-02-01 08:12:15

ReducerContext狀態

2012-11-06 11:07:59

jQueryJSjQuery框架

2024-11-18 16:15:00

2024-01-18 08:31:22

go實現gorm框架
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 亚洲成人一区 | 久久久精品 | 欧美激情一区二区三区 | 91精品国产综合久久久密闭 | 久久中文字幕一区 | 美女久久 | 国产成人精品一区二区三区网站观看 | 成人小视频在线观看 | 亚洲精品在线视频 | 亚洲视频免费观看 | 欧美天堂 | 成人午夜视频在线观看 | 精品免费国产视频 | 国产精品一区在线观看你懂的 | 四虎永久免费地址 | 亚洲欧美综合精品久久成人 | 国产免费一区二区三区免费视频 | 成年人在线视频 | 日韩在线不卡视频 | 亚洲成av片人久久久 | 欧美激情在线播放 | 欧美成人a∨高清免费观看 91伊人 | 午夜视频免费在线观看 | 午夜免费视频 | 国精日本亚洲欧州国产中文久久 | 午夜影院在线观看视频 | 亚洲永久| 国产激情在线播放 | 国产一级精品毛片 | 久久中文视频 | 亚洲欧美国产精品一区二区 | 亚洲人成人一区二区在线观看 | 国产在线播放一区二区三区 | 亚洲精品国产a久久久久久 中文字幕一区二区三区四区五区 | 免费欧美视频 | 日韩午夜影院 | 色精品 | 国产一区| 日韩精品一区二区三区中文在线 | 欧美一级淫片免费视频黄 | 中文字幕动漫成人 |