Flutter State Management狀態管理全面分析

i校長發表於2020-05-21

前言

Flutter 狀態管理是個很熱門的話題,而且狀態管理的庫也是超級多,這確實是我們每一個做Flutter開發難以避免的一道坎,既然這麼重要,我們如何去理解它,如何使用它,如何做到更好呢?接下來讓我告訴你答案

主要內容

Flutter State Management狀態管理全面分析

一張圖告訴你,我要講的主要內容。

是什麼

程式=演算法+資料結構 同意嗎?同意請往下看,不同意請點右上角‘X’關閉頁面,謝謝?,哈哈開個玩笑。 Flutter=演算法+資料結構 Flutter狀態管理=演算法+資料結構+UI對映
瞬間秒懂有沒有?不懂?請點右上角‘X’關閉頁面,謝謝? 演算法就是我們如何管理,資料結構就是狀態,狀態管理的本質還是如何通過合理的演算法管理資料,如何取,如何接收等,最終展示在UI上,通過UI的變更來體現狀態的管理邏輯。

為什麼需要

  • 隨著業務的不斷迭代,資料趨於複雜,我們已經無法清晰的測試和尋找資料的來源,因為頁面之前的資料錯綜複雜,慢慢的你就失去了主動權,被這些複雜的資料牽著鼻子走,迷失方向
  • 跨頁面資料傳遞,如:A頁面需要用到B頁面的輸入資料
  • 跨頁面資料共享,如:A頁面和B頁面需要共享App的主題資料
  • 資料與檢視分離 你有沒有注意到一個核心問題,在Android、Ios中我們是如何變更資料的?來看個例子
//android
TextView tv = TextView()
tv.setText("text")
複製程式碼
///flutter
setState{
    text = "text"
}
複製程式碼

其實Android、Ios與flutter的本質的區別就是資料與檢視完全分離,你仔細想一下,這樣設計有什麼弊端? 對了你猜對了:頁面如何重新整理才是Flutter的關鍵效能所在,Android、Ios已經很明確的告訴UI要重新整理什麼更新什麼,而Flutter沒有說,只是更新了資料來源,那麼為了能做到高效能重新整理UI,狀態管理就出來了,還是從前那個少年,setState一遍又一遍,狀態管理幫你做到UI的重新整理(高效能的重新整理)。

基本分類

  • 區域性管理 如:登入頁面的使用者名稱與密碼就屬於區域性管理
  • 全域性管理 如:登入頁面的登入狀態,是一個全域性狀態,其他頁面都需要,也可以是區域性,不要太糾結這個問題

底層邏輯

  • State
    StatefulWidget、StreamBuilder狀態管理方式
  • InheritedWidget 專門負責Widget樹中資料共享的功能型Widget,如Provider、scoped_model就是基於它開發
  • Notification 與InheritedWidget正好相反,InheritedWidget是從上往下傳遞資料,Notification是從下往上,但兩者都在自己的Widget樹中傳遞,無法跨越樹傳遞。
  • Stream 資料流 如Bloc、flutter_redux、fish_redux等也都基於它來做實現 為什麼列這些東西?因為現在大部分流行的狀態管理都離不開它們。理解它們比理解那些吹自己牛逼的框架要好的多。請關注底層邏輯,這樣你才能遊刃有餘。下面我們一個個分析一下:

State

State 是我們常用而且使用最頻繁的一個狀態管理類,我們經常會用StatefulWidget來管理狀態的變化,StreamBuilder繼承自StatefulWidget也不用多說它的原理。

為什麼會有它呢?請看Flutter渲染步驟。 Widget=>Element=>RenderObject Widget本身是不變的,那麼就需要一個角色來控制它,State就出現了,State的任何更改都會強制整個Widget重新構建

注意 是整個Widget重新構建(而且子Widget也會跟著銷燬重建),這個點也是為什麼不推薦你大量使用StatefulWidget的原因。如果頁面足夠複雜,就會導致嚴重的效能損耗。如何優化呢?建議使用StreamBuilder,它原理上也是State,但它做到了子Widget的區域性重新整理,不會導致整個頁面的重建,是不是就好很多了呢?

State缺點

  • 無法做到跨元件共享資料
  • setState會成為維護的難點,因為啥哪哪都是。
  • 處理資料邏輯和檢視混合在一起,違反程式碼設計原則

InheritedWidget

InheritedWidget是一個無私的Widget,它可以把自己的狀態資料,無私的交給所有的子Widget,所有的子Widget可以無條件的繼承它的狀態。就這麼一個東西。

注意 它的資料是隻讀的,雖然很無私,但子widget不能修改,呵呵。 說到它就不得不提InheritedModel,它是InheritedWidget的子類,InheritedModel可以做到部分資料改變的時候才會重建

推薦閱讀 inheritedmodel-vs-inheritedwidget juju.one/inheritedwi…

