一、核心原因:性能優(yōu)化
Flutter 每秒可能重建 60 次 UI(60fps),如果 Widget 可變,性能會(huì)崩潰。
對(duì)比:可變 vs 不可變
// ? 如果 Widget 可變會(huì)怎樣?
class Counter extends Widget {
int count = 0; // 可變
void increment() {
count++; // 直接修改
// 問題:Flutter 怎么知道需要重建?
// 需要監(jiān)聽每個(gè)屬性的變化 → 性能災(zāi)難
}
}
// ? Widget 不可變的設(shè)計(jì)
class Counter extends StatefulWidget {
final int initialCount; // 不可變配置
const Counter({required this.initialCount});
}
class _CounterState extends State<Counter> {
late int _count;
void increment() {
setState(() { // 明確告訴 Flutter 需要重建
_count++;
});
}
}
二、五大設(shè)計(jì)優(yōu)勢(shì)
1. 高效的對(duì)比算法(Diffing)
不可變 Widget 可以用 == 快速對(duì)比
// Widget 不可變,可以直接對(duì)比引用
Widget oldWidget = Text('Hello');
Widget newWidget = Text('Hello');
// Flutter 的對(duì)比邏輯
if (oldWidget.runtimeType != newWidget.runtimeType) {
// 類型不同,完全重建
} else if (oldWidget.key != newWidget.key) {
// key 不同,重建
} else {
// 對(duì)比屬性(因?yàn)椴豢勺?,可以淺對(duì)比)
if (oldWidget == newWidget) {
// 完全相同,跳過重建!?
}
}
如果 Widget 可變
// ? 可變 Widget 需要深度對(duì)比每個(gè)屬性
class MutableText extends Widget {
String text;
Color? color;
double? fontSize;
// ... 100 個(gè)屬性
@override
bool shouldRebuild(MutableText old) {
// 需要對(duì)比所有屬性!??
return text != old.text ||
color != old.color ||
fontSize != old.fontSize ||
// ... 對(duì)比 100 個(gè)屬性
}
}
2. 安全的并發(fā)處理
Flutter 在多個(gè) Isolate 中可能同時(shí)訪問 Widget 樹
// ? 不可變 Widget:線程安全
const myWidget = Text('Hello'); // 可以安全地在多線程中共享
// ? 可變 Widget:需要加鎖
class MutableWidget {
String text;
final _lock = Lock();
void updateText(String newText) {
_lock.synchronized(() { // 性能損失
text = newText;
});
}
}
3. 簡(jiǎn)化的內(nèi)存管理
不可變對(duì)象可以安全共享,減少內(nèi)存分配
// 不可變 Widget 可以復(fù)用
const divider = Divider(); // 編譯時(shí)常量
Widget build(BuildContext context) {
return Column(
children: [
Text('Item 1'),
divider, // 復(fù)用同一個(gè)實(shí)例
Text('Item 2'),
divider, // 復(fù)用同一個(gè)實(shí)例
Text('Item 3'),
divider, // 復(fù)用同一個(gè)實(shí)例
],
);
}
// 如果可變,每個(gè)位置都需要獨(dú)立實(shí)例
// 否則修改一個(gè)會(huì)影響所有!
4. 可預(yù)測(cè)的行為
不可變對(duì)象的狀態(tài)永遠(yuǎn)不會(huì)意外改變
// ? 不可變:行為可預(yù)測(cè)
class Parent extends StatelessWidget {
@override
Widget build(BuildContext context) {
final child = Text('Hello');
return Column(
children: [
child,
SomeWidget(child: child), // 傳遞給子組件
child, // 仍然是 'Hello',不會(huì)被 SomeWidget 修改
],
);
}
}
// ? 可變:行為不可預(yù)測(cè)
class MutableParent {
Widget build() {
final child = MutableText('Hello');
return Column(
children: [
child,
SomeWidget(child: child), // SomeWidget 可能修改 child.text
child, // ?? 可能已經(jīng)變成 'Goodbye' 了!
],
);
}
}
5. 聲明式 UI 的基礎(chǔ)
不可變?cè)O(shè)計(jì)讓 UI = f(state) 成為可能
// 聲明式:UI 完全由狀態(tài)決定
Widget build(BuildContext context) {
return Text(
_count.toString(), // UI 是狀態(tài)的純函數(shù)
);
}
// 如果 Widget 可變,就變成命令式
void updateUI() {
myText.text = _count.toString(); // 手動(dòng)修改 UI
myText.color = Colors.red;
myText.fontSize = 20;
// ... 需要手動(dòng)管理所有變化
}
三、實(shí)戰(zhàn)案例:性能對(duì)比
場(chǎng)景:列表滾動(dòng)(每秒重建 60 次)
// ? 不可變 Widget:高效
class ImmutableList extends StatelessWidget {
final List<String> items; // 不可變
const ImmutableList({required this.items});
@override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
// Flutter 可以快速判斷是否需要重建
return Text(items[index]); // const 構(gòu)造函數(shù)
},
);
}
}
// 性能分析
// - Widget 對(duì)比:O(1) 引用對(duì)比
// - 內(nèi)存分配:可以復(fù)用 const 實(shí)例
// - 線程安全:無需加鎖
// ? 可變 Widget:低效
class MutableList extends Widget {
List<MutableText> items; // 可變
void updateItem(int index, String text) {
items[index].text = text; // 修改現(xiàn)有對(duì)象
// 問題:
// 1. Flutter 不知道哪個(gè) item 變了
// 2. 需要重建整個(gè)列表
// 3. 需要深度對(duì)比所有屬性
}
}
// 性能分析
// - Widget 對(duì)比:O(n) 深度對(duì)比
// - 內(nèi)存分配:無法復(fù)用,頻繁 GC
// - 線程安全:需要加鎖,性能損失
四、Flutter 的智能優(yōu)化
1. const 構(gòu)造函數(shù)優(yōu)化
// 編譯時(shí)創(chuàng)建,整個(gè)應(yīng)用共享一個(gè)實(shí)例
const text1 = Text('Hello');
const text2 = Text('Hello');
print(identical(text1, text2)); // true!同一個(gè)對(duì)象
// 性能提升
// - 零內(nèi)存分配
// - 零 GC 壓力
// - 對(duì)比速度:O(1)
2. Widget 緩存
class MyWidget extends StatelessWidget {
// 緩存不變的子 Widget
static const _header = Text('Header');
static const _footer = Text('Footer');
@override
Widget build(BuildContext context) {
return Column(
children: [
_header, // 復(fù)用
DynamicContent(), // 動(dòng)態(tài)部分
_footer, // 復(fù)用
],
);
}
}
3. Element 復(fù)用
// Widget 不可變,但 Element 可以復(fù)用
Widget build1() => Text('Hello'); // Widget 實(shí)例 A
Widget build2() => Text('Hello'); // Widget 實(shí)例 B(新對(duì)象)
// Flutter 的處理
// 1. 對(duì)比 Widget A 和 B(因?yàn)椴豢勺?,快速?duì)比)
// 2. 發(fā)現(xiàn)內(nèi)容相同
// 3. 復(fù)用 Element,不重建 RenderObject
// 4. 性能提升:跳過昂貴的渲染操作
五、與其他框架對(duì)比
React(類似設(shè)計(jì))
// React 也推薦不可變
function Counter() {
const [count, setCount] = useState(0);
// ? 不可變更新
setCount(count + 1);
// ? 可變更新(不推薦)
count++; // React 不會(huì)檢測(cè)到變化
}
Android View(可變?cè)O(shè)計(jì)的問題)
// Android 傳統(tǒng) View:可變
TextView textView = new TextView(context);
textView.setText("Hello"); // 直接修改
// 問題:
// 1. 需要手動(dòng)管理狀態(tài)同步
// 2. 容易出現(xiàn)不一致
// 3. 難以優(yōu)化性能
六、常見疑問
Q1:不可變會(huì)不會(huì)浪費(fèi)內(nèi)存?
A:不會(huì),反而更省內(nèi)存
// 不可變可以共享
const divider = Divider();
List.generate(1000, (_) => divider); // 只有 1 個(gè)實(shí)例
// 可變必須獨(dú)立
List.generate(1000, (_) => MutableDivider()); // 1000 個(gè)實(shí)例
Q2:每次都創(chuàng)建新對(duì)象不慢嗎?
A:Widget 創(chuàng)建很快,且可以被優(yōu)化掉
// Widget 只是配置描述(輕量級(jí))
Text('Hello'); // 只分配幾十字節(jié)
// 真正昂貴的是 RenderObject(重量級(jí))
// Flutter 會(huì)盡量復(fù)用 RenderObject
Q3:為什么 State 可以可變?
A:State 是長(zhǎng)期存在的,不需要頻繁對(duì)比
// State 生命周期
State 創(chuàng)建 → 長(zhǎng)期存在 → 銷毀
↑
└─ 不需要頻繁對(duì)比,可以可變
// Widget 生命周期
Widget 創(chuàng)建 → 對(duì)比 → 丟棄 → 創(chuàng)建 → 對(duì)比 → 丟棄 ...
↑
└─ 需要頻繁對(duì)比,必須不可變
七、總結(jié)
Widget 不可變的核心價(jià)值
不可變?cè)O(shè)計(jì)
↓
┌───────────────────────────────┐
│ 1. 快速對(duì)比(O(1) vs O(n)) │
│ 2. 線程安全(無需加鎖) │
│ 3. 內(nèi)存優(yōu)化(可共享復(fù)用) │
│ 4. 行為可預(yù)測(cè)(無副作用) │
│ 5. 聲明式 UI(狀態(tài)驅(qū)動(dòng)) │
└───────────────────────────────┘
↓
60fps 流暢體驗(yàn) ??
記憶口訣:
- Widget 是配置快照(拍照后不能修改照片)
- State 是狀態(tài)容器(可以隨時(shí)更新內(nèi)容)
- 不可變 = 高性能 + 簡(jiǎn)單 + 安全
這就是為什么 Flutter 堅(jiān)持 Widget 必須不可變的原因!