Flutter 入門與實戰(六十三):Redux之利用 distinct 屬性進行效能優化

島上碼農發表於2021-08-26

這是我參與8月更文挑戰的第26天,活動詳情檢視:8月更文挑戰

前言

在開啟本篇的正文之前我們先做一個實驗,我們來定義一個空的 Action,也就是在 Reducer 中。這個 Action 什麼也不做。然後我們在頁面的Widgetbuild 方法列印對應的資訊在頁面,通過響應按鈕點選來排程這個 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 方法,以根據這兩個方法判斷是否改變。
  • 設定 distincttrue

第一項好理解,第二項則是因為在 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. 如果設定為 truereducer 中針對一個 Action返回的狀態和之前的狀態相等的話,那麼 Store 不會傳送 onChange 事件。

減少重新整理來優化效能

知道了上面兩點,我們來對購物清單應用的程式碼做一下改造,首先是在 Store 定義的時候設定 distincttrue

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 來說,如果使用不慎可能導致全域性重新整理,因此良好的習慣是設定Storedistinct 屬性為 true,並且過載狀態物件的==hashCode 方法,以減少不必要的重新整理,優化效能。


我是島上碼農,微信公眾號同名,這是Flutter 入門與實戰的專欄文章,對應原始碼請看這裡:Flutter 入門與實戰專欄原始碼

??:覺得有收穫請點個贊鼓勵一下!

?:收藏文章,方便回看哦!

?:評論交流,互相進步!

相關文章