2.3 数据驱动与响应式系统


2.3 数据驱动与响应式系统

本节摘要:数据驱动指"界面是数据的函数"——数据决定界面"应该是什么样"。响应式系统则回答触发时机问题:Vue 用依赖追踪自动发现"谁该更新",React 用显式触发告诉组件"更新发生了"。两条路线是理解两框架差异最关键的一把钥匙。

本节阅读目标
阅读完本节,你应当能够:

  1. 区分"数据驱动"与"响应式触发"是两个不同层面的概念。
  2. 讲清 Vue 依赖追踪的基本过程与它的精细粒度来源。
  3. 说清 React 显式触发渲染的流程与其牺牲的精确度。

一、数据驱动的第一性原则

"界面是数据的函数"这六个字,是 Data-driven UI 的内核。同样一个用户列表,数据是"空数组",界面是空态提示;数据填满,界面出现列表。你只管维护数据,界的"长相"由框架算出来。这跟第 1.1 节"声明式"是一回事的两面:声明式是理念,数据驱动是它在状态维度的体现。

数据驱动带来的直接好处:状态源唯一,界面是它的投影,调试时改动数据就能复现问题,不必猜测是哪个 DOM 忘了同步。

二、难点转移:谁在什么时候更新

数据驱动把"要不要更新的判断"从开发者转移到框架。这个难题拆两半:

  1. 感知变化:凭什么知道某个数据变了?
  2. 传导变化:知道了,通知哪个组件重渲染?

React 与 Vue 的分野,正是在这两步上选了不同分工。

三、两条触发路线的机制对比

Vue 的依赖追踪(自动发现)

Vue 在读取数据时记下"这个组件依赖了这份数据",形成一张依赖表;数据一变,框架翻表,精确通知依赖它的组件。只有被依赖的组件重跑,粒度细、无谓更新少。变化感知靠数据劫持(各版本的 Proxy / 访问器实现),传导靠依赖表。

React 的显式渲染(哪种更新会重跑)

React 里状态更新通过 setState 或 useState 的 setter 触发,从该组件起整棵子树重新渲染(配合虚拟 DOM diff 收敛最终变更)。它不做"谁依赖谁"的精细追踪,而是"这个节点被标脏了,以它为根往下重渲"。更直白,但无谓重渲染的机会更多,需要靠 memo 等优化手段控制粒度。

一个对照表把差异摊开:

维度 Vue(依赖追踪) React(显式触发)
感知变化 数据劫持自动感知 调用 setter 主动触发
传导粒度 精确到依赖组件的函数 以组件为根的整树重渲
无谓更新 天然较少 依赖 memo 等手动约束
心智 少管"何时更新" 开发者需理解渲染时机

四、一张图说清响应式分工

图:两种响应式触发路线对照

图:两种响应式触发路线对照

五、用最小代码摸清两种机制

纸上谈兵总归虚,各看一段最贴近直觉的骨架。

Vue 端,依赖追踪的"记录"是隐形发生的——你在 template 里读 user.name,框架在读取的一刻把当前组件记进 user 的依赖表;之后你哪怕在别处改了 user.name,框架翻表就能只通知"那一处":

// 简化示意:读取时登记、写入时派发 let _reactive = new Map(); function read(key, subscriber) { /* 把 subscriber 记进依赖表 */ } function mutate(key, value) { /* 翻依赖表,逐个通知订阅者 */ }

React 端没有这张表,setState 一调用就沿组件树向下标脏整棵子树再去 diff 收敛:

setUser((prev) => ({ ...prev, name: newName })); // 触发的是"以本组件为根的整块重渲"

把这两段并排看,你能一眼分出差别:Vue 告诉你"是哪几处变了",React 告诉你"从哪个组件往下重渲"。2.2 提到虚拟 DOM 负责把 React 的粗粒度重渲"收敛成最小变更",正是为了补上 React 显式触发那条更粗暴的路径。

六、深层影响与工程取舍

细粒度追踪让 Vue 居中省心,但"何时重渲"对读者更透明的要求较低;React 把渲染时机暴露给你,让你拥有更强的控制力,代价是要懂背后的时机。两者没有绝对优劣,但理解这条对照之后:你在 Vue 里会少写很多不必要的优化,在 React 里会主动去防无谓重渲染。第 3、4 章的代码练习会不断回到这张表。

把"谁触发了更新"这件事钉进心智

两条触发路线的差异,最终会落在你写代码时的两个习惯上。在 Vue 里,你基本可以"少操心何时更新"——只要保证数据走的是响应式通道,改了界面自己会跟上,依赖表兜住了"谁该动"。在 React 里,你要有点"渲染时机感":每次 setState 都意味着"从这里往下一整棵子树要重渲",于是你会发现需要偶尔用 memo 等手段把"没有变化的分支"挡在重渲之外。

这不是谁优谁劣的对比,而是两套纪律。对新同学常有一个现象:刚从 Vue 迁到 React 的人,容易把"数据变了界面没动"归咎于 bug,其实往往只是没有走 setState 这条唯一的触发通道;反之,刚从 React 迁到 Vue 的人,会过度手动控制渲染,做了很多不必要的优化。心里这幅"谁触发、谁重渲"的图,正是跨框架不踩坑的那根弦。

七、用一次改动做"谁该更新"的推演

把抽象落成一次具体的改动,最能检验你懂没懂两条路线。假设页面上有一个用户卡片粗组件和一个顶栏,顶栏里显示"当前用户:阿明"。现在用户把昵称改成"阿翔"。在 Vue 里,改的是 user.name 这个响应式数据,框架翻依赖表发现只有顶栏那处组件读了它,于是只重渲顶栏;卡片里的头像、简介只要没读 user.name,就纹丝不动。在 React 里,setUser({ ...prev }) 一调用,从持有这份 state 的那个组件往下,整棵子树都被标脏重渲,再由虚拟 DOM diff 把最终变更收敛到真正变化的那几处。

这两条路径的结果在屏幕上可能一模一样——都只更新了"阿翔"两个字。差别在过程:Vue 是从"数据被哪些组件依赖"逆向算出来,React 是从"从哪个组件往下重渲"正向推下去。于是工程上出现了一条可验证的经验:当 React 页面出现"明明改一个数据,却有一大片地方都在重跑效率"这类问题时,八成是没做 memo 收敛;而当 Vue 出现"改了数据居然没反应"时,八成是数据没走响应式通道(比如直接解构、用非响应式容器装了它)。一对判断,正好都掉头指向本节这张对照表的两端。

本节要点回顾

  • 数据驱动:界面是数据的函数,状态源唯一,界面是投影。
  • 两大难题:感知变化、传导变化,两条路线在此分界。
  • Vue 依赖追踪:劫持 + 依赖表,精确通知,无谓更新少。
  • React 显式触发:setter 标脏 + 整树重渲,粒度粗但可控。
  • 工程取舍:Vue 省心、React 可控,理解时机是两端的共同前提。

下一节接着讲组件从创建到销毁的时机问题——生命周期管理,看看"该在哪一步做数据加载、在哪一步做资源清理"。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U