State Management,还是不要 State Management?
现代前端应用已经很难真正“没有 State Management”。
这是一个很强的说法。绝对、挑衅,而且当然过于笼统。静态 Landing Page 不需要 Redux,只有三个字段的表单也不需要。谁要是为每次 Button Click 都引入一个 Global Store,大概率不是在解决架构问题,而是在制造新的问题。
但作为切入点,这个命题很有用,因为它能把真正需要讨论的问题暴露出来。
关键问题并不是:
我们需不需要 State Management?
而是:
我们的状态住在哪里,谁可以改变它,它又以多清晰的方式流经应用?
因为状态始终存在。区别只是:我们有意识地建模它,还是任由它偶然分散在 Component、Service、Subscription、Input/Output 链、Router Parameter 和局部变量里。

State Management 到底是什么
Section titled “State Management 到底是什么”State Management 并不自动等于 Redux、NgRx、Signals、Store 或某个库。
它首先只意味着一件事:
应用的状态被有意识地管理。
这会带来一组很实际的问题:
- 应用当前知道哪些数据?
- 这些数据存在哪里?
- 谁可以修改它们?
- 修改通过什么触发?
- 应用其他部分如何对此响应?
- 如何从 Raw Data 得到 UI Model?
- Loading、Error、Success 与 Side Effect 如何处理?
即使没有“显式 State Management”,应用依然有状态。
只是它经常散落在各处。
一点在 Component。
一点在 Service。
一点在 Router。
一点在某个 Subject。
一点在 Subscription。
一点在 Template。
再加一点希望:最好没人按错顺序操作。
这当然也是 State Management。
只是它是隐式的。

State Management 解决的到底是什么问题
Section titled “State Management 解决的到底是什么问题”State Management 并不会让复杂应用突然不复杂。
它解决的是另一个问题:
它让复杂度变得可见、可定位、可测试。
很多前端应用里,真正困难的甚至不是业务本身。业务流程可能非常简单:
- 加载数据
- 打开表单
- 保存修改
- 显示成功
- 刷新列表
- 导航到详情页
- 显示错误
问题在于,这些步骤经常横跨整个应用。
Component 调 Service。
Service 里有一个 Subject。
另一个 Component 对它做 Subscription。
Toast 顺手在旁边触发。
Routing 又发生在 Callback 里。
Reload 挂在另一个 Callback 上。
最后没人说得清:某个错误到底来自 HTTP Call、Mapping、Template,还是一条忘记清理的旧 Subscription。
State Management 可以把这种散乱重新组织起来。
不是因为 Store 会魔法般生成好代码,而是因为它迫使团队思考“流”。
真正的优势:清晰的 Flow
Section titled “真正的优势:清晰的 Flow”一个好的 State Management 方案至少会区分:
- Intent:用户或系统想做什么?
- Command / Event:什么被发送进应用?
- Operation:发生了什么外部操作,例如 HTTP?
- Result:得到什么业务结果?
- State Update:状态如何变化?
- ViewModel:UI 最终看到什么?
- Side Effect:Toast、Navigation、Reload、Tracking 等
于是流程变得可追踪:
Button click -> createIntent(payload) -> event.on(createIntent) -> http.put(payload) -> createSuccess(result) oder createError(error) -> unabhängige Reaktionen
Button 并不负责启动整个业务流程。
它只表达一个意图。
UI 说:
我想创建这个东西。
但它并不决定接下来调用什么 Infrastructure、重新加载哪些数据、显示哪个 Toast 或导航到哪里。
这些反应属于应用流程,而不是 Button Click Handler。
这正是 State Management 带来的架构价值。
不是 Callback 链,而是 Event Flow
Section titled “不是 Callback 链,而是 Event Flow”一个常见错误,是把 Success 后的所有动作做成线性的调用链。
例如:
save() -> http.put() -> reload() -> showToast() -> navigate()这当然能工作。
但它把本来可以独立的行为强耦合在一起。
很多情况下,更好的表达是:
createSuccess(result) ├─ event.on(createSuccess) -> reload resource ├─ event.on(createSuccess) -> show toast └─ event.on(createSuccess) -> navigate看起来只是小改动,架构含义却不同。
第一种形式里,所有动作依附于一条命令式链。
第二种形式里,先出现一个具有业务含义的结果,多个应用部分可以分别对此响应。

