Redux進階(一)

發表於2018-08-26

State的不可變化帶來的麻煩

在用Redux處理深度複雜的資料時會有一些麻煩。由於js的特性,我們知道當對一個物件進行復制時實際上是複製它的引用,除非你對這個物件進行深度複製。Redux要求你每次你返回的都是一個全新的State,而不是去修改它。這就要求我們要對原來的State進行深度複製。這往往帶來複雜的操作(查詢,合併)。一種簡單的情況是通過擴充套件符號或者Object.assign來處理:

這種方式在處理簡單的資料來說是比較方便的,但是如果遇到更深一級的資料結構時會顯得很無力,而且程式碼會變得冗長。不僅僅如此,當我們要處理一個包含著物件的陣列時,我們要怎麼辦才好呢?我想,除了深度複製陣列然後修改新的陣列之外大概沒有其他的方法了吧?而且很重要的一點是,如果你對原來整個陣列進行了複製,那麼繫結了資料的UI會自動渲染,即使它們的資料沒有發生變化,簡單來說,就是你修改了某一條表格資料,但是介面上整個表格被重新渲染了:

在Redux官方文件中提到了一種解決方案,即正規化化資料:概括起來就一句話:減少層級,唯一id索引,用後端建表的方法構建我們的資料結構。其中最重要原則無非是扁平化和關聯性。最終我們需要將資料形式轉化成以下格式:

按照滷煮的理解,正規化化資料無非就是給物件瘦瘦身,再深的層級,我們也儘量將它們扁平化,這樣會減少我們對State的查詢帶來的效能消耗。然後是建立索引表,標識每組資料之間的聯絡。那麼怎麼樣才能得到我們想要的資料呢?

normalizr方法使用指南

官方最薦normalizr模組,它的用法還是需要時間的去研究的。下面我們就以上面的資料為示例,說明它的用法:

說明:new schema.Entity的第一個參數列示你建立的最外層的實體名稱,第二個引數是它和其他新建立的實體的關係,預設是最小的層級,即它只是最後一層,不包含其他層級了。第三個引數裡面有個idAttribute,指的是以哪個欄位為索引,預設是”id”,它也可以是個引數,返回你自己構造的唯一值,記住,是唯一值。按照這樣的套路,你可以隨意構建你想要的扁平化資料結構,無論源資料的層級有多深。我們最終都會得到希望的資料結構。

github上有詳細的官方文件可供查詢,滷煮在此只是簡單的說明一下用法,諸位可以仔細觀察文件上的用法,不需要多少時間就可以熟練掌握。到此為止,萬里長城,終於走完了第一步。

如何將正規化化資料後再次轉化

什麼?好不容易轉化成自己想要的資料結構,還需要再次轉化嗎?很遺憾告訴你,是的。因為正規化化後的資料只便於我們在維護Redux,而介面業務渲染的資料結構往往跟我們處理後的資料是不一樣的,舉個例子:bootstrap或者ant.design的表格渲染資料結構是這個樣的:

因而在介面引用State上的資料時,我們需要一箇中介,把正規化化的資料再次轉化成業務資料結構。我相信這個步驟十分簡單,只需要寫一個簡單的轉換器就行了:

如果你在mapStateToProps裡面斷點除錯,你會發現每一次dispatch都會強行執行mapStateProps方法保證物件的最新狀態(除非你引用的是基礎型別資料),因此,不管介面的操作是如何,被connect資料都會被強行執行一次,雖然介面沒有變化,但是顯然,js的效能會有折扣,尤其是對深度物件的複雜處理。因此,官方推薦我們建立可記憶的函式高效計算Redux Store裡面的衍生資料。

Reselect方法使用指南

我在這裡做了個耍了點花樣,你可以看到,我是按照table.data這個陣列來查詢介面業務資料的。這種操作可以使得我們只需要關心table.data這個簡單的一維陣列,在刪除或者新增一條資料的時候顯得尤為有用。

我們最後為了計算state,引用了dot-prop-immutable模組,他是immutable的擴充套件,對於資料計算非常高效。我接著使用了另外一個dot-prop-immutable-chain模組,它增加了dot-prop-immutable的鏈式用法。關於dot-prop-immutable的用法滷煮不再詳細說明,在後面給出的例子中一眼就能看明白,而且官方文件上也有詳細說明。下面我們通過一個表格增刪查改來實際展示我們這次的解決方案。

瞧,我們展示了整個reducer,相信它已經變得容易維護得多了,並且由於使用了正規化化資料結構以及immutable的擴充套件模組,不僅僅提升了計算效能,減少介面的的渲染,而且還符合Redux的State不可修改的原則。

結束語

React+Redux組合在實際應用過程中需要優化的地方還很多,這裡只是簡單展示其中的一個小點。雖然在計算dom介面變化時React已經做得足夠好,但並不意味著我們可以不用為介面渲染問題背鍋,React肩負了多數介面更新計算的任務,而讓前端開發人員更多地去處理資料,因此,我們可以在這裡層多花點時間把專案做好。

參考資料

 Redux中文文件

把你的redux store 組織成資料庫形式

normalizr在GitHub上的地址

reselect在GitHub上的地址

dot-prop-immutable在GitHub上的地址

相關文章