InheritedWidget 缺點

  • 容易造成不必要的重新整理
  • 不支援跨頁面(route)的狀態
  • 資料是不可變的,必須結合StatefulWidget、ChangeNotifier或者Steam使用

Notification

它是Flutter中跨層資料共享的一種機制,注意,它不是widget,它就是它,不一樣的傻叉,它提供了dispatch方法,來讓我們沿著context對應的Element節點向上逐層傳送通知

推薦閱讀 flutter-notifications-bubble-up-and-values-go-down notification

Notification缺點

  • 不支援跨頁面(route)的狀態

Stream

它其實是純Dart的實現,跟Flutter沒什麼關係,扯上關係的就是用StreamBuilder來構建一個Stream通道的Widget,像知名的rxdart、BloC、flutter_redux全都用到了Stream的api。所以學習它才是我們掌握狀態管理的一個關鍵

推薦閱讀 我自己寫的StreamBuilder原始碼分析 大神寫的Stream全面分析

Stream 缺點

  • api生澀,不好理解
  • 需要定製化,才能滿足更復雜的場景

缺點恰恰是它的優點,保證了足夠靈活,你更可基於它做一個好的設計,滿足當下業務的設計。

小結

通過對State、InheritedWidget、Notification、Stream的學習,你是不是覺得,Flutter的狀態管理也就這些了呢?不一定哈,我也在不斷的學習,如果碰到新的技術,繼續分享給你們哦。難道這就完了嗎?沒有,其實我們只是學了第一步,是什麼,如何用,還沒有討論怎麼用好呢?需要什麼標準嗎,當然有,下面通過我的專案實戰經驗來提出一個基本原則,超過這個原則你就是在破壞平衡,請往下看。

狀態管理的使用原則

區域性管理猶於全域性

這個原則來源於,Flutter的效能優化,區域性重新整理肯定比全域性重新整理要好很多,那麼我們在管理狀態的同時,也要考慮該狀態到底是區域性還是全域性,從而編寫正確的邏輯。

保持資料安全性

用“_”私有化狀態,因為當開發人員眾多,當別人看到你的變數的時候,第一反應可能不是找你提供的方法,而是直接對變數操作,那就有可能出現想不到的後果,如果他只能呼叫你提供的方法,那他就要遵循你方法的邏輯,避免資料被處理錯誤。

考慮頁面重新build帶來的影響

很多時候頁面的重建都會呼叫build函式,也就是說,在一個生命週期內,build函式是多次被呼叫的,所以你就要考慮資料的初始化或者重新整理怎麼樣才能合理。

使用成熟狀態管理庫弊端

  • 增加程式碼複雜性
  • 框架bug修復需要時間等待
  • 不理解框架原理導致使用方式不對,反而帶來更多問題
  • 選型錯誤導致不符合應用要求
  • 與團隊風格衝突不適用 通過了解它們的弊端來規避一些風險,綜合考慮,選框架不易,且行且珍惜。

選型原則

  • 侵入性
  • 擴充套件性
  • 高效能
  • 安全性
  • 駕馭性
  • 易用性
  • 範圍性

所有的框架都有侵入性,你同意嗎?不同意請左轉,前面有個坑,你可以跳過去。目前侵入性比較高的代表ScopedModel,為啥?因為它是用extend實現的,需要繼承實現的基本不是什麼好實現,你同意嗎?同上。 擴充套件性就不用說了,如果你選擇的框架只能使用它提供的幾個入口,那麼請你放棄使用它。高效能也是很重要的,這個需要明白它的原理,看它到底如何做的管理。安全性也很重要,看他資料管理通道是否安全穩定。駕馭性,你說你都不理解你就敢用,出了問題找誰?如果駕馭不了也不要用。易用性大家應該都明白,如果用它一個框架需要N多配置,N多實現,放棄吧,不合適。簡單才是硬道理。

範圍性 這個特點是flutter中比較明顯的,框架選型一定要考慮框架的適用範圍,到底是適合做區域性管理,還是適合全域性管理,要做一個實際的考量。

推薦用法

如果是初期,建議多使用Stream、State、Notification來自行處理,順便學習原始碼,多理解,多實踐。有架構能力的就可以著手封裝了,提供更簡單的使用方式 如果是後期,當然也是在前面的基礎之上,再去考慮使用Provider、redux等複雜的框架,原則上要吃透原始碼,否則不建議使用。

注意

你以為使用框架就能萬事大吉了?效能優化是一個不變的話題,包括Provider在內的,如果你使用不當,照樣出現頁面的效能損耗嚴重,所以你又回到了為啥會這樣,請你學習上面的底層邏輯,謝謝?

總結

通過這期分享,你是不是對Flutter的狀態管理有了一個重新的認識呢?如果對你有幫住,請點一下下面的贊哦。謝謝?。

相關文章