【重学 Vue:深入本质,脱离八股文,彻底搞定面试】:(六)响应式的本质

在前两篇文章【重学 Vue:深入本质,脱离八股文,彻底搞定面试】:(四)数据拦截的本质《【重学 Vue:深入本质,脱离八股文,彻底搞定面试】:(五)响应式数据的本质》 中,我们深入理解了 JS 中的两种数据拦截方式ref 和 reactive 实现响应式的策略,明确了 什么操作会触发数据拦截

但我们仍然留下了一个关键问题没有深入探讨:Vue 是如何知道某个函数依赖了哪些响应式数据?又是如何追踪并在数据变化时通知这些函数重新执行的?

这,正是 Vue 响应式系统的“核心秘密”:依赖收集 + 派发更新

本篇文章将带你深入这个核心机制,并用多个示例拆解,演示多种典型的依赖收集和派发更新场景,带你彻底摆脱”黑箱式使用“,玩转”响应式陷阱“。


🧩 本文结构:

  1. Vue 响应式的核心机制
  2. 如何建立依赖
  3. 是否收集依赖示例
  4. 是否派发更新示例

一、Vue 响应式的核心机制

Vue 的响应式系统本质上是一种“函数与数据之间的映射关系机制”,数据变了,对应的函数重新运行。为了实现这一点,就需要两个核心机制:

  • 依赖收集:在函数运行期间,收集它所依赖的响应式数据。
  • 派发更新:当响应式数据变化时,通知依赖它的函数重新执行。

二、 如何建立依赖

依赖关系的核心是:

函数运行时读取了响应式数据 ⇒ 函数依赖该响应式数据。

✅ 判断是否建立依赖的关键:

  • 函数必须是 被监控的函数
  • 函数 **运行期间 **必须有 同步代码 读取了 响应式数据
  • 读取操作被拦截(即访问响应式对象的属性)才会建立依赖

📌 特别说明:

  • 赋值不会建立依赖,只有“读取”才会。
  • 异步读取不会建立依赖。

📌 被监控的函数:

  • effect(底层 API)
  • watchEffect
  • watch
  • 组件的渲染函数

📌响应式数据:

  • ref
  • reactive
  • computed
  • props

这些数据具有 可追踪变化 的能力,即当数据发生变换会通知一些函数重新执行。


三、是否收集依赖示例

📍 demo1:非响应式变量

var a;
function foo() {
  console.log(a); // ❌ 无依赖,a 不是响应式数据
}

📍 demo2:响应式变量

import { ref } from "vue";
var a = ref(1);
function foo() {
  console.log(a); // ❌ 无依赖,虽然是 ref,但未访问 .value,读取未被拦截
  // console.log(a.value); // ✅ 有依赖,foo 依赖 a.value
}

📍 demo3:嵌套对象属性读取

import { ref } from "vue";
var a = ref({ b: 1 });
const k = a.value;
const n = k.b;
function foo() {
  a;
  a.value; // ✅ 有依赖
  a.value.b; // ✅ 有依赖
  k.b; // ✅ 有依赖
  n;
}

📍 demo4:各种访问形式都建立依赖

import { ref } from "vue";
var a = ref({ b: 1 });
const k = a.value;
const n = k.b;
function foo() {
  function fn2(){
    a;
    a.value.b; // ✅ 有依赖
    n;
  }
  fn2();
}

📍 demo5:异步读取不建立依赖

import { ref } from "vue";
var a = ref({ b: 1 });
const k = a.value;
async function foo() {
  a;
  a.value; // ✅ 有依赖
  await 1;
  k.b;     // ❌ 无依赖
}

🚫 await 后的代码不再参与依赖收集。

📌_ 详见:_Vue 响应式为什么异步读取不建立依赖?


四、是否派发更新示例

demo 共用部分:

import { ref, watchEffect } from "vue";
const state = ref({ a: 1 });
const k = state.value;
const n = k.a;

📍 demo1:完整收集依赖

watchEffect(() => {
  console.log("运行");
  state; // ❌ 无依赖
  state.value; // ✅ 依赖 value
  state.value.a; // ✅ 依赖 value,value.a
  n; // ❌ 无依赖
});
setTimeout(() => {
  state.value = { a: 3 }; // 会重新运行
  // state.value; // 不会重新运行
  // state.value.a = 1; // 不会重新运行,值没变
  // k.a = 2; // 会重新运行
  // n++; // 不会重新运行
  // state = 100; // 不会重新运行,已经不是 ref 对象了
}, 500);

📍 demo2:只依赖 value,不依赖 a 属性

watchEffect(() => {
  console.log("运行");
  state; // ❌ 无依赖
  state.value; // ✅ 依赖 value
  n;
});
setTimeout(() => {
  state.value.a = 100; // 不会重新运行
}, 500);

📍 demo3:重新赋原始值

watchEffect(() => {
  console.log("运行");
  state;
  state.value.a; // ✅ 依赖 value,value.a
  n;
});
setTimeout(() => {
  state.value.a = 1; // 不会重新运行,值没有改变
}, 500);
setTimeout(() => {
  state.value.a = 2; // 会重新运行
}, 1000);
setTimeout(() => {
  k.a = 3; // 会重新运行
}, 1500);

📍 demo4:重新赋引用值

watchEffect(() => {
  console.log("运行");
  state.value.a; // ✅ 依赖 value,value.a
});
setTimeout(() => {
  state.value = { a: 1 }; // 会重新运行
}, 500);
setTimeout(() => {
  state.value.a = 2; // 会重新运行
}, 1000);
setTimeout(() => {
  k.a = 3; // 不会重新运行,因为前面修改了 state.value,不再是同一个代理对象
}, 1000);

📍 demo5:k.a 建立依赖,value 重新赋引用值

watchEffect(() => {
  console.log("运行");
  state.value.a; // ✅ 依赖 value,value.a
  k.a; // 返回的 proxy 对象的 a 成员
});
setTimeout(() => {
  state.value = { a: 1 }; // 会重新运行
}, 500);
setTimeout(() => {
  k.a = 3; // 会重新运行
}, 1000);

📍 demo6:拦截value 的 get, a 的 set

watchEffect(() => {
  console.log("运行");
  state.value.a = 2; // ✅ 仅依赖 value,拦截value 的 get, a 的 set
});
setTimeout(() => {
  state.value.a = 100; // 不会重新运行
  // state.value = {}; // 会重新运行
}, 500);

🧠 总结

  • 所谓响应式,其实就是函数和数据的一组映射,当数据发生变化,会将该数据对应的所有函数全部执行一遍。当然这里的数据和函数都是有要求的。数据是响应式数据,函数是被监控的函数
  • 收集数据和函数的映射关系在 Vue 中被称之为依赖收集,数据变化通知映射的函数重新执行被称之为派发更新
  • 什么时候会产生依赖收集?
    • 只有被监控的函数,在它的同步代码运行期间读取操作被拦截的响应式数据,才会建立依赖关系;
    • 建立了依赖关系之后,响应式数据发生变化,对应的函数才会重新执行.

📌 下一篇:【重学 Vue:深入本质,脱离八股文,彻底搞定面试】:(七)响应式和组件渲染

敬请期待~


如果你觉得这篇文章对你有帮助,欢迎关注 + 点赞 + 收藏,我会持续输出「Vue 技术本质」系列内容。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值