vue怎么实现数据响应
Vue 数据响应的实现原理
Vue 的数据响应式系统基于 JavaScript 的 Object.defineProperty 或 ES6 的 Proxy(Vue 3 中使用)实现。以下是核心实现方式:
基于 Object.defineProperty(Vue 2)
通过 Object.defineProperty 劫持对象的属性访问和修改,在 getter 中收集依赖,在 setter 中触发更新。
function defineReactive(obj, key, val) {
Object.defineProperty(obj, key, {
get() {
console.log(`读取 ${key}`);
return val;
},
set(newVal) {
if (newVal !== val) {
console.log(`设置 ${key} 为 ${newVal}`);
val = newVal;
}
}
});
}
const data = { message: 'Hello' };
defineReactive(data, 'message', data.message);
基于 Proxy(Vue 3)
Vue 3 使用 Proxy 代理整个对象,可以监听属性的增删和数组变化。
function reactive(obj) {
return new Proxy(obj, {
get(target, key) {
console.log(`读取 ${key}`);
return target[key];
},
set(target, key, value) {
if (target[key] !== value) {
console.log(`设置 ${key} 为 ${value}`);
target[key] = value;
}
return true;
}
});
}
const data = reactive({ message: 'Hello' });
依赖收集与派发更新
Vue 通过 Watcher 和 Dep 实现依赖收集和更新派发:
- Dep:每个响应式属性都有一个 Dep 实例,用于存储所有依赖该属性的 Watcher。
- Watcher:在组件渲染或计算属性时创建,订阅依赖的属性变化。
class Dep {
constructor() {
this.subscribers = new Set();
}
depend() {
if (currentWatcher) {
this.subscribers.add(currentWatcher);
}
}
notify() {
this.subscribers.forEach(watcher => watcher.update());
}
}
let currentWatcher = null;
class Watcher {
constructor(updateFn) {
this.updateFn = updateFn;
this.update();
}
update() {
currentWatcher = this;
this.updateFn();
currentWatcher = null;
}
}
数组的响应式处理
Vue 2 通过重写数组的变异方法(如 push、pop)实现响应式:
const arrayProto = Array.prototype;
const arrayMethods = Object.create(arrayProto);
['push', 'pop', 'shift', 'unshift'].forEach(method => {
const original = arrayProto[method];
arrayMethods[method] = function(...args) {
const result = original.apply(this, args);
console.log(`数组 ${method} 操作`);
return result;
};
});
实际应用中的响应式
在 Vue 组件中,data 选项会被自动转换为响应式对象:
export default {
data() {
return {
count: 0 // 自动变为响应式
};
},
methods: {
increment() {
this.count++; // 触发视图更新
}
}
};
注意事项
- 新增属性:Vue 2 中直接添加新属性无法触发响应,需使用
Vue.set或this.$set。 - 数组索引修改:Vue 2 中直接通过索引修改数组元素需使用
Vue.set。 - 性能考虑:过度使用响应式数据可能影响性能,可通过
Object.freeze冻结不需要响应的数据。







