由浅入深,从基本概念到源码原理,再到实战案例,系统梳理 Web、移动、原生各平台的状态管理方案
一、什么是状态管理? 1.1 状态(State)的本质 状态 是应用在某一时刻的数据快照,它决定了 UI 的展示内容和行为。任何会随时间变化的数据——用户输入、网络请求结果、权限、主题——都可视为状态。
1 2 3 4 5 6 7 8 9 +------------+ +------------+ +------------+ +------------+ | User Input | | Biz Data | | UI State | | Server | | Form/Search| | List/Detail| |Modal/Load | | Cache | +------+-----+ +------+-----+ +------+-----+ +------+-----+ | | | | +-----------------+--------+--------+-----------------+ | v UI 渲染 / 副作用执行
用户输入·业务数据·UI状态·服务端 → 汇聚为统一的 UI 渲染与副作用执行
1.2 为什么需要状态管理?
痛点
说明
状态管理的价值
散落
状态分散在各处,难以追踪
集中、可预测的数据流
同步
多处 UI 依赖同一数据,容易不一致
单一数据源(Single Source of Truth)
生命周期
状态何时创建、何时销毁、何时持久化
与组件/页面生命周期绑定
跨层级
深层级组件需要访问顶层状态
提供 Context / Provider / 依赖注入
可测试
业务逻辑与 UI 耦合,难以单测
状态逻辑可独立测试
1.3 各平台状态管理方案概览
平台/框架
主要方案
核心机制
特点
React
Hooks (useState/useReducer)
虚拟 DOM diff + 依赖收集
函数式、声明式、生态丰富
Next.js
Server Components + Client State
服务端/客户端分离
减少客户端 JS、流式渲染
Flutter
Provider / Riverpod / Bloc
InheritedWidget / Listenable
Dart 异步流、可组合
Swift
Combine / 手动 KVO
发布-订阅
系统级、与 SwiftUI 整合
RxSwift
Observable / Subject
响应式流
操作符丰富、事件驱动
SwiftUI
@State / @Binding / @Observable
声明式 + 属性包装器
数据驱动视图、自动重绘
ArkTS
@State / @Prop / @Link
装饰器 + 响应式
鸿蒙声明式 UI
RxJava
Observable / Subject
响应式流
异步编排、背压支持
Agera
Observable / Updatable
推事件-拉数据
Google 早期方案,已归档
Jetpack Compose
remember / mutableStateOf
重组(Recomposition)
声明式、与 Kotlin 协程整合
二、状态管理的核心原则与模式 2.1 单向数据流(Unidirectional Data Flow) 大多数现代框架采用「单向数据流」:状态向下流动,事件向上反馈。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ┌─────────────┐ │ State │ ← 单一数据源 └──────┬──────┘ │ 只读传递 ▼ ┌─────────────┐ │ View │ → 用户操作 └──────┬──────┘ │ 事件 / Action ▼ ┌─────────────┐ │ Reducer / │ → 计算新状态 │ setState │ └──────┬──────┘ │ └──────────► 更新 State → 重新渲染
React、Redux、Flutter Bloc、Jetpack Compose 的 ViewModel 均遵循此模式。
2.2 观察者模式:统一的底层基石 状态管理大多基于观察者模式 :被观察对象变化时,通知所有订阅者。
平台
被观察者
观察者
通知方式
React
useState 返回值
组件函数
调度重渲染
SwiftUI
@State / @Published
视图 body
自动重算 body
Jetpack Compose
mutableStateOf
Composable
重组(Recomposition)
Flutter
ChangeNotifier
addListener
notifyListeners
RxJava
Observable
Observer
onNext
ArkTS
@State 变量
组件 build
框架触发重绘
2.3 局部状态 vs 全局状态
类型
范围
典型方案
场景
局部状态
单组件/单页面
useState / @State / remember
输入框、折叠面板、动画
共享状态
多组件/跨页面
Context / Provider / ViewModel
主题、用户信息、购物车
服务端状态
与后端同步
React Query / SWR / 自定义
列表、详情、缓存
三、Web 前端:React 与 Next.js 3.1 React 状态管理 3.1.1 基本概念:useState useState 是 React 最基础的状态 Hook,返回当前值和更新函数:
1 2 3 4 5 6 7 8 function Counter ( ) { const [count, setCount] = useState (0 ); return ( <button onClick ={() => setCount(c => c + 1)}> Count: {count} </button > ); }
原理简述 :React 在内部维护 Fiber 树,每个组件对应一个 Fiber 节点,useState 将状态存储在 Fiber 的 memoizedState 链表中。更新时调度 setState,标记组件需要重渲染,之后执行 diff 并提交 DOM 变更。
3.1.2 派生状态与 useReducer 当状态逻辑复杂时,用 useReducer 集中处理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 function reducer (state, action ) { switch (action.type ) { case 'increment' : return { count : state.count + 1 }; case 'decrement' : return { count : state.count - 1 }; default : return state; } } function Counter ( ) { const [state, dispatch] = useReducer (reducer, { count : 0 }); return ( <> <span > {state.count}</span > <button onClick ={() => dispatch({ type: 'increment' })}>+</button > </> ); }
3.1.3 跨组件共享:Context 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 const ThemeContext = createContext ('light' );function App ( ) { const [theme, setTheme] = useState ('light' ); return ( <ThemeContext.Provider value ={{ theme , setTheme }}> <Header /> <Main /> </ThemeContext.Provider > ); } function Header ( ) { const { theme } = useContext (ThemeContext ); return <div className ={theme} > ...</div > ; }
注意 :Context 变化会导致所有消费该 Context 的组件重渲染,可配合 useMemo / 拆分 Provider 优化。
3.1.4 副作用与依赖:useEffect 1 2 3 4 5 6 7 8 9 10 const [keyword, setKeyword] = useState ('' );const [users, setUsers] = useState ([]);useEffect (() => { if (keyword.length < 2 ) return ; const timer = setTimeout (() => { fetchUsers (keyword).then (setUsers); }, 300 ); return () => clearTimeout (timer); }, [keyword]);
3.2 Next.js 状态管理 Next.js 引入 Server Components 与 Client Components 的区分,状态管理策略随之变化。
3.2.1 服务端 vs 客户端状态
类型
组件
可用能力
典型场景
Server Component
默认
直接 fetch、访问 DB、无 hooks
静态内容、SEO、首屏数据
Client Component
'use client'
useState、useEffect、事件处理
交互、表单、实时更新
3.2.2 组合模式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 async function Page ( ) { const data = await fetchFromDB (); return ( <Layout > <StaticContent data ={data} /> <InteractiveCart /> {/* Client Component,内部用 useState */} </Layout > ); } 'use client' ;export function InteractiveCart ( ) { const [items, setItems] = useState ([]); return <CartUI items ={items} onAdd ={...} /> ; }
3.2.3 Context 在 Next.js 中的使用 Context Provider 必须放在 Client Component 中:
1 2 3 4 5 6 7 8 9 10 'use client' ;export function ThemeProvider ({ children } ) { const [theme, setTheme] = useState ('light' ); return ( <ThemeContext.Provider value ={{ theme , setTheme }}> {children} </ThemeContext.Provider > ); }
四、跨平台移动:Flutter 4.1 基本概念 Flutter 的 UI 是声明式 的:给定状态,构建对应 Widget 树。状态变化触发 setState 或 notifyListeners,进而重建 Widget。
4.2 内置方案 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class Counter extends StatefulWidget { @override _CounterState createState() => _CounterState(); } class _CounterState extends State <Counter > { int _count = 0 ; @override Widget build(BuildContext context) { return ElevatedButton( onPressed: () => setState(() => _count++), child: Text('$_count ' ), ); } }
4.2.2 ValueNotifier + ValueListenableBuilder(局部重建) 1 2 3 4 5 6 7 final counter = ValueNotifier<int >(0 );ValueListenableBuilder<int >( valueListenable: counter, builder: (context, value, child) => Text('$value ' ), )
4.2.3 ChangeNotifier + Provider 1 2 3 4 5 6 7 8 9 10 11 12 13 14 class CartModel extends ChangeNotifier { final List <Item> _items = []; void add(Item item) { _items.add(item); notifyListeners(); } } ChangeNotifierProvider(create: (_) => CartModel(), child: MyApp()) context.watch<CartModel>(); context.read<CartModel>();
4.3 进阶:Riverpod / Bloc
Riverpod :编译期安全、可测试、不依赖 BuildContext
Bloc :事件 → 状态 的显式映射,适合复杂业务流
五、iOS 原生:Swift、RxSwift、SwiftUI 5.1 Swift 传统方式
属性观察器 :willSet / didSet
KVO :observe(_:options:changeHandler:)
通知 :NotificationCenter
Delegate / 闭包回调
5.2 RxSwift 响应式状态 RxSwift 将状态抽象为流 ,通过 Observable / Subject 管理:
1 2 3 4 5 6 7 8 9 10 11 let searchResults = searchTextField.rx.text.orEmpty .debounce(.milliseconds(300 ), scheduler: MainScheduler .instance) .flatMapLatest { api.search($0 ) } .observe(on: MainScheduler .instance) searchResults .bind(to: tableView.rx.items(cellIdentifier: "Cell" )) { _ , model, cell in cell.textLabel? .text = model.name } .disposed(by: disposeBag)
Subject 可同时作为观察者和被观察者,常用于「桥接」命令式代码与响应式流:
1 2 3 4 5 6 7 8 let buttonTaps = PublishSubject <Void >()buttonTaps .flatMapLatest { api.fetchData() } .subscribe(onNext: { updateUI($0 ) }) .disposed(by: disposeBag) button.rx.tap.bind(to: buttonTaps).disposed(by: disposeBag)
5.3 SwiftUI 声明式状态 5.3.1 @State:组件内部值类型状态 1 2 3 4 5 6 struct CounterView : View { @State private var count = 0 var body: some View { Button ("Count: \(count) " ) { count += 1 } } }
5.3.2 @Binding:父子双向绑定 1 2 3 4 5 6 7 8 9 10 11 12 13 struct ParentView : View { @State private var isOn = false var body: some View { ToggleView (isOn: $isOn ) } } struct ToggleView : View { @Binding var isOn: Bool var body: some View { Toggle ("" , isOn: $isOn ) } }
5.3.3 @ObservedObject / @StateObject:引用类型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class UserViewModel : ObservableObject { @Published var name = "" @Published var isLoading = false } struct ProfileView : View { @StateObject private var viewModel = UserViewModel () var body: some View { Text (viewModel.name) } } struct ChildView : View { @ObservedObject var viewModel: UserViewModel var body: some View { ... } }
5.3.4 @Observable(iOS 17+) 新宏 @Observable 可替代 ObservableObject,更简洁:
1 2 3 4 5 6 7 8 9 10 11 12 @Observable class User { var name: String = "" var age: Int = 0 } struct UserView : View { @State private var user = User () var body: some View { Text (user.name) } }
六、Android 原生:RxJava、Agera、Jetpack Compose 6.1 RxJava 响应式状态 1 2 3 4 5 6 7 8 9 val querySubject = PublishSubject.create<String>()val results = querySubject .debounce(300 , TimeUnit.MILLISECONDS) .filter { it.length >= 2 } .switchMap { api.search(it).toObservable() } .observeOn(AndroidSchedulers.mainThread()) results.subscribe { updateUI(it) }.addTo(compositeDisposable) querySubject.onNext(editText.text.toString())
StateFlow / SharedFlow (Kotlin Flow)是官方推荐替代 LiveData 的方案:
1 2 3 4 5 6 7 8 9 10 class ViewModel : ViewModel () { private val _uiState = MutableStateFlow<UiState>(UiState.Loading) val uiState: StateFlow<UiState> = _uiState.asStateFlow() fun loadData () { viewModelScope.launch { _uiState.value = UiState.Success(repository.fetch()) } } }
6.2 Agera 简介(已归档) Agera 是 Google 早期的轻量级响应式库,采用推事件、拉数据 模型:
Observable :广播事件
Updatable :监听事件,从 Repository 拉取数据
1 2 3 4 5 6 7 8 9 val repository = Repositories.repositoryWithInitialValue(initialData) .observe() .onUpdatesPerLoop() .getFrom { fetchFromNetwork() } .compile() repository.addUpdatable(updatable)
注意 :Agera 已于 2023 年 3 月归档,新项目建议使用 Kotlin Flow / StateFlow。
6.3 Jetpack Compose 状态管理 6.3.1 remember + mutableStateOf 1 2 3 4 5 6 7 @Composable fun Counter () { var count by remember { mutableStateOf(0 ) } Button(onClick = { count++ }) { Text("Count: $count " ) } }
remember :在重组间保持值,避免每次重组重新创建
mutableStateOf :创建可观察状态,读该状态的 Composable 会在值变化时重组
6.3.2 状态提升(State Hoisting) 1 2 3 4 5 6 7 8 9 10 11 12 @Composable fun Parent () { var count by remember { mutableStateOf(0 ) } Child(count = count, onCountChange = { count = it }) } @Composable fun Child (count: Int , onCountChange: (Int ) -> Unit ) { Button(onClick = { onCountChange(count + 1 ) }) { Text("$count " ) } }
6.3.3 ViewModel + StateFlow 1 2 3 4 5 6 7 8 9 10 11 12 13 14 class MyViewModel : ViewModel () { private val _state = MutableStateFlow(MyState()) val state: StateFlow<MyState> = _state.asStateFlow() fun update () { _state.update { it.copy(...) } } } @Composable fun Screen (viewModel: MyViewModel = viewModel() ) { val state by viewModel.state.collectAsStateWithLifecycle() }
6.3.4 配置变更保留:rememberSaveable 1 2 var count by rememberSaveable { mutableStateOf(0 ) }
七、鸿蒙:ArkTS 7.1 装饰器驱动的状态 ArkTS 通过装饰器声明「可观察」状态,状态变化自动触发 UI 更新。
7.1.1 @State:组件内部状态 1 2 3 4 5 6 7 8 9 10 11 12 @Entry @Component struct Counter { @State count : number = 0 build ( ) { Column () { Text (`Count: ${this .count} ` ) Button ('+1' ) .onClick (() => { this .count ++ }) } } }
7.1.2 @Prop:父 → 子单向 1 2 3 4 5 6 7 8 9 10 @Component struct Child { @Prop value : number build ( ) { Text (`${this .value} ` ) } } Child ({ value : this .count })
7.1.3 @Link:父子双向 1 2 3 4 5 6 7 8 9 10 @Component struct Child { @Link value : number build ( ) { Button ('+1' ).onClick (() => { this .value ++ }) } } Child ({ value : $count })
7.1.4 @Provide / @Consume:跨层级 1 2 3 4 5 @Provide ('theme' ) theme : string = 'light' @Consume ('theme' ) theme : string
7.1.5 @Observed + @ObjectLink:嵌套对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Observed class User { name : string constructor (name : string ) { this .name = name } } @Component struct UserView { @ObjectLink user : User build ( ) { Text (this .user .name ) .onClick (() => { this .user .name = 'New' }) } }
7.2 状态管理 V2(@ObservedV2 + @Trace) V2 支持深层属性观察,解决嵌套对象内部属性不可观察的问题。
八、跨平台对比与选型 8.1 概念映射表
概念
React
Flutter
SwiftUI
Jetpack Compose
ArkTS
组件内状态
useState
State
@State
remember + mutableStateOf
@State
父子单向
props
构造函数参数
普通参数
参数
@Prop
父子双向
回调 + props
回调
@Binding
回调 + 参数
@Link
跨层级
Context
Provider/InheritedWidget
EnvironmentObject
CompositionLocal
@Provide/@Consume
引用类型
useRef/Context
ChangeNotifier
@ObservableObject
ViewModel
@Observed+@ObjectLink
8.2 选型建议
场景
推荐方案
简单 UI 状态
各平台内置(useState / @State / remember)
跨组件共享
Context / Provider / ViewModel / @Provide
复杂异步流
RxSwift / RxJava / Kotlin Flow
服务端数据
React Query / SWR / ViewModel + Repository
Next.js 全栈
Server Components 拉数据 + Client 管理交互态
新 Android 项目
Jetpack Compose + ViewModel + StateFlow
新 iOS 项目
SwiftUI + @Observable
鸿蒙应用
ArkUI 装饰器体系
九、源码原理浅析 9.1 React:Fiber 与 Hooks 链表 React 在 Fiber 节点上维护 memoizedState 链表,每个 Hook 对应链表中的一个节点:
1 2 3 4 5 Fiber.memoizedState → useState → useEffect → useContext → ... │ ├─ baseState ├─ baseQueue └─ next (下一个 Hook)
setState 会将更新放入队列,调度器在合适时机执行重渲染,按顺序应用更新,保证 Hooks 调用顺序稳定。
9.2 Jetpack Compose:快照与重组 Compose 使用 Snapshot 系统追踪状态读取:
mutableStateOf 创建 SnapshotMutableState
读取 state.value 时,当前 Composable 的「重组作用域」会记录对该 state 的依赖
写入 state.value = x 时,Compose 标记依赖该 state 的作用域需要重组
重组时重新执行对应 Composable,得到新 UI
1 读取 state → 记录依赖 → 写入 state → 标记无效 → 调度重组 → 重新执行 Composable
9.3 SwiftUI:@State 与依赖追踪 SwiftUI 在编译期和运行期结合,追踪 body 中对 @State 等属性的访问。当 @State 变化时,视图的 body 会重新求值,生成新的 View 描述,再与旧描述 diff 后更新真实视图。
9.4 ArkTS:装饰器与更新调度 @State 等装饰器在编译期生成观察逻辑,运行时状态变化会标记组件为脏,下一帧统一执行 build 更新 UI,类似 Flutter 的 setState + 帧调度。
十、实战案例 10.1 登录表单校验(多字段组合) 需求 :用户名 ≥3 字符、密码 ≥6 字符时,登录按钮才可点击。
React :
1 2 3 4 const [user, setUser] = useState ('' );const [pwd, setPwd] = useState ('' );const canLogin = user.length >= 3 && pwd.length >= 6 ;return <Button disabled ={!canLogin} > 登录</Button > ;
SwiftUI :
1 2 3 4 @State private var user = "" @State private var pwd = "" var canLogin: Bool { user.count >= 3 && pwd.count >= 6 }Button ("登录" ) { }.disabled(! canLogin)
Jetpack Compose :
1 2 3 4 var user by remember { mutableStateOf("" ) }var pwd by remember { mutableStateOf("" ) }val canLogin = user.length >= 3 && pwd.length >= 6 Button(onClick = {}, enabled = canLogin) { Text("登录" ) }
ArkTS :
1 2 3 4 5 6 @State user : string = '' @State pwd : string = '' private get canLogin (): boolean { return this .user .length >= 3 && this .pwd .length >= 6 } Button ('登录' ).enabled (this .canLogin )
10.2 搜索防抖 + 取消旧请求 React :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 const [keyword, setKeyword] = useState ('' );const [results, setResults] = useState ([]);useEffect (() => { if (keyword.length < 2 ) return ; const timer = setTimeout (() => { let cancelled = false ; search (keyword).then (data => { if (!cancelled) setResults (data); }); return () => { cancelled = true ; }; }, 300 ); return () => clearTimeout (timer); }, [keyword]);
RxSwift :
1 2 3 4 5 6 7 searchTextField.rx.text.orEmpty .filter { $0 .count >= 2 } .debounce(.milliseconds(300 ), scheduler: MainScheduler .instance) .flatMapLatest { api.search($0 ) } .observe(on: MainScheduler .instance) .bind(to: resultsRelay) .disposed(by: disposeBag)
Jetpack Compose + ViewModel :
1 2 3 4 5 6 val query = MutableStateFlow("" )val results = query .debounce(300 ) .filter { it.length >= 2 } .flatMapLatest { repository.search(it) } .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000 ), emptyList())
10.3 购物车总价实时计算 Flutter (Provider) :
1 2 3 4 5 class CartModel extends ChangeNotifier { final List <CartItem> _items = []; double get total => _items.fold(0 , (sum, item) => sum + item.price * item.qty); void add(CartItem item) { _items.add(item); notifyListeners(); } }
React :
1 2 3 4 5 const [items, setItems] = useState ([]);const total = useMemo ( () => items.reduce ((s, i ) => s + i.price * i.qty , 0 ), [items] );
10.4 Next.js 服务端数据 + 客户端状态 1 2 3 4 5 6 7 8 9 10 export default async function Page ({ params }: { params: { id: string } } ) { const product = await fetchProduct (params.id ); return ( <div > <ProductInfo product ={product} /> <AddToCartButton productId ={product.id} /> {/* 内部用 useState 管理数量 */} </div > ); }
十一、总结与最佳实践 11.1 核心要点
维度
共性
本质
状态驱动 UI,变化触发更新
模式
单向数据流、观察者、单一数据源
局部 vs 全局
按范围选择合适的共享机制
生命周期
状态与组件/页面生命周期绑定,避免泄漏
11.2 最佳实践
最小化状态 :能推导的不要存储,用 useMemo / computed / getter
状态提升 :当多组件需要共享时,提升到共同祖先
不可变更新 :避免直接修改,使用 setState/copy/扩展运算符
副作用清理 :useEffect 清理、Disposable.dispose、viewModelScope
服务端状态分离 :与 UI 状态区分,用专门库(React Query 等)管理
类型安全 :TypeScript、Swift、Kotlin 充分利用类型约束状态结构
11.3 各平台快速对照
平台
局部状态
共享状态
异步/流式
React
useState
Context / Redux
useEffect / React Query
Next.js
useState (Client)
Context (Client)
Server Components + fetch
Flutter
State / ValueNotifier
Provider / Riverpod
Stream / Future
SwiftUI
@State
@ObservableObject / Environment
async/await
RxSwift
Observable / Subject
同上 + Subject
Observable 链
Compose
remember + mutableStateOf
ViewModel + StateFlow
Flow / LaunchedEffect
ArkTS
@State
@Provide / @Consume
Promise / async
参考资源