Push-based vs. Pull-based 响应式:细粒度系统背后的两大驱动模型

发布: (2025年12月15日 GMT+8 08:40)
5 min read
原文: Dev.to

Source: Dev.to

Cover image for Push-based vs. Pull-based Reactivity: The Two Driving Models Behind Fine-Grained Systems

回顾

基于之前关于响应性的核心思想的文章,这部分阐明 Push‑basedPull‑based 响应式模型之间的区别。

核心思想

在细粒度响应式系统中:

  • Push‑based(推送式)系统在值变化时立即执行计算。
  • Pull‑based(拉取式)系统会延迟计算,直到有人读取该值时才进行。

实际案例

Push‑based

想象在美食广场点餐。

  • 写入(下单): 你下单。
  • 推送(完成通知): 当餐点准备好时,你的呼叫器会震动或亮灯——更新被直接推送给你。
  • 效果(取餐): 你走到柜台去取餐。

响应式解释: 当源发生变化时,依赖节点会立即重新计算并立刻得到通知。

Pull‑based

现在想象去买一杯奶茶。

  • 写入(下单): 你下单点饮料。
  • 标记(仅更新状态): 当饮料准备好时,店家只会在屏幕上显示你的号码——他们不会直接通知你。
  • 读取 → 计算(仅在需要时): 当你查看屏幕时,这个读取操作会触发“哦,已经好了,我该去取了”。
  • 效果(取餐): 你去柜台。

响应式解释: 写入仅标记节点为脏;真正的计算在有人读取该值时才进行。

正式定义

模型行为焦点简化流程
Push‑based写入时计算:更新立即传播set() → propagate → compute → effect
Pull‑based写入时标记,读取时计算set() → markDirty ⏸ read() → if dirty → compute → effect

关键洞察: 两种模型都“推送”信号——Push 推送计算,而 Pull 推送脏标记。

时间线图示

Push‑based

push flow

Pull‑based

pull flow

优缺点

方面Push‑basedPull‑based
读取延迟最低——始终最新第一次读取可能触发重新计算
写入成本可能较高:O(depth × writes)较低:大多为 O(depth × 1)(仅标记脏)
过度计算高——即使从未读取也会计算低——仅在实际读取时计算
批处理困难——工作已经完成自然适配——稍后一次性刷新
调试可见性依赖链立即展开需要 DevTools 检查拉取发生时机
最佳使用场景高频写入、低读取(例如协作应用中的光标同步)低写入、高读取(仪表盘、图表)

注: Pull‑based 系统在标记阶段仍会遍历依赖图,但它们重新计算——仅将 dirty = true 设置为脏。

如何选择?

用例推荐模型原因
实时协作、游戏状态同步Push立即反映比避免额外计算更重要
大型仪表盘、数据可视化Pull写入稀少,读取频繁——仅计算实际需要的内容
时间线/滚动驱动动画Pull + SchedulerPull 推迟工作;调度器确保每帧最多一次重新计算
数据管道(昂贵计算多次复用)Push‑on‑Commit预先一次性计算,然后在各处复用

React 基本上是 Pull + Scheduler,这就是批处理工作方式的原因。
RxJS 和 MobX 是 Push‑on‑Commit 的经典例子。

常见误解

  • “Pull 意味着扫描整个图!”
    不——Pull 只在读取值时检查向上的相关依赖链。无需遍历
Back to Blog

相关文章

阅读更多 »

页面刷新后表单内容消失

Forem 动态 !Forem 标志 https://media2.dev.to/dynamic/image/width=65,height=,fit=scale-down,gravity=auto,format=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.co...