在使用 Vue 时,你是否想过:当你修改一个变量,页面是如何自动更新的? 这背后其实就是 Vue 的响应式系统在发挥作用。
本文将深入探讨 Vue 的响应式原理,包括其核心机制、关键实现代码解析(基于 Vue 2 和 Vue 3 的不同实现),并通过简单示例来帮助你更好地理解“数据驱动视图”这一核心理念。
响应式系统(Reactivity System)指的是:当数据发生变化时,视图能自动感知并更新,无需开发者手动操作 DOM。
例如:
<div id="app">
<p>{{ message }}p>
div>
<script>
new Vue({
el: '#app',
data: {
message: 'Hello Vue!'
}
})
script>
当我们执行 vm.message = '你好 Vue!'
,页面中的 内容会自动更新,这就是响应式的效果。
Vue 2 的响应式系统依赖以下几个核心概念:
当我们在 data
中定义属性时,Vue 会递归遍历每个属性,使用 Object.defineProperty
将其转换为 getter 和 setter:
function defineReactive(obj, key, val) {
const dep = new Dep(); // 依赖收集器
Object.defineProperty(obj, key, {
get() {
Dep.target && dep.addSub(Dep.target); // 收集依赖
return val;
},
set(newVal) {
if (val !== newVal) {
val = newVal;
dep.notify(); // 通知依赖更新
}
}
});
}
class Dep {
constructor() {
this.subs = [];
}
addSub(watcher) {
this.subs.push(watcher);
}
notify() {
this.subs.forEach(watcher => watcher.update());
}
}
Watcher 的角色是“订阅数据变化”,一旦数据发生变化,它就会执行 update()
来更新视图。
Vue 3 使用了现代浏览器支持的 Proxy
替代 Object.defineProperty
,实现了更高效和灵活的响应式系统。
const state = reactive({ count: 0 });
watchEffect(() => {
console.log(`count is: ${state.count}`);
});
state.count++; // 自动触发响应
function reactive(target) {
const handler = {
get(target, key, receiver) {
track(target, key); // 依赖收集
return Reflect.get(target, key, receiver);
},
set(target, key, value, receiver) {
const result = Reflect.set(target, key, value, receiver);
trigger(target, key); // 触发更新
return result;
}
};
return new Proxy(target, handler);
}
相比 Vue 2 的 defineProperty
,Proxy 支持数组、嵌套对象、属性新增等更多情况。
Vue 3 中依赖收集与触发由 effect
和 track/trigger
实现:
let activeEffect;
function effect(fn) {
activeEffect = fn;
fn();
activeEffect = null;
}
const bucket = new WeakMap();
function track(target, key) {
if (!activeEffect) return;
let depsMap = bucket.get(target);
if (!depsMap) {
depsMap = new Map();
bucket.set(target, depsMap);
}
let deps = depsMap.get(key);
if (!deps) {
deps = new Set();
depsMap.set(key, deps);
}
deps.add(activeEffect);
}
function trigger(target, key) {
const depsMap = bucket.get(target);
if (!depsMap) return;
const deps = depsMap.get(key);
deps && deps.forEach(effect => effect());
}
这是响应式系统的最小实现模型,非常有助于深入理解。
特性 | Vue 2 (defineProperty ) |
Vue 3 (Proxy ) |
---|---|---|
基础 API | Object.defineProperty |
Proxy |
支持新增属性 | ❌ 需使用 Vue.set |
✅ 原生支持 |
数组支持 | ❌ 局限较多 | ✅ 全面支持 |
性能 | 一定递归开销 | 更高效 |
实现难度 | 较复杂,遍历对象属性 | 更简洁 |
function reactive(obj) {
const depsMap = new Map();
return new Proxy(obj, {
get(target, key) {
if (!depsMap.has(key)) depsMap.set(key, new Set());
depsMap.get(key).add(activeEffect);
return target[key];
},
set(target, key, value) {
target[key] = value;
if (depsMap.has(key)) {
depsMap.get(key).forEach(fn => fn());
}
return true;
}
});
}
let activeEffect = null;
function effect(fn) {
activeEffect = fn;
fn();
activeEffect = null;
}
// 使用
const state = reactive({ count: 0 });
effect(() => {
console.log(`count: ${state.count}`);
});
state.count++;
Vue 的响应式系统是其核心优势之一,真正实现了数据驱动视图。
Object.defineProperty
,存在一些局限性;Proxy
,支持更多功能和更高性能;