由浅入深,从基本概念到源码原理,再到实战案例,系统梳理五大主流开发框架中的响应式编程共性与差异
一、什么是响应式编程? 1.1 从命令式到声明式 无论使用哪个平台,传统命令式编程的本质都是「我告诉程序每一步该做什么」:
1 2 3 4 5 6 7 8 9 editText.addTextChangedListener(object : TextWatcher { override fun onTextChanged (s: CharSequence?, p1: Int, p2: Int, p3: Int) { val text = text.toString() if (text.length >= 3 ) { searchUsers(text) } } })
1 2 3 4 5 6 textField.addTarget(self , action: #selector (textDidChange), for: .editingChanged) func textDidChange () { let text = textField.text ?? "" if text.count >= 3 { searchUsers(text) } }
响应式编程则将数据流(Data Stream)视为核心,用声明式 方式描述「当数据变化时该做什么」:
1 2 命令式:监听 → 判断 → 执行 响应式:数据流 → 转换/过滤 → 订阅并响应
1.2 响应式编程的三大特征
特征
说明
跨平台体现
数据流
事件、状态、异步结果统一抽象为「流」
Observable / Signal / Stream / State
声明式
描述「是什么」而非「怎么做」
链式操作符、装饰器、Hooks
自动传播
依赖变化自动触发更新
订阅机制、依赖收集、重新渲染
1.3 五大平台的响应式方案概览
平台
主要方案
核心类型
特点
Android
RxJava / Kotlin Flow
Observable / Flow
操作符丰富,协程整合
iOS
RAC / RxSwift
Signal / Observable
热/冷信号、Cocoa 扩展
鸿蒙
ArkUI
@State / @Observed
声明式 UI,装饰器驱动
React
Hooks
useState / useEffect
函数式、虚拟 DOM 驱动
Flutter
Stream / ValueNotifier
Stream / ChangeNotifier
Dart 异步流、Listenable
二、核心概念共通点 2.1 观察者模式:统一的底层基石 所有响应式实现都以观察者模式 为基础:有「被观察对象」和「观察者」,数据变化时通知观察者。
1 2 3 4 5 6 7 ┌─────────────────┐ ┌──────────────────┐ │ 数据源/生产者 │ ──────► │ 观察者/消费者 │ │ (Observable等) │ 订阅 │ (Observer等) │ └─────────────────┘ └──────────────────┘ │ ▲ │ onNext / send / emit │ └────────────────────────────┘
平台
被观察者
观察者
订阅方式
Android RxJava
Observable
Observer
subscribe()
Android Flow
Flow
Collector
collect {}
iOS RAC
Signal / SignalProducer
Observer
observe() / start()
React
useState 返回值
组件
隐式(依赖 React 调度)
Flutter
Stream / ValueNotifier
StreamSubscription / addListener
listen() / addListener()
鸿蒙
@State 变量
UI 组件
隐式(框架自动重绘)
2.2 可取消性(Disposable / 生命周期) 订阅通常会产生「可取消」的句柄,用于在合适时机释放资源,避免内存泄漏:
平台
取消句柄
典型用法
RxJava
Disposable
compositeDisposable.add()
Kotlin Flow
Job / CoroutineScope
viewModelScope.launch {}
RAC
Disposable
disposable.dispose()
React
useEffect 清理函数
return () => cleanup()
Flutter
StreamSubscription
subscription.cancel()
鸿蒙
框架管理
组件销毁自动解绑
2.3 热流 vs 冷流(部分平台) 在 RxJava、RAC、Kotlin Flow 中,流有「热」「冷」之分:
类型
含义
典型场景
冷流
每次订阅才执行,每个订阅者独立收到完整数据
网络请求、文件读取
热流
无论是否订阅都会持续发送,多订阅共享同一流
按钮点击、通知、UI 事件
React / Flutter / 鸿蒙 的「状态」更接近热流:始终有一个当前值,变化时通知依赖方。
三、各平台实现原理浅析 3.1 Android:RxJava 与 Kotlin Flow RxJava :基于 ReactiveX 规范,Observable 链式操作符,每次操作返回新 Observable,订阅时从上游向下游传递事件。
1 2 3 4 5 6 7 8 9 10 11 Observable.create<String> { emitter -> emitter.onNext("Hello" ) emitter.onComplete() } .map { it.uppercase() } .filter { it.length > 0 } .subscribe( { println(it) }, { it.printStackTrace() } )
Kotlin Flow :冷流,基于协程,背压天然支持,与 StateFlow/SharedFlow 配合做 UI 状态和事件。
1 2 3 4 5 flowOf(1 , 2 , 3 ) .map { it * 2 } .filter { it > 2 } .collect { println(it) }
LiveData :轻量级、生命周期感知,主线程回调,适合简单 UI 状态,官方推荐新项目用 Flow 替代。
3.2 iOS:ReactiveCocoa / ReactiveSwift Signal :热信号,由 pipe() 创建,observer 控制发送。
1 2 3 let (signal, observer) = Signal <String , Never >.pipe()signal.observeValues { print ($0 ) } observer.send(value: "Hi" )
SignalProducer :冷信号,每次 start 执行一次,适合异步任务。
1 2 3 4 5 let producer = SignalProducer <String , Never > { obs, _ in obs.send(value: "Hello" ) obs.sendCompleted() } producer.startWithValues { print ($0 ) }
3.3 鸿蒙 ArkUI:装饰器驱动的状态 ArkUI 用装饰器声明「可观察」的状态,状态变化自动触发 UI 更新:
1 2 3 4 5 6 7 8 9 10 11 12 @State count : number = 0 @Observed class User { name : string }
@ObservedV2 + @Trace (V2):支持深层属性观察,解决嵌套对象不可观察的问题。
3.4 React:状态与副作用 React 的响应式体现在「状态驱动 UI」和「副作用与依赖同步」:
1 2 3 4 5 6 7 8 9 10 11 const [keyword, setKeyword] = useState ('' );const [users, setUsers] = useState ([]);useEffect (() => { if (keyword.length < 3 ) return ; const timer = setTimeout (() => { searchUsers (keyword).then (setUsers); }, 300 ); return () => clearTimeout (timer); }, [keyword]);
状态变化 → 重新渲染 → 虚拟 DOM diff → 最小化 DOM 更新。
3.5 Flutter:Stream 与 Listenable Stream :Dart 异步流,类似冷流,支持 map、where、asyncMap 等。
1 2 3 4 Stream.periodic(Duration (seconds: 1 )) .map((i) => i * 2 ) .take(5 ) .listen((value) => print (value));
ValueNotifier / ChangeNotifier :同步、轻量,配合 ValueListenableBuilder 做局部重建:
1 2 3 4 5 final counter = ValueNotifier<int >(0 );ValueListenableBuilder<int >( valueListenable: counter, builder: (_, value, __) => Text('$value ' ), )
四、操作符与组合逻辑的共性 4.1 转换类:map / filter
操作符
含义
Android
iOS RAC
Flutter
React
map
值转换
map {}
.map {}
.map()
派生 state
filter
过滤
filter {}
.filter {}
.where()
条件 + early return
4.2 组合类:combineLatest / merge 多流组合在各平台均有对应能力:
场景
RxJava
RAC
Flutter
React
多源最新值
combineLatest
combineLatest
RxDart combineLatest2
多个 useState + useEffect
多流合并
merge
merge
StreamGroup.merge
自定义逻辑
4.3 扁平化:flatMap / switchMap 将「每个值 → 新流」展开,常用于搜索联想、取消旧请求:
平台
操作符
行为
RxJava
flatMap / switchMap
switchMap 只保留最新内层流
RAC
flatMap(.latest)
新关键词取消上次请求
Flutter
asyncExpand
类似 flatMap
React
useEffect + 清理
依赖变化时清理上次 effect
4.4 时间控制:debounce / throttle 防抖、节流在搜索、滚动等场景通用:
平台
防抖
节流
RxJava
debounce()
throttleFirst/throttleLatest
RAC
debounce()
throttle()
Flutter
debounce from rxdart
throttle from rxdart
React
自定义 setTimeout + cleanup
useThrottle / lodash
五、源码层面的共通原理 5.1 链式结构与包装 操作符通常不修改原流,而是返回新的「包装流」,内部订阅上游并转换后传给下游:
1 2 3 4 5 6 7 8 9 Observable.map(f) │ ├─ 创建 MapObservable │ │ │ ├─ source = 上游 Observable │ └─ mapper = 转换函数 f │ └─ subscribe 时:上游.subscribe(下游的包装 Observer) 下游 Observer 收到值时:onNext(mapper(value))
RAC 的 map 也是类似结构:Signal { observer in self.observe { ... observer.send(value: transform($0)) } }。
5.2 订阅传播 订阅是「从下游往上游」建立,事件是「从上游往下游」传递:
1 2 3 4 5 6 subscribe(observer) → 下游 Observable 订阅其 source → 再往上游订阅 → … 直到最顶层 → 顶层开始 onNext/onComplete → 层层传递到最下游
5.3 取消传播 Disposable / Job 形成链:取消下游会向上传递,终止整条链的执行。
六、跨平台示例:搜索防抖 + 取消旧请求 6.1 Android (RxJava) 1 2 3 4 5 6 7 8 RxTextView.textChanges(searchEditText) .map { it.toString() } .filter { it.length >= 2 } .debounce(300 , TimeUnit.MILLISECONDS) .switchMap { keyword -> api.searchUsers(keyword).toObservable() } .observeOn(AndroidSchedulers.mainThread()) .subscribe({ users -> updateUI(users) }, { it.printStackTrace() }) .addTo(compositeDisposable)
6.2 iOS (RAC) 1 2 3 4 5 6 7 8 9 10 searchTextField.reactive.continuousTextValues .filter { ($0 ?? "" ).count >= 2 } .debounce(0.3 , on: QueueScheduler .main) .flatMap(.latest) { keyword in searchAPI(keyword ?? "" ) } .observe(on: UIScheduler ()) .observeValues { [weak self ] users in self ? .updateSearchResults(users) }
6.3 鸿蒙 (ArkUI) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 @State keyword : string = '' @State users : User [] = []private timer : number = -1 onInputChange (value : string ) { this .keyword = value clearTimeout (this .timer ) if (value.length < 2 ) return this .timer = setTimeout (() => { SearchAPI .searchUsers (value).then ((users : User [] ) => { this .users = users }) }, 300 ) }
6.4 React 1 2 3 4 5 6 7 8 9 10 11 12 13 14 const [keyword, setKeyword] = useState ('' );const [users, setUsers] = useState ([]);useEffect (() => { if (keyword.length < 2 ) return ; const timer = setTimeout (() => { let cancelled = false ; searchUsers (keyword).then (data => { if (!cancelled) setUsers (data); }); return () => { cancelled = true ; }; }, 300 ); return () => clearTimeout (timer); }, [keyword]);
6.5 Flutter 1 2 3 4 5 6 7 8 9 10 11 12 13 final _keyword = StreamController<String >.broadcast();final _users = ValueNotifier<List <User>>([]);_keyword.stream .where((s) => s.length >= 2 ) .transform(StreamTransformer.fromHandlers( handleData: (data, sink) => Timer(Duration (milliseconds: 300 ), () => sink.add(data)), )) .asyncMap((k) => searchAPI(k)) .listen((users) => _users.value = users); _keyword.add(controller.text);
七、实际项目应用案例 7.1 登录表单校验(多字段组合) 需求 :用户名 ≥3 字符、密码 ≥6 字符时,登录按钮才可点击。
平台
实现要点
RxJava
combineLatest(username, password).map { u, p -> u.length>=3 && p.length>=6 }
RAC
Signal.combineLatest(username.signal, password.signal).map { … }
React
useMemo 派生 canLogin = user.length>=3 && pwd.length>=6
Flutter
ValueNotifier 或 Stream,combineLatest2 派生
鸿蒙
@State user/pwd,computed 或 @Computed 派生 canLogin
7.2 多数据源合并展示 需求 :本地缓存 + 网络接口合并去重后展示。
各平台通用思路:
本地流 + 远程流
combineLatest / zip / merge 合并
map 去重、排序
订阅结果更新 UI
7.3 列表下拉刷新 + 分页加载 需求 :下拉刷新、上拉加载更多,防重复请求。
用 Subject / PublishSubject 表示下拉、触底事件
debounce 防抖
flatMap/switchMap 发起请求,合并到单一列表流
错误重试、loading 状态管理
7.4 电商 App 购物车总价 需求 :商品数量、价格变化时,实时计算总价。
每个商品的数量、单价作为流/状态
combineLatest 合并所有项
map/reduce 计算总价
单一订阅更新总价 UI
八、各平台选型建议
场景
Android
iOS
鸿蒙
React
Flutter
新项目
优先 Kotlin Flow
RAC/RxSwift
ArkUI 原生
Hooks
Stream + ValueNotifier
复杂异步流
RxJava / Flow
RAC
自定义 + Promise
useEffect + 自定义 Hook
rxdart
简单 UI 状态
StateFlow / LiveData
@Published
@State
useState
ValueNotifier
跨层级状态
ViewModel + StateFlow
单例 + Property
@Provide/@Consume
Context/Redux
Provider/Riverpod
九、共同的最佳实践
生命周期绑定 :订阅要在页面/组件销毁时取消,避免泄漏。
线程/调度 :UI 更新必须在主线程/主 Isolate,注意 observeOn / UIScheduler。
防抖与节流 :输入、滚动等高频事件务必做时间控制。
取消旧请求 :搜索、联想场景用 switchMap / flatMap(.latest) 或 useEffect 清理。
错误处理 :onError / catch / retry 统一处理,避免吞掉异常。
弱引用 :闭包中 [weak self] / WeakReference,防止循环引用。
十、总结
维度
共性
本质
观察者模式 + 数据流抽象
思想
声明式、数据驱动、自动传播
操作符
map、filter、combine、flatMap、debounce 等在各平台都有对应
取消
Disposable / 清理函数 / 生命周期绑定
热/冷
部分平台区分热流(共享)与冷流(按需执行)
应用
表单校验、搜索联想、多源合并、列表分页、实时计算
掌握这些共性后,从一个平台迁移到另一个平台时,可以快速找到对应概念和 API,写出一致、可维护的响应式代码。
参考资源