跳到主要内容

Jetpack Compose 状态与重组

🌟 Compose 重组是核心機制——状态变化触发重组,数据向下流。理解重组范围和防止不必要重组是性能优化的关键。

状态类型

// remember —— 重组时保留值
// mutableStateOf —— 变化时触发重组
@Composable
fun Counter() {
var count by remember { mutableStateOf(0) }
Button(onClick = { count++ }) {
Text("Count: $count")
}
}

// rememberSaveable —— 追加跨旋转屏幕/进程杀死恒久化
var name by rememberSaveable { mutableStateOf("") }

状态上提(State Hoisting)

// 有状态
@Composable
fun StatefulCounter() {
var count by remember { mutableStateOf(0) }
StatelessCounter(count = count, onIncrement = { count++ })
}

// 无状态(可复用、可测试)
@Composable
fun StatelessCounter(count: Int, onIncrement: () -> Unit) {
Button(onClick = onIncrement) { Text("$count") }
}

重组范围控制

key 修饰符

// 用 key 识别 Composable,防止列表重组时状态错位
LazyColumn {
items(items, key = { it.id }) { item ->
ItemRow(item)
}
}

derivedStateOf 减少重组

// 只有计算结果变化时才重组
val showScrollToTop by remember {
derivedStateOf { listState.firstVisibleItemIndex > 0 }
}
// 没有 derivedStateOf:每次滚动都重组
// 有 derivedStateOf:只有偷看状态变化时重组

remember 计算缓存

// 防止每次重组都重新计算
val filteredItems = remember(items, query) {
items.filter { it.name.contains(query) } // 只有 items 或 query 变化时重计算
}

SideEffect 副作用

// LaunchedEffect —— 启动干静的协程
LaunchedEffect(userId) { // userId 变化时重新执行
viewModel.loadUser(userId)
}

// DisposableEffect —— 需要清理的副作用
DisposableEffect(key) {
val listener = ...
addListener(listener)
onDispose {
removeListener(listener) // Composable 离开时清理
}
}

// SideEffect —— 每次重组后执行
SideEffect {
analytics.logScreenView(screenName)
}

常见误区

  • 在 Composable 函数中直接修改状态而不是通过回调,导致无限重组
  • LazyColumn 列表项没有 key,列表变化时重组出错
  • 不要在 Composable 中直接调用 remember 外的远程操作,应用 LaunchedEffect