这样扩展也更容易。
以后如果还要发送 Analytics Event,不需要把现有 Save Flow 再拆开重组,只需要增加一个对 createSuccess 的新反应。
“什么时候需要 State Management?”
Section titled ““什么时候需要 State Management?””这个问题很常见,也完全合理。
务实的回答是:
当一个状态被不止一个地方读取或修改时,显式 State Management 很快就开始值得。
常见信号包括:
- 多个 Component 需要同一份数据
- 某个 Action 之后需要一致地刷新数据
- Loading / Error / Success State 不断重复
- Save 后需要同时发生 Toast、Reload、Navigation
- Component 越来越大
- Service 同时承载业务逻辑、UI Logic 与 Cache Logic
- 手工 Subscription 越来越多
- Debugging 变成“这个值到底在哪里被 set 了?”
- 因为逻辑挂在 Component 上,测试越来越困难
如果目标是训练自己真正理解响应式编程,答案甚至可以更严格:
如果你想学习响应式编程,就始终练习显式 State Management。
不是因为每个应用都必须这样做,而是因为它会训练一种更干净的思维方式:
- 数据单向流动
- 修改通过显式 Event 发生
- UI 是 State 的派生
- Side Effect 被有意识地建模
- Component 是消费者,不是控制中心
开始时结构会多一点。
以后混乱会少很多。

培训里真正应该先问的问题
Section titled “培训里真正应该先问的问题”我曾经在一次培训里从一个很简单的问题开始:
你们希望这个项目采用响应式编程吗?
如果答案是“不”,那我们甚至不用讨论 Redux、Store 或 State Management。继续使用命令式方式也可以,只需要同时接受它带来的后果。
但如果答案是“是”,就必须认真讨论 State Management。
因为响应式编程并不等于代码里出现了一个 Observable 或 signal()。
只有当 UI、数据和事件被有意识地建模成 Flow,应用才真正开始变得响应式。
三种实现,同一份业务
Section titled “三种实现,同一份业务”为了说明这一点,我曾经把同一个 CRUD Flow 分别实现成三种方式:
- Observer Service
- 经典 Redux Pattern 的 NgRx
- NgRx Signals
业务完全相同:
- 创建数据
- 执行 HTTP Request
- 处理成功或错误
- 刷新列表
- 显示 Success Toast
- 程序化 Navigation
- UI 与 Infrastructure / Application Logic 清晰分离
三种方案里的架构边界都刻意保持一致:
- Infrastructure 负责 HTTP
- Application Layer 负责 Use Case
- State Layer 负责状态与 Flow
- UI 只负责消费
有意思的并不是哪个库“赢了”。
真正有意思的是参与者注意到了一件事:
业务没有变化,但代码变少了。
这并不意味着 State Management 永远会减少代码。经典 Redux 完全可能产生大量 Boilerplate。
真正的规律是:工具越贴合所采用的 Paradigm,需要自己发明的机制就越少。

