
在打造响应式系统时,一个容易遇到的状况是,effect
在执行期间同时"读取"又"写入"同一个依赖,这会造成自我触发 (self-trigger)。
effect
为了读值而被追踪为依赖,但它在同一次执行中又修改了同一个值,导致立即再次触发自己,形成无限循环。
可以看下面的示例
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Document</title>
<style>
body {
padding: 150px;
}
</style>
</head>
<body>
<div id="app"></div>
<script type="module">
// import { ref, effect } from '../../../node_modules/vue/dist/vue.esm-browser.js'
import { ref, effect } from '../dist/reactivity.esm.js'
const count = ref(0)
effect(() => {
// console.log(count++) // 错误的写法,会引发无限循环
console.log(count.value++) // 正确的写法应该是 count.value++,但这里为了演示问题
})
</script>
</body>
</html>
你打开控制台,会看到浏览器因为栈溢出而崩溃:

问题分析
JavaScript
effect(() => {
count.value++ // 这里有两个操作!
})
实际上等同于:
JavaScript
effect(() => {
const currentValue = count.value; // 1. 读取 count (收集依赖)
count.value = currentValue + 1; // 2. 修改 count (触发更新)
})
- 读取
count.value
:这会触发依赖收集,将当前的effect
注册为count
的订阅者。 - 修改
count.value
:这会触发更新,响应式系统会遍历count
的所有订阅者并执行它们。由于effect
自身就是订阅者,它会被重新执行,从而形成了自我触发的无限循环。
无限循环的流程
同一个
effect
在追踪期间读取了 count
,又立刻写入了 count
,使自己被再度排入执行队列;这个"读→写→再排队"的节奏每一轮都发生一次,因此形成无限循环。
解决方法
Vue 3 使用 tracking
标记来防止同一个 effect
在其自身执行期间,被重复加入到更新队列中:

代码实现
-
effect.ts
TypeScript// effect.ts import { Link, startTrack, endTrack } from './system' export let activeSub; export class ReactiveEffect { // ... tracking = false // 是否正在执行(收集中) // ... }
-
system.ts
TypeScript// system.ts // ... export function propagate(subs) { let link = subs let queuedEffect = [] while (link) { const sub = link.sub // 只有不在执行中的 effect 才加入队列 if (!sub.tracking) { queuedEffect.push(sub) } link = link.nextSub } queuedEffect.forEach(effect => effect.notify()) } /** * 开始追踪,将 depsTail 设为 undefined */ export function startTrack(sub) { sub.depsTail = undefined sub.tracking = true // 标记为正在执行 } /** * 结束追踪,找到需要清理的依赖 */ export function endTrack(sub) { sub.tracking = false // 执行结束,取消标记 // ... (依赖清理逻辑) }
如果我们没有 tracking
机制,effect
在读 count
时被收集,写 count
时又触发自己,接着再执行自己,永远停不下来。通过增加这个简单的状态标记,我们就有效地切断了这种自我触发的无限循环。
想了解更多 Vue 的相关知识,抖音、B站搜索我师父「远方os」,一起跟日安当同学。