哈曼卡顿Harman Kardon One(哈曼卡顿智能音箱)
156.1MB · 2026-03-12
作为程序员,我们或多或少都听过“响应式”这个词——比如 Vue3 的响应式原理、React 的状态管理,背后都藏着一套“数据变了,页面自动更”的魔法。而这套魔法的核心,就是 Proxy + effect + track/trigger 这铁三角。
很多人第一次接触这三个东西,都觉得像看天书:Proxy 是干嘛的?effect 跟它有啥关系?track 和 trigger 又是来凑什么热闹?别急,今天咱们不玩晦涩概念,用“打工人协作”的类比,把这套流程讲得明明白白,看完你会直呼“原来这么简单,还想看下一篇!”
先给大家定个小目标:看完这篇,你能搞懂“数据修改后,页面为什么能自动更新”,甚至能自己写一个极简版的响应式demo。话不多说,开整!
在讲流程之前,咱们先给这三个核心角色分分工,就像一个小团队,每个人都有自己的活儿,少了谁都玩不转。
Proxy 翻译过来是“代理”,顾名思义,它不直接干活,而是站在数据的“门口”,监控着数据的一举一动——比如数据被读取了、被修改了,它都能第一时间知道。
举个生活化的例子:你是一个程序员(数据),Proxy 就是你的“秘书”。别人想找你要资料(读取数据),得先经过秘书;别人想让你改代码(修改数据),也得先告诉秘书。秘书不会干涉你干活,但会把你的所有操作都记下来,或者通知相关的人。
这里要注意:Proxy 只负责“监控”,不负责“处理”——它看到数据被读取了,不会自己做什么,只会喊一句“有人读数据啦!”;看到数据被修改了,也只会喊一句“数据被改啦!”。至于谁来响应这些喊声,就是另外两个角色的事了。
effect 翻译过来是“副作用”,但咱们不用纠结这个专业术语,你就把它理解成“需要依赖数据、并且数据变了就必须重新干活的函数”——比如渲染页面的函数、计算属性的函数,都是 effect。
还是刚才的类比:effect 就是公司里的“业务岗”,比如运营、设计师。他们的工作依赖于你的代码(数据):你改了代码,运营就要重新写文案,设计师就要重新做图。他们不会主动去看你改没改代码,全靠秘书(Proxy)通知。
关键一点:effect 执行的时候,会自动去“读数据”——比如渲染页面时,会读取 data 里的 name、age 等数据。而这个“读数据”的动作,就会被 Proxy 监控到。
track 是“跟踪”,trigger 是“触发”,这俩是一对好搭档,负责在 Proxy 和 effect 之间传递消息,相当于“传话筒”。
简单说:当 effect 读取数据时,track 会把“这个 effect 依赖了这个数据”记下来(相当于给秘书说“运营要用到这个代码,改了记得通知他”);当数据被修改时,trigger 会找到“依赖这个数据的所有 effect”,并让它们重新执行(相当于秘书通知运营“代码改了,快重新写文案”)。
到这里,三个角色的分工就清晰了:Proxy 监控数据,effect 负责干活,track/trigger 负责传消息。接下来,咱们就看它们仨是怎么配合完成“数据变、页面更”的完整流程的。
为了让大家看得更清楚,咱们用一个极简的场景来模拟整个流程:假设我们有一个数据 data = { name: "张三" },还有一个 effect 函数,负责把 name 渲染到页面上。
整个流程分为两个阶段:依赖收集阶段(读数据) 和 响应触发阶段(改数据) ,咱们一步步来。
当页面第一次渲染时,effect 函数会被执行,这个过程就是“读数据”,也是依赖收集的过程,具体步骤如下:
这一步的核心:track 的作用就是“记笔记”,把数据和依赖它的 effect 一一对应起来。就像秘书把“运营依赖代码A”“设计师依赖代码B”记在小本子上,方便后续通知。
现在,我们修改数据:data.name = "李四"。这时候,响应触发阶段就开始了,具体步骤如下:
这一步的核心:trigger 的作用就是“喊人干活”,根据 track 记的“笔记”,找到所有需要重新执行的 effect,让它们更新。就像秘书看到你改了代码A,就去通知运营:“代码A改了,快重新写文案”。
很多人看完流程会疑惑:“这些问题,Vue3不是都解决了吗?” 没错!Vue3 确实通过 reactive、ref 等API封装,帮我们规避了大部分底层坑,但了解这些底层局限,才能更清楚API的设计逻辑,避免误用API导致踩坑。咱们结合Vue3的解决方案,把这些细节讲透(想深入API封装逻辑,下次专门写一篇):
光说不练假把式,咱们用几行代码,写一个极简版的 Proxy + effect + track/trigger,感受一下这个流程(复制到浏览器控制台就能运行):
// 1. 定义依赖表:key是数据对象,value是“数据属性 -> 依赖的effect数组”
const targetMap = new WeakMap();
// 2. track:收集依赖
function track(target, key) {
// 找到当前正在执行的effect
const activeEffect = effectStack[effectStack.length - 1];
if (!activeEffect) return;
// 给当前数据对象,创建一个“属性- effect”映射
let depsMap = targetMap.get(target);
if (!depsMap) targetMap.set(target, (depsMap = new Map()));
// 给当前属性,创建一个effect数组
let deps = depsMap.get(key);
if (!deps) depsMap.set(key, (deps = new Set()));
// 把当前effect加入依赖数组
deps.add(activeEffect);
}
// 3. trigger:触发依赖
function trigger(target, key) {
// 找到当前数据对象的“属性- effect”映射
const depsMap = targetMap.get(target);
if (!depsMap) return;
// 找到当前属性依赖的所有effect
const deps = depsMap.get(key);
if (deps) {
// 挨个执行effect
deps.forEach(effect => effect());
}
}
// 4. effect:创建副作用函数
const effectStack = [];
function effect(fn) {
const effectFn = () => {
// 把当前effect加入栈,标记为“正在执行”
effectStack.push(effectFn);
// 执行用户传入的函数(会读数据,触发track)
fn();
// 执行完,从栈中移除
effectStack.pop();
};
// 第一次执行,触发依赖收集
effectFn();
return effectFn;
}
// 5. 用Proxy代理数据
const data = new Proxy({ name: "张三" }, {
get(target, key) {
// 读数据时,触发track
track(target, key);
return target[key];
},
set(target, key, value) {
// 改数据时,触发trigger
target[key] = value;
trigger(target, key);
return true;
}
});
// 6. 测试:创建effect,渲染页面
effect(() => {
console.log("页面渲染:", data.name); // 第一次执行,输出“页面渲染:张三”
});
// 改数据,触发响应
data.name = "李四"; // 触发trigger,执行effect,输出“页面渲染:李四”
运行这段代码,你会发现:修改 data.name 后,console 会自动输出新的内容,这就是最极简的响应式!是不是瞬间就懂了?
今天咱们讲的,是 Proxy + effect + track/trigger 的基础流程,也是 Vue3 响应式的核心骨架。但实际开发中,还有很多细节没讲:
比如,effect 怎么取消依赖?track 怎么避免重复收集依赖?Proxy 监控数组的时候有什么坑?还有,Vue3 里的 reactive、ref 是怎么基于这套流程封装的?
这些问题,咱们下一篇接着聊!关注我,下次带你从“基础流程”进阶到“实战源码”,手把手教你看懂 Vue3 响应式的底层逻辑,再也不怕被面试官问倒~
156.1MB · 2026-03-12
31.0MB · 2026-03-12
117.49M · 2026-03-12