Observer Service:可以工作,但很多规则需要自己造
Section titled “Observer Service:可以工作,但很多规则需要自己造”Observer Service 在小到中型场景里完全可以成立。
常见做法是:一个 Service,里面有 Subject 或 BehaviorSubject,再加一些 Action Method 和供 UI 消费的公开 Stream。
这并没有错。
问题在于:你实际上在自己实现一套小型 State Management。
于是也必须自己定义规则:
- Event 怎么命名?
- Error Handling 在哪里发生?
- Loading 在哪里建模?
- 谁更新 Cache?
- 谁触发 Reload?
- 谁允许调用
next()? - Side Effect 放在哪里?
- 怎样一致地测试?
高度自律的团队可以把它做好。
但长期来看,纪律通常没有结构那么容易扩展。
经典 NgRx:结构很强,仪式也很多
Section titled “经典 NgRx:结构很强,仪式也很多”Redux 风格的 NgRx 提供了非常明确的规则:
- Action 描述 Event
- Reducer 改变 State
- Effect 处理 Side Effect
- Selector 为 UI 准备数据
从架构角度看,这很强,尤其适合较大的应用。
代价则是 Ceremony。
更多文件、更多类型、更多连接代码。对理解 Redux 的团队,这是显式结构;对只想“赶快加载一下数据”的团队,它会显得很重。
只有当应用复杂度足够高,这种结构节省的成本超过它自身带来的成本时,收益才真正显现。
NgRx Signals:代码更少,但基本思想没变
Section titled “NgRx Signals:代码更少,但基本思想没变”NgRx Signals 并没有改变 State Management 的核心思想。
它只是让这种思想更贴近 Angular。
State、Computed Value、Method 和 Reactive Derivation 可以更紧凑地放在一起。过去分散在 Action、Reducer、Effect 和 Selector 里的某些结构,可以更靠近业务模型表达。
因此,同样一个 CRUD Flow 往往可以小很多。
不是因为架构消失了。
而是因为表达这套架构所需要的 Infrastructure Code 少了。
UI 仍然是消费者。 HTTP 仍然属于 Infrastructure。 业务 Operation 仍然需要清晰建模。 State 依然是显式的。
只是表达方式没有那么重。
State Management 会带来哪些新问题
Section titled “State Management 会带来哪些新问题”State Management 不是免费的收益。
它会带来新的问题:
- Local Component State 在哪里结束?
- 什么应该进入 Store?
- Store 应该切多细?
- 如何避免 Global Store 变成垃圾场?
- Event 与 Method 如何统一命名?
- Command State 与 Read State 怎么分离?
- 如何防止 Store 成为新的 God Class?
糟糕的 State Management 只是另一种糟糕的代码。
把所有东西放进一个 Global Store,不叫架构。
那只是地下室。

State Management 只有与明确边界结合时才真正有帮助:
- Feature Store,而不是 Global Data Dump
- ViewModel,而不是 Template Logic
- 明确 Event / Intent,而不是任意 Method
- 有意识地建模 Side Effect
- 保持 Component 小而清晰
- Infrastructure 与 UI 分离
- 使用 Derivation,而不是手工同步
“代码更少”并不是最重要的收益
Section titled ““代码更少”并不是最重要的收益”代码更少当然很好。
但更重要的是:
你知道应该去哪里找问题。
Save 失败,我看 Command Flow。 数据展示错误,我看 ViewModel。 Toast 没出现,我看 Success Handler。 Reload 没发生,我看谁对 Success Event 做反应。 UI 行为异常,我首先看它是否意外拥有了自己的业务逻辑。
这会显著改善 Debugging。
也会改善测试。
如果 Component 只消费 State 并发送 Intent,就不需要在 UI 层测试太多逻辑。真正的逻辑会落在 Function、Store、Mapper 和 Use Case 中,更容易做快速、稳定的测试。

真正需要做的决定
Section titled “真正需要做的决定”“要不要 State Management?”通常太粗。
更好的问题是:
- 我们的 State Flow 有多复杂?
- 多少 Component 共享数据?
- 一次 Action 多经常需要触发后续反应?
- Debugging 的可追踪性有多重要?
- Business Logic 需要多高的可测试性?
- 团队对响应式编程理解到什么程度?
- Component 变成控制中心的风险有多大?
在现代前端应用里,答案经常不再是:
我们不需要 State Management。
而更像是:
我们不需要在所有地方使用同一级别的 State Management。
局部 UI State 可以留在局部。
Dropdown 是否展开,不需要进入 Global Store。 Dialog 是否显示,通常也不需要。 Form Field 是否 Focus,更不需要。
但业务状态、已加载数据、Command Flow、权限、Selection、Filter、Loading State 与 Derived ViewModel,几乎总能从显式结构中获益。
“现代前端应用已经离不开 State Management。”
作为绝对命题,它当然不准确。
作为架构警告,它却非常接近事实。
因为状态始终存在。我们真正要决定的是:有意识地管理它,还是让它随着应用增长而自行扩散。
好的 State Management 不意味着:
我们用了某个库。
它意味着:
- 清晰的数据流
- 清晰的 Event
- 清晰的职责
- 小而简单的 UI Component
- 更好的测试
- 更好的 Debugging
- 更少的隐式耦合
- 更少偶然形成的架构
所以更好的问题不是:
到什么时候我们才需要 State Management?
而是:
到什么时候,我们还承担得起没有它的代价?