“全局 State 是不是反模式”本身就是一个误导性命题——真正的问题不是 全不全局,而是:
谁能改?怎么改?改了谁会受影响?
一句话结论(先给判断)
全局 State 本身不是反模式,
“可被任意修改的全局 State”才是。
一、为什么“全局 State”会被骂成反模式?
因为历史上它常常长这样👇
Singleton
├── 登录状态
├── 用户信息
├── 配置
├── 缓存
├── UI Flag
└── 谁都能改
灾难不是“全局”,而是:
- ❌ 修改入口无限多
- ❌ 修改路径不可追踪
- ❌ 没有边界、没有 owner
- ❌ 改一个字段,全 App 都可能炸
二、什么情况下“全局 State = 灾难”?
1️⃣ 它承载“局部业务状态”
AppState.currentEditingPost
AppState.currentTab
AppState.isKeyboardVisible
❌ 这是典型灾难:
- 生命周期短
- 强 UI 相关
- 与 Feature 强耦合
👉 本该是 局部 State,却被放成全局
2️⃣ 多个 Feature 直接读写
Feature A → 改
Feature B → 也改
Feature C → 监听副作用
结果:
- 无法预测调用顺序
- 并发条件竞争
- Bug 难复现
3️⃣ 缺乏单向数据流约束
GlobalState.user = ...
谁改的?为什么改?什么时候改?
👉 调试直接进入“上帝模式”
4️⃣ 被用作“方便的捷径”
“先放全局,后面再重构”
这是大型 App 里技术债最凶的起点之一。
三、那什么时候“全局 State”反而是最优解?
关键一句话:
当 State 的“语义就是全局的”,并且“变化路径是严格受控的”。
✅ 情况一:真正的 App 级事实(App Facts)
例如:
- 登录 / 登出状态
- 当前账号
- Feature Flag / AB 实验
- 语言 / 地区 / 时区
- 远程配置版本
它们的特点:
- 生命周期 = App 生命周期
- 所有 Feature 都“合理依赖”
- 改变时需要全 App 响应
👉 不全局反而更怪
✅ 情况二:只读为主、极少修改
读:高频
写:低频、可控
例如:
- 配置
- 权限矩阵
- Capability 描述
👉 风险不在“读”,而在“写”。
✅ 情况三:有唯一写入口(极重要)
Action → Reducer → Global State
例如 TCA:
struct AppState {
var auth: AuthState
var settings: SettingsState
}
- Feature 只能发 Action
- 不能直接改 State
👉 全局 ≠ 失控
✅ 情况四:跨 Feature 协调状态
例如:
- Deep Link
- Push 跳转
- 全局错误处理
- Session 失效
它们天然是:
“一个事件 → 多个 Feature 联动”
四、判断一段 State 是否“该全局”的工程标准
你可以用这 4 个问题来问自己:
✅ 1️⃣ 生命周期是否 ≈ App 生命周期?
- 是 → 倾向全局
- 否 → 局部
✅ 2️⃣ 是否有“唯一可信来源”?
- 是 → 可全局
- 否 → 拆分
✅ 3️⃣ 修改是否必须可审计?
- 是 → 强制单向数据流
- 否 → 风险高
✅ 4️⃣ Feature 是否可以“只读”?
- 可以 → 风险低
- 必须写 → 需要边界
五、正确的“全局 State”长什么样?
✅ 好的全局 State(工程级)
struct AppState {
var auth: AuthState
var config: ConfigState
var environment: EnvironmentState
}
- 分域
- 不承载 UI 细节
- 不承载瞬时状态
❌ 坏的全局 State
struct AppState {
var currentTab
var isModalPresented
var editingText
}
👉 UI 生命周期 ≠ App 生命周期。
六、SwiftUI / TCA 里的真实结论
全局 Store 是 TCA 的优势,而不是缺陷。
真正危险的是:没有 scope 的全局 Store。
scope(state:action:) 的意义不是解耦 View,
而是 给全局 State 划出“局部可见性” 。
最后一句话(架构级)
“全局”不是罪,
“无边界、无约束、无责任人”的全局,才是。