从零到一打造 Vue3 响应式系统 Day 10 - 为何 Effect 会被指数级触发?

DOM 交互

我们的响应式系统经过前几天的努力,已经初具雏形,感觉可以加入一些 DOM 交互,来进行简单的测试。

HTML 复制代码
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Title</title>
    <style>
      body {
        padding: 150px;
      }
    </style>
  </head>
  <body>
    <button id="btn">按钮</button>
    <script type="module">
      import { ref, effect } from '../dist/reactivity.esm.js'

      const flag = ref(true)

      effect(() => {
        flag.value
        console.count('effect')
      })

      btn.onclick = () => {
        flag.value = !flag.value
      }
    </script>
  </body>
</html>

我们预期每次点击按钮,effect 只会执行一次。但实际情况看起来不太妙。

console.count 的结果可以看到,effect 的执行次数随着点击呈现指数级增长。这肯定是不行的。

我们来了解一下问题的症结所在。

执行步骤图解

初始化页面

页面加载时,effect 执行一次。在执行过程中,读取了 flag.value,触发 getter 进行依赖收集。 系统会创建一个 link1 节点,将 effectflag 关联起来。到这里都符合预期。

第一次点击按钮

当按钮第一次被点击,flag.valuetrue 变为 false,触发了 setter。 setter 内的 propagate 函数开始遍历 flag 的依赖链表。

propagate 执行 link1 中存储的 effect.run()

effect 函数重新执行,又读取了 flag.value,再次触发了 getter。

此时问题出现了 :在 effect.run() 的过程中,又进行了一次依赖收集,系统创建了一个新的 link2 节点并添加到链表尾部。

执行结束后的链表:

第二次点击按钮

当按钮又被点击,flag.valuefalse 变为 true,再次触发 setter。

propagate 开始遍历依赖链表。但这一次,链表上有两个节点 (link1link2)。

  1. propagate 先执行 link1 中的 effect.run()effect 内部读取 flag.value,触发依赖收集,创建了一个新的 link3 节点并添加到链表尾部。
  2. propagate 接着执行 link2 中的 effect.run()effect 内部又一次读取 flag.value,触发依赖收集,又创建了一个新的 link4 节点并添加到链表尾部。

执行结束后的链表:

执行完成后的链表结构

我们可以发现在触发更新时,链表上的每一个节点 都会触发一次 effect 的重新执行,而每一次执行又会创建 一个新的节点加入到链表中,因此发生了指数级触发 effect 的情况。

关键问题点

每次 effect 重新执行时:

  1. 没有检查该 effect 是否已经存在于依赖链表中。
  2. 盲目地创建新的 Link 节点并添加到链表末尾。
  3. 导致依赖链表在每次更新时都会成倍增长。

因此,每次点击按钮,链表上的每一个 Link 都会触发一次 effect 的重新执行,而在每一次执行中又会创建新的 Link,从而导致重复执行和指数级增长现象。

因为下个篇幅比较长,今天就先讲到这里。大家需要先理解问题的症结所在,这样明天在实现解决方案时,才能明白我们为什么要那样做。


想了解更多 Vue 的相关知识,抖音、B站搜索我师父「远方os」,一起跟日安当同学。

相关推荐
todaycode1 分钟前
Vue + CPP项目
javascript·c++·vue.js
ZC跨境爬虫5 分钟前
跟着 MDN 学CSS day_7:(层叠优先级与继承)
前端·css·数据库·ui·html
Shadow(⊙o⊙)10 分钟前
qt信号和槽链接的接入与断开
开发语言·前端·c++·qt·学习
慕斯fuafua11 分钟前
JS——DOM操作
前端·javascript·html
微祎_19 分钟前
写给新手的 triton-inference-server-ge-backend:昇腾Triton推理服务后端到底是啥?
前端·人工智能·cann
烂不烂问厨房23 分钟前
两张图片拼接在一起中间有条白线
前端
掘金安东尼26 分钟前
浏览器跨域窗口通信技术调研:window.open 与 postMessage
前端
Highcharts.js2 小时前
缺失数据可视化图表开发实战|Highcharts创建人员出生统计面积图表示例
开发语言·前端·javascript·信息可视化·highcharts·图表开发
LaughingZhu9 小时前
Product Hunt 每日热榜 | 2026-05-21
前端·人工智能·经验分享·chatgpt·html
怕浪猫9 小时前
Electron 开发实战(一):从零入门核心基础与环境搭建
前端·electron·ai编程