引言
Vue 3 最大的革新之一就是响应式系统的重构。从 Vue 2 的 Object.defineProperty 升级到基于 Proxy 的实现,带来了性能提升和功能增强。本文将深入剖析 Vue 3 响应式系统的核心原理,让你真正理解 ref、reactive 等 API 背后的秘密。
一、Vue 2 的局限性
在 Vue 2 中,响应式系统使用 Object.defineProperty 实现:
scss
// Vue 2 的响应式实现(简化版)
function defineReactive(obj, key, val) {
const dep = new Dep();
Object.defineProperty(obj, key, {
get() {
// 收集依赖
if (Dep.target) {
dep.addSub(Dep.target);
}
return val;
},
set(newVal) {
if (newVal !== val) {
val = newVal;
// 通知更新
dep.notify();
}
}
});
}
主要问题:
- 无法检测对象属性的添加和删除
- 无法检测数组索引的变化
- 需要 递归 遍历所有属性,性能开销大
- 不支持
Map、Set等数据结构
二、Vue 3 的 Proxy 方案
Vue 3 使用 Proxy 重构了响应式系统:
scss
// Vue 3 的响应式实现(简化版)
function reactive(obj) {
return new Proxy(obj, {
get(target, key, receiver) {
const res = Reflect.get(target, key, receiver);
// 依赖收集
track(target, key);
// 如果是对象,递归处理
return isObject(res) ? reactive(res) : res;
},
set(target, key, value, receiver) {
const oldValue = target[key];
const res = Reflect.set(target, key, value, receiver);
// 触发更新
if (oldValue !== value) {
trigger(target, key);
}
return res;
},
deleteProperty(target, key) {
const res = Reflect.deleteProperty(target, key);
if (res) {
trigger(target, key);
}
return res;
}
});
}
优势:
- ✅ 可以拦截属性的添加和删除
- ✅ 支持数组索引变化
- ✅ 支持
Map、Set、WeakMap、WeakSet - ✅ 懒代理,提升性能
三、核心概念解析
1. 依赖收集(Track)
ini
// 全局的 activeEffect
let activeEffect = null;
const targetMap = new WeakMap();
function track(target, key) {
if (!activeEffect) return;
let depsMap = targetMap.get(target);
if (!depsMap) {
depsMap = new Map();
targetMap.set(target, depsMap);
}
let dep = depsMap.get(key);
if (!dep) {
dep = new Set();
depsMap.set(key, dep);
}
dep.add(activeEffect);
}
2. 触发更新(Trigger)
ini
function trigger(target, key) {
const depsMap = targetMap.get(target);
if (!depsMap) return;
const dep = depsMap.get(key);
if (dep) {
dep.forEach(effect => effect());
}
}
3. Effect 副作用函数
ini
function effect(fn) {
const effectFn = () => {
activeEffect = effectFn;
const result = fn();
activeEffect = null;
return result;
};
// 立即执行一次,收集依赖
effectFn();
return effectFn;
}
四、ref 与 reactive 的区别
ref - 处理基本类型
javascript
import { ref, effect } from 'vue';
const count = ref(0);
// 访问需要 .value
effect(() => {
console.log(count.value); // 自动追踪
});
// 修改
count.value++;
实现原理:
csharp
function ref(value) {
return {
_isRef: true,
get value() {
track({ _isRef: true }, 'value');
return value;
},
set value(newVal) {
if (value !== newVal) {
value = newVal;
trigger({ _isRef: true }, 'value');
}
}
};
}
reactive - 处理对象
ini
import { reactive, effect } from 'vue';
const state = reactive({
user: {
name: '张三',
age: 25
},
items: []
});
effect(() => {
console.log(state.user.name);
console.log(state.items.length);
});
// 修改任意属性都会触发更新
state.user.age = 26;
state.items.push('新项');
五、实际应用场景
场景 1:表单双向绑定
javascript
import { ref, effect } from 'vue';
const form = ref({
username: '',
password: ''
});
// 自动追踪表单变化
effect(() => {
console.log('表单数据:', form.value);
// 这里可以自动提交或验证
});
// 修改表单
form.value.username = 'admin';
场景 2:计算属性
ini
import { ref, computed } from 'vue';
const firstName = ref('张');
const lastName = ref('三');
const fullName = computed(() => {
return firstName.value + lastName.value;
});
// 当 firstName 或 lastName 变化时,fullName 自动更新
firstName.value = '李';
console.log(fullName.value); // '李三'
场景 3:列表渲染优化
php
import { reactive, effect } from 'vue';
const list = reactive([
{ id: 1, text: 'Item 1' },
{ id: 2, text: 'Item 2' }
]);
effect(() => {
// 只追踪实际使用的数据
list.forEach(item => {
console.log(item.text);
});
});
// 添加新项只会触发一次更新
list.push({ id: 3, text: 'Item 3' });
六、性能优化技巧
1. 使用 shallowRef 和 shallowReactive
对于大型对象,不需要深度响应式:
javascript
import { shallowRef, shallowReactive } from 'vue';
// 只追踪顶层属性
const largeData = shallowRef({
config: { /* 大量配置 */ },
cache: new Map() // 内部变化不会触发更新
});
// 手动触发更新
largeData.value = newData;
2. 使用 markRaw 避免不必要的代理
javascript
import { reactive, markRaw } from 'vue';
const state = reactive({
// 不需要响应式的对象
apiClient: markRaw(new Axios())
});
七、总结
Vue 3 的响应式系统通过 Proxy 实现了更强大、更高效的响应式能力:
核心要点:
- Proxy 比 Object.defineProperty 更强大 - 支持更多操作
- 依赖收集是核心 -
track和trigger配合工作 - ref 和 reactive 各有适用场景 - 基本类型用 ref,对象用 reactive
- 性能优化很重要 - 合理使用 shallowRef、markRaw 等工具
最佳实践 :
- 优先使用
composition API组织代码 - 对大型对象使用
shallowRef提升性能 - 避免在
setup中创建不必要的响应式对象 - 使用
toRefs解构reactive对象保持响应式
Vue 3 的响应式系统不仅是一个技术升级,更是开发体验的巨大提升。理解其原理,能让你更好地利用 Vue 3 的强大功能,写出更高效、更优雅的代码。