這是我參與8月更文挑戰的第6天,活動詳情檢視:8月更文挑戰
前言
上兩篇我們介紹了使用 InheritedWidget
深入狀態管理,並且解耦了元件和狀態管理,從而使得程式碼更易於維護,而且還能實現區域性重新整理的效果。上兩篇文章傳送門:
到底能不能實現區域性重新整理呢嗎,我們本篇通過一個示例來驗證一下。
假設
回到我們前一個故事 —— 小芙和雷思相親的故事,假設我們的雷思不是那麼木,他能夠讀懂小芙的心思(共享狀態),那麼也許結局可能就不是之前那樣。所謂心有靈犀一點通,我們分別通過ModelBinding
和初級的setState
方式來實現這樣的效果。
使用 ModelBinding 實現狀態共享
為了使用 ModelBinding
實現狀態共享,我們需要將雷思和小芙作為ModelBinding
的子元件。同時,我們另外寫了一個不依賴於狀態的元件(StatelessNoDepend
),也放直在 ModelBinding
的子元件中,以便驗證區域性重新整理是否有效。
class StatefulStatelessDemoPage extends StatelessWidget {
StatefulStatelessDemoPage({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('3年後'),
),
body: ModelBindingV2(
child: Column(
children: [
Xiaofu3(),
Leisi(),
StatelessNoDepend(),
],
),
create: () => FaceEmotion(emotion: '驚訝'),
),
);
}
}
複製程式碼
同時,在雷思(Leisi) 元件中,我們通過一個文字來顯示狀態中的資料,這裡是一個情緒描述字串。這個就是從狀態管理中共享得到,因為依賴於狀態,因此在狀態改變的時候會重新build。
@override
Widget build(BuildContext context) {
print('ModelBinding=====build:雷思');
return Center(
child: Text(
'ModelBinding=====雷思感受到了小芙的${ModelBindingV2.of<FaceEmotion>(context).emotion}'),
);
}
複製程式碼
同樣的,在小芙(Xiaofu3)中,我們也會讀取狀態的情緒字串,以及使用了一個按鈕來改變情緒,從而使得元件重新整理。
@override
Widget build(BuildContext context) {
print('ModelBinding=====build:小芙');
return Center(
child: Column(children: [
Text(
'ModelBinding=====小芙的表情:${ModelBindingV2.of<FaceEmotion>(context).emotion}'),
TextButton(
onPressed: () {
ModelBindingV2.update<FaceEmotion>(
context, FaceEmotion(emotion: '高興'));
},
child: Text('小芙表情變了')),
]),
);
}
複製程式碼
為了看是否真的被重建,我們在三個元件(Leisi,Xiaofu3和StatelessNoDepend)中的build
方法裡列印了一個資訊,同時我們在建構函式也列印了構造資訊。執行後,我們點選按鈕,整個列印的資訊如下:
flutter: ModelBinding=====constructor: 小芙
flutter: ModelBinding=====constructor: 雷思
flutter: constructor: 不依賴狀態的元件
flutter: ModelBinding=====build:小芙
flutter: ModelBinding=====build:雷思
flutter: build:不依賴於狀態的元件
-------------------------------
flutter: ModelBinding=====build:小芙
flutter: ModelBinding=====build:雷思
複製程式碼
分隔線以下是點選按鈕後的列印資訊,可以看到,第一次載入的時候,三個元件都 build
方法都被呼叫了。而點選按鈕後,只有 Leisi 和 Xiaofu 的build
方法被呼叫,這說明了確實實現了區域性重新整理的效果 —— 不依賴於狀態的元件不會被重建。
使用 setState 共享狀態
使用 setState
的話會要複雜一點,我們需要通過父元件將資料傳遞給子元件,然後在狀態發生改變的時候,需要呼叫 setState
方法更新子元件,這個時候還需要父元件的更新狀態方法傳遞到更改狀態的子元件裡。顯然,耦合度是很高的。
class SetStateDemo extends StatefulWidget {
SetStateDemo({Key key}) : super(key: key);
_SetStateDemoState createState() => _SetStateDemoState();
}
class _SetStateDemoState extends State<SetStateDemo> {
FaceEmotion faceEmotion = FaceEmotion();
void updateEmotion(FaceEmotion newEmotion) {
if (faceEmotion != newEmotion) {
setState(() {
faceEmotion = newEmotion;
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('setState 方式'),
),
body: Column(
children: [
StatelessXiaofu(face: faceEmotion, updateEmotion: updateEmotion),
StatelessLeisi(face: faceEmotion),
StatelessNoDepend(),
],
),
);
}
}
複製程式碼
剩下的程式碼很簡單,就不貼了,現在看進入頁面和點選按鈕更改狀態後的整個過程。
flutter: setState=====constructor:小芙
flutter: setState=====constructor: 雷思
flutter: constructor: 不依賴狀態的元件
flutter: setState=====build:小芙
flutter: setState=====build:雷思
flutter: build:不依賴於狀態的元件
------------------------------
flutter: setState=====constructor:小芙
flutter: setState=====constructor: 雷思
flutter: constructor: 不依賴狀態的元件
flutter: setState=====build:小芙
flutter: setState=====build:雷思
flutter: build:不依賴於狀態的元件
複製程式碼
可以看到,第一次進入頁面的過程和ModelBinding
是一樣的。但是,在點選按鈕呼叫setState
方法的時候就完全不一樣了,使用ModelBinding
方法只是呼叫了 依賴於狀態的build
方法,而setState
之後全部子元件被重新構造了一遍,也就是移除後再插入了新的元件——這就效能消耗和 ModelBinding
相比,肯定高很多。那麼,setState
的過程到底發生了什麼?我們下一篇通過原始碼來分析一下。
總結
本篇對比了使用 InheritedWidget 實現狀態共享和使用 setState 方式實現狀態共享的區別,很明顯,使用 InheritedWidget的方式效能更高,可以實現區域性重新整理,而且不會出現 setState 那種重構整個元件樹的情況。這個特點十分重要,意味著我們要儘可能地避免在高層級的元件上直接使用 setState重新整理介面,而是要依賴於狀態管理實現區域性重新整理。當然,如果這個元件本身是元件樹的葉子節點,那麼使用 setState 不會有什麼效能損失,這個時候倒是沒必要非得使用狀態管理工具。
我是島上碼農,微信公眾號同名,這是Flutter 入門與實戰的專欄文章。
??:覺得有收穫請點個贊鼓勵一下!
?:收藏文章,方便回看哦!
?:評論交流,互相進步!