Compose 只重组 读取了已变化状态 的代码块(lambda / composable 作用域)。 重组范围 = 读取状态的最小可组合函数或内联块。避免“父重组导致子全部重组”。
重组以 @Composable 函数为边界,但内联 lambda 会扩大范围。将状态读取下沉到子项。
若 Text(text) 读取了 text,只有该 Text 重组,兄弟不受影响。
在顶层 Composable 读取大量状态 → 整个界面重组。使用派生状态、lambda 包裹。
// ✅ 重组范围最小化:只有读取 count 的 Text 重组
@Composable
fun Counter() {
var count by remember { mutableStateOf(0) }
Button(onClick = { count++ }) { Text("➕") }
Text("点击次数: $count") // 仅这里重组
HeavyStaticComponent() // 不重组
}
Compose 编译器通过“稳定性”判断是否需要跳过重组。
稳定类型:编译器知道其是否变化(equals 或引用)。非稳定类型:编译器无法推断,每次均重组。
| 分类 | 说明 | 常见类型 |
|---|---|---|
| ✅ 稳定 (Stable) | 不可变或 Compose 可追踪变化 | 原始类型、String、Lambda、MutableState、@Immutable data class |
| ⚠️ 非稳定 (Unstable) | 可变且未标记,编译器保守处理 | 普通 List、Map、普通 data class (未加注解)、接口、抽象类 |
表示类型所有公开属性在构造后永不改变(深度不可变)。编译器可完全跳过相等性检查。
@Immutable data class User(val name: String, val age: Int)
表示类型对外表现“稳定”,即使内部可变,但 Compose 能通过快照系统感知变化。常用于可变但受控的类。
@Stable
class MutableUiState {
var name by mutableStateOf("")
var age by mutableStateOf(0)
}
💡 最佳实践:数据类加 @Immutable;若类包含 MutableState 或快照属性,使用 @Stable。避免编译器将你的类标记为非稳定。
| 注解 | 可变性 | 编译器行为 | 典型用例 |
|---|---|---|---|
| @Immutable | 完全不可变 | 跳过重组(恒等/equals) | data class, 配置, 常量 |
| @Stable | 允许受控可变 | 信任快照通知,跳过不必要重组 | ViewModel 状态, 带 mutableStateOf 的类 |
| 无注解 | 可变 / 不确定 | 视为非稳定,每次重组 | 普通 List, 可变集合, 接口 |