在 Vue.js 中,响应式系统是其核心特性之一,它使得数据的变化能够自动更新到 DOM 上,实现了数据和视图的双向绑定。下面详细介绍 Vue.js 响应式系统的原理以及它是如何实现数据绑定的。
Vue.js 的响应式系统主要基于 JavaScript 的 Object.defineProperty() 方法(Vue 2.x)和 ES6 的 Proxy 对象(Vue 3.x)来实现。其核心思想是通过拦截数据对象的属性访问和修改操作,当数据发生变化时,自动触发相应的更新操作,从而更新与之绑定的 DOM。
// 模拟Vue的响应式系统
function defineReactive(obj, key, val) {
// 创建一个Dep对象,用于收集依赖
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();
}
}
});
}
// Dep类,用于管理依赖
class Dep {
constructor() {
this.subs = [];
}
addSub(sub) {
this.subs.push(sub);
}
notify() {
this.subs.forEach(sub => sub.update());
}
}
// Watcher类,用于订阅数据变化
class Watcher {
constructor(vm, expOrFn, cb) {
this.vm = vm;
this.cb = cb;
Dep.target = this;
this.getter = parsePath(expOrFn);
this.value = this.get();
Dep.target = null;
}
get() {
return this.getter.call(this.vm, this.vm);
}
update() {
const oldValue = this.value;
this.value = this.get();
this.cb.call(this.vm, this.value, oldValue);
}
}
// 解析路径
function parsePath(path) {
const segments = path.split('.');
return function (obj) {
for (let i = 0; i < segments.length; i++) {
if (!obj) return;
obj = obj[segments[i]];
}
return obj;
};
}
// 使用示例
const data = {
message: 'Hello, Vue!'
};
defineReactive(data, 'message', data.message);
const updateDOM = function (newValue, oldValue) {
console.log(`数据更新:${oldValue} -> ${newValue}`);
};
new Watcher(data, 'message', updateDOM);
// 修改数据
data.message = 'Hello, World!';
// 模拟Vue 3.x的响应式系统
function reactive(target) {
const handler = {
get(target, key) {
// 收集依赖
track(target, key);
return target[key];
},
set(target, key, value) {
target[key] = value;
// 通知更新
trigger(target, key);
return true;
}
};
return new Proxy(target, handler);
}
// 依赖收集
const targetMap = new WeakMap();
function track(target, key) {
if (!Dep.target) return;
let depsMap = targetMap.get(target);
if (!depsMap) {
targetMap.set(target, (depsMap = new Map()));
}
let dep = depsMap.get(key);
if (!dep) {
depsMap.set(key, (dep = new Set()));
}
dep.add(Dep.target);
}
// 发布更新
function trigger(target, key) {
const depsMap = targetMap.get(target);
if (!depsMap) return;
const dep = depsMap.get(key);
if (dep) {
dep.forEach(sub => sub.update());
}
}
// Watcher类,用于订阅数据变化
class Watcher {
constructor(vm, expOrFn, cb) {
this.vm = vm;
this.cb = cb;
Dep.target = this;
this.getter = parsePath(expOrFn);
this.value = this.get();
Dep.target = null;
}
get() {
return this.getter.call(this.vm, this.vm);
}
update() {
const oldValue = this.value;
this.value = this.get();
this.cb.call(this.vm, this.value, oldValue);
}
}
// 解析路径
function parsePath(path) {
const segments = path.split('.');
return function (obj) {
for (let i = 0; i < segments.length; i++) {
if (!obj) return;
obj = obj[segments[i]];
}
return obj;
};
}
// 使用示例
const data = reactive({
message: 'Hello, Vue 3!'
});
const updateDOM = function (newValue, oldValue) {
console.log(`数据更新:${oldValue} -> ${newValue}`);
};
new Watcher(data, 'message', updateDOM);
// 修改数据
data.message = 'Hello, World 3!';
Vue.js 的响应式系统通过Object.defineProperty()(Vue 2.x)或Proxy对象(Vue 3.x)实现了数据劫持,通过依赖收集和发布更新机制实现了数据绑定,使得数据的变化能够自动更新到 DOM 上。