第3章 局部状态与全局状态的划分原则
在 Compose 架构中,状态放置的位置直接影响组件复用性、可测试性和模块边界清晰度。局部状态与全局状态的划分并非凭直觉,而是基于三项经过验证的原则:作用域判定、生命周期对齐和跨屏幕复用。本章将逐一拆解这三项原则,并给出可落地的决策流程与验收标准。
3.1 为什么需要明确划分原则
未加约束的状态放置会导致两种典型问题:
- 全局状态泛滥:所有状态都放入 ViewModel 或单例,导致组件难以独立测试,状态生命周期失控,模块边界被击穿。
- 局部状态过度提升:为复用而将状态提到不合理的层级,引发不必要的重组和参数透传,组件接口膨胀。
三个划分原则分别从可见范围、生存周期和共享需求三个维度约束状态放置,形成可重复的决策框架,帮助团队在模块边界处做出一致的选择。
3.2 三大原则概览
| 原则 | 核心问题 | 判定标准 | 典型场景 |
|---|---|---|---|
| 作用域判定 | 状态被哪些组件读取/写入? | 仅单组件使用 → 局部;多组件共享 → 提升 | 按钮加载状态、表单输入值、展开/收起标志 |
| 生命周期对齐 | 状态的生存周期应与谁一致? | 与组件同生共死 → 局部;超出组件 → 全局 | 对话框可见性、定时器引用、网络轮询控制 |
| 跨屏幕复用 | 状态是否需要在多个屏幕间共享? | 跨屏共享 → 全局;屏内独享 → 局部 | 用户登录态、主题偏好、购物车数量 |
3.3 原则一:作用域判定
作用域判定关注状态的读写边界。一个状态如果在组件树中只被单个叶节点组件读写,则定义为局部状态;如果被兄弟节点或跨层级组件共享,则必须提升到最近的公共父级作用域。这是状态提升模式中最基础的判断依据。
✅ 正确做法:在
@Composable 内部使用 remember { mutableStateOf(false) },作用域限定在当前组合树节点。
// ✅ 局部状态:作用域仅限当前卡片
@Composable
fun ExpandableCard(title: String, content: String) {
var isExpanded by remember { mutableStateOf(false) }
// isExpanded 只在此 Composable 内使用
Card(onClick = { isExpanded = !isExpanded }) {
Text(title)
AnimatedVisibility(visible = isExpanded) {
Text(content)
}
}
}
// ❌ 反例:将单组件状态放入 ViewModel 全局化
class CardViewModel : ViewModel() {
var isExpanded by mutableStateOf(false) // 不必要地全局化
}
作用域判定的核心收益在于组件职责清晰——每个 Composable 只管理属于自己的状态,不越界读写外部数据。数据流方向从父级到子级单向传递,控制流则通过回调反向上报。
3.4 原则二:生命周期对齐
状态的生存周期必须与引用它的最长生命周期的宿主对齐。当状态的生命周期超出宿主组件时,会引发内存泄漏或状态残留;当状态生命周期短于宿主时,会导致状态丢失或初始化异常。
- 组件级状态:如动画进度、输入焦点、展开标志 → 与 Composable 生命周期对齐 → 使用
remember或rememberSaveable - 屏幕级状态:如列表数据、筛选条件、表单草稿 → 与 NavBackStackEntry 生命周期对齐 → 放在 Screen 级别的 ViewModel
- 应用级状态:如用户身份、深色模式、语言偏好 → 与 Application 生命周期对齐 → 放在单例 Store 或全局 ViewModel
remember 将状态生命周期限定在组合树内。对于需要持久化的状态,使用 rememberSaveable 而非直接提升到全局层。
生命周期对齐直接决定了模块边界的牢固程度。当状态生命周期与宿主对齐时,模块的创建和销毁不会产生副作用泄漏,边界自然清晰。
3.5 原则三:跨屏幕复用
跨屏幕复用的核心判断标准是:同一份状态是否需要被多个不共享父子关系的屏幕/模块同时读写。典型需要全局化的场景包括:
- 用户认证态:登录信息在多个 Tab 页、底部导航页面共享
- 全局配置:语言、主题、字体缩放、深色模式
- 跨模块数据:购物车数量、未读消息数、全局搜索状态
- 跨屏幕事件:全局通知、强弹窗控制、会话过期信号
对于仅在单个屏幕内复用、但跨组件的状态,优先选择状态提升到屏幕级 ViewModel,而非直接提升到全局层级。这可以避免不必要的全局状态膨胀,保持模块的独立部署能力。
3.6 决策流程框架
将三个原则串联为可执行的决策流程:从状态候选出发,依次经过作用域判定、生命周期对齐、跨屏幕复用三道关卡,最终确定状态归属。下图为完整的决策树,可直接嵌入团队 Code Review 检查清单。