天天看點

技本功丨知否知否,Redux源碼竟如此意味深長(上集)

技本功丨知否知否,Redux源碼竟如此意味深長(上集)

夫 子 說

技本功丨知否知否,Redux源碼竟如此意味深長(上集)

元月二号欠下袋鼠雲技術公号一篇關于Redux源碼解讀的文章,轉眼月底,期間常被“債主”上門催債。由于年底項目工期比較緊,于是債務就這樣被利滾利。但是好在這段時間有點閑暇,于是趕緊把這篇文章給完成了。據說文章點贊多了可以抵扣利息,小夥們要是覺得我這篇文章還不錯的話,記得幫我點贊哦!好讓我早日擺脫債務,感激不盡!

技本功丨知否知否,Redux源碼竟如此意味深長(上集)

好了,回到正題。今天打算和大家講一講redux的源碼,通過分析源碼,我個人覺得受益匪淺,借此通過這篇文章把我的一些心得體會向大家分享一下,另外需要注意一下這次分享的源碼用的redux的V4.0.0版本,小夥伴在對照的時候可别搞錯咯。接下來老司機可是要發車了,大家抓緊時間上車哦!

技本功丨知否知否,Redux源碼竟如此意味深長(上集)

在講源碼之前我們首先回顧一下redux是如何使用的,下面我們看一下使用demo:

技本功丨知否知否,Redux源碼竟如此意味深長(上集)

上面這段代碼完整地展示了redux的使用(如果有小夥伴對這段代碼不是很了解的話,建議先去學習redux的使用再來看這篇源碼,這樣更加事半功倍)。通過上段代碼,我們拆分幾個比較核心的點,我一一列舉一下:

1. action的結構是如何的?

2. 如何去定義一個reducer?

3. combineReducers是如何整合多個reducer的?

4. createStore是如何建立一個store?

5.dispatch拿到action到底幹了什麼?

6. subscribe是如何監聽狀态發生改變的?

7. getState是如何拿到所有的狀态值的?

本期先解決前三個疑問,讓我們一起去源碼裡尋找答案!

1、action的結構是如何的?

首先得解釋一下action是幹嘛的,它是負責把資料從應用帶到store裡面,也是store的唯一資料來源,并由以下兩個部分組成:

1. type  (操作類型)

2. payload (攜帶的資料)

為什麼得有這兩個?其實也很好了解,我們拿銀行來類比。某天,你拿着一萬塊來到銀行,走到櫃台,人業務員第一件事肯定是問你要辦啥業務,存錢?轉賬?還是還貸?你得把這些告訴業務員,不然業務沒法給你辦理業務,是以我們action就得有一個type,好讓reducer知道你要幹啥。當然,你辦理存款或者是還款啥的,必不能少的就是毛爺爺了,payload對應的值就好比這些毛爺爺。用一個話來總結action的作用就是:告訴reducer拿着payload去做type這件事。

2、如何去定義一個reducer?

上面講action的時候,提到了reducer了,這裡還是拿我上面的銀行做個類比,當我們拿着錢去銀行存錢,我們不可能自己去銀行把銀行保險櫃打開,完了把錢放進去,這樣是不允許的,我們得需要業務員這個中間人去幫我們做存錢這件事,而業務員所扮演的角色正好就是reducer所要擔任的角色。接下來講一下如何去定義一個reducer,其實reducer的寫法并沒有絕對的寫法,隻要符合下面幾個條件都能稱之為reducer:

1. 必須得是一個函數。

2. 函數接收兩個參數。第一個:該reducer所負責的state。第二個:action。

3. 函數體内部可以針對不同的action的type做出響應,這裡你可以if-else或者switch-case都是可以的。

4. 函數必須有傳回值。當修改state了之後,必須将修改後的state傳回。如果遇到未知的type則需要傳回一個預設值。 

3、combineReducers是如何整合多個reducer的?

我們先看一下combineReducers傳入的參數:

技本功丨知否知否,Redux源碼竟如此意味深長(上集)

combineReducers接受的是一個參數首先得是對象,其次該對象每一個屬性對應一個reducer。搞清楚combineReducers的結構之後,我們再看一下combineReducers對其做了哪些處理。

第一步:淺拷貝reducers

技本功丨知否知否,Redux源碼竟如此意味深長(上集)

這裡定義了一個finalReducers和finalReducerKeys,分别用來拷貝reducers和其屬性。先用Object.keys方法拿到reducers所有的屬性,然後進行for循環,每一項可根據其屬性拿到對應的reducer,并淺拷貝到finalReducers中,但是前提條件是每個reducer的類型必須是Function,不然會直接跳過不拷貝。

第二步:檢測finalReducers裡的每個reducer是否都有預設傳回值

技本功丨知否知否,Redux源碼竟如此意味深長(上集)

assertReducerShape方法主要檢測兩點:

1. 不能占用redux内部特有的命名空間

2. 如果遇到未知的action的類型,reducer不能傳回undefined,得傳回預設的值

如果傳入type為 @@redux/INIT<随機值> 的action,傳回undefined,說明沒有對未 知的action的類型做響應,需要加預設值。如果對應type為 @@redux/INIT<随機值> 的action傳回不為undefined,但是卻對應type為 @@redux/PROBE_UNKNOWN_ACTION_<随機值> 傳回為undefined,說明占用了 命名空間。整個邏輯相對簡單,好好自己梳理一下。

第三步:傳回一個函數,用于代理所有的reducer

技本功丨知否知否,Redux源碼竟如此意味深長(上集)

先對傳入的state用getUnexpectedStateShapeWarningMessage做了一個異常檢測,找出state裡面沒有對應reducer的key,并提示開發者做調整。接着我們跳到getUnexpectedStateShapeWarningMessage裡,看其實作。

技本功丨知否知否,Redux源碼竟如此意味深長(上集)

getUnexpectedStateShapeWarningMessage接收四個參數inputState(state)、reducers(finalReducers)、action(action)、unexpectedKeyCache(unexpectedKeyCache),這裡要說一下unexpectedKeyCache是上一次檢測inputState得到的其裡面沒有對應的reducer集合裡的異常key的集合。整個邏輯如下:

1. 前置條件判斷,保證reducers集合不為{}以及inputState為簡單對象

2. 找出inputState裡有的key但是 reducers集合裡沒有key

3. 如果是替換reducer的action,跳過第四步,不列印異常資訊

4. 将所有異常的key列印出來

getUnexpectedStateShapeWarningMessage分析完之後,我們接着看後面的代碼。

技本功丨知否知否,Redux源碼竟如此意味深長(上集)

首先定義了一個hasChanged變量用來表示state是否發生變化,周遊reducers集合,将每個reducer對應的原state傳入其中,得出其對應的新的state。緊接着後面對新的state做了一層未定義的校驗,函數getUndefinedStateErrorMessage的代碼如下:

技本功丨知否知否,Redux源碼竟如此意味深長(上集)

邏輯很簡單,僅僅做了一下錯誤資訊的拼接。未定義校驗完了之後,會跟原state作對比,得出其是否發生變化。最後發生變化傳回nextState,否則傳回state。

未完待續

下期預告

《技本功丨知否知否,Redux源碼竟如此意味深長(下集)》

THE END

最後,

袋萌萌感謝每一位老鐵2018年的陪伴,

生死看淡,不服就幹