這是我參與8月更文挑戰的第26天,活動詳情檢視:8月更文挑戰
前言
在開啟本篇的正文之前我們先做一個實驗,我們來定義一個空的 Action
,也就是在 Reducer
中。這個 Action
什麼也不做。然後我們在頁面的Widget
的build
方法列印對應的資訊在頁面,通過響應按鈕點選來排程這個 Action
。大家猜猜依賴於 Store
的元件是否會被重建(Rebuild
)?
我們先看一下執行結果。
答案是:**如果你什麼都不做那就會重建。**這肯定會讓我們覺得奇怪,為什麼狀態資料沒有改變,還是會做重建操作呢?
Redux 重新整理機制
要回答上面的問題,我們就需要了解 Redux 的重新整理機制,來看StoreConnector
原始碼中的定義:
Every time the store changes, the Widget will be rebuilt. As a performance optimization, the Widget can be rebuilt only when the [ViewModel] changes. In order for this to work correctly, you must implement [==] and [hashCode] for the [ViewModel], and set the [distinct] option to true when creating your StoreConnector.
英文看不懂,沒關係,給你翻譯翻譯:
每次
Store
改變的時候,元件都會被重建。為了保證只有在ViewModel
改變的時候,元件才會被重建。為了讓這種方式發揮作用,需要對ViewModel
實現[==]
和hashCode
方法,並且在StoreConnector
中設定distinct
屬性為true
。
也就是我們為了在狀態沒改變的時候避免重建,需要做兩個事情:
ViewModel
需要過載操作符==
方法和hashCode
方法,以根據這兩個方法判斷是否改變。- 設定
distinct
為true
。
第一項好理解,第二項則是因為在 Store 的定義中說的:
If set to true, the Store will not emit onChange events if the new State that is returned from your [reducer] in response to an Action is equal to the previous state. 如果設定為
true
,reducer
中針對一個Action
返回的狀態和之前的狀態相等的話,那麼Store
不會傳送onChange
事件。
減少重新整理來優化效能
知道了上面兩點,我們來對購物清單應用的程式碼做一下改造,首先是在 Store 定義的時候設定 distinct
為 true
。
final store = Store<ShoppingListState>(
shoppingListReducer,
initialState: ShoppingListState.initial(),
middleware: shopplingListMiddleware(),
distinct: true,
);
複製程式碼
之後對狀態類 ShoppingListState
實現==
和 hashCode
方法。
bool operator ==(Object? other) {
if (other == null || !(other is ShoppingListState)) return false;
if (other.shoppingItems.length != shoppingItems.length) return false;
for (int i = 0; i < other.shoppingItems.length; i++) {
if (other.shoppingItems[i] != shoppingItems[i]) return false;
}
return true;
}
@override
get hashCode => shoppingItems.hashCode;
複製程式碼
其中==
操作符需要比對清單資料中的每一項是否都想等,只要有一項不相等,就返回 false
。這意味著我們的 ShoppingItem
也需要改造, 實現==
和 hashCode
方法。
bool operator ==(Object? other) {
if (other == null || !(other is ShoppingItem)) return false;
return other.name == this.name &&
other.selected == this.selected &&
other.count == this.count;
}
@override
get hashCode => this.toJson().hashCode;
複製程式碼
好了,來看看效果,這個時候發現列表和底部的統計欄都沒有再呼叫 build
方法了。
總結
對於全域性的 Store
來說,如果使用不慎可能導致全域性重新整理,因此良好的習慣是設定Store
的 distinct
屬性為 true
,並且過載狀態物件的==
和 hashCode
方法,以減少不必要的重新整理,優化效能。
我是島上碼農,微信公眾號同名,這是Flutter 入門與實戰的專欄文章,對應原始碼請看這裡:Flutter 入門與實戰專欄原始碼。
??:覺得有收穫請點個贊鼓勵一下!
?:收藏文章,方便回看哦!
?:評論交流,互相進步!