简介:本文将深入探讨Vue.js的响应式原理,通过解析给定的代码,我们将学习到Vue.js是如何利用观察者模式和依赖收集实现数据的双向绑定的。
话不多说,上代码!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
let depId = 0
let watcherId = 0
const vm = {
_data: {
msg: "哈哈哈哈哈哈哈",
}
};
function render() {
document.body.innerHTML = vm.msg
}
function defineReactive(target, key, val) {
const dep = new Dep();
Object.defineProperty(target, key, {
get() {
if (Dep.target) {
dep.depend(Dep.target);
}
return vm._data[key];
},
set(newVal) {
vm._data[key] = newVal;
dep.notify();
},
});
}
class Dep {
constructor() {
this.subs = [];
this.id = depId++
}
depend() {
Dep.target.addDep(this)
}
notify() {
this.subs.forEach((watcher) => watcher.update());
}
addWatcher(watcher) {
this.subs.push(watcher)
}
}
Dep.target = null;
class Watcher {
constructor(vm, effectFn) {
this.getter = effectFn;
this.vm = vm;
this.id = watcherId++
this.depsId = new Set()
this.deps = []
this.get();
}
get() {
Dep.target = this;
this.getter();
Dep.target = null;
}
update() {
this.get();
console.log("值改变了-触发了更新");
}
addDep(dep) {
if (!this.depsId.has(dep.id)) {
this.depsId.add(dep.id)
this.deps.push(dep)
dep.addWatcher(this)
}
}
}
defineReactive(vm, "msg");
new Watcher(vm, render);
setTimeout(() => {
vm.msg = "嘿嘿嘿嘿嘿嘿嘿嘿";
}, 3000)
</script>
</body>
</html>
1. 响应式的概念和目标
在Vue.js中,响应式是指当数据发生变化时,相关的界面会自动更新,以保持数据和界面的同步。这种响应式的特性让开发者能够以声明式的方式编写代码,而无需手动处理DOM的更新。
2. 观察者模式
观察者模式是一种常见的设计模式,用于在对象之间建立一种一对多的依赖关系。在给定的代码中,我们可以看到以下几个关键角色:
- Dep(依赖):它是一个观察者模式中的主题,负责管理所有的观察者(Watcher)对象,并在数据发生变化时通知它们。
- Watcher(观察者):它是一个观察者模式中的观察者,负责订阅(依赖)数据的变化,并在变化发生时执行相应的更新操作。
- defineReactive():这个函数用于定义数据的响应式,它利用了Object.defineProperty()方法来劫持数据的读取和修改操作。
3. defineReactive函数
defineReactive函数是实现数据响应式的核心。它接收三个参数:目标对象(target)、属性名(key)和初始值(val)。在函数内部,它创建了一个Dep实例,用于管理与该属性相关的所有观察者。
通过Object.defineProperty()方法,我们定义了属性的getter和setter函数。getter函数用于收集依赖(即将观察者添加到依赖中),而setter函数在属性值发生变化时,更新该属性的值,并通知所有依赖进行更新。
4. Watcher类
Watcher类表示一个观察者对象,它负责订阅依赖的变化,并在变化发生时执行相应的更新操作。在构造函数中,它接收两个参数:要观察的Vue实例(vm)和一个用于更新界面的回调函数(effectFn)。
Watcher对象在构造时会调用自身的get()方法,这个方法的作用是将当前Watcher实例赋值给Dep.target,并立即调用回调函数。这样做的目的是触发响应式数据的读取操作,从而将Watcher添加到对应的依赖中。
当数据发生变化时,Dep会调用每个依赖(Watcher)的update()方法,而update()方法内部又会调用Watcher的get()方法。这样,Watcher会重新执行一次回调函数,从而实现界面的更新。
5. 依赖收集
依赖收集是指在读取响应式数据时,收集当前正在读取数据的Watcher对象。这样做的目的是建立数据与Watcher之间的关系,当数据发生变化时,能够通知到相应的Watcher进行更新。
在给定的代码中,当定义一个响应式属性时,会为该属性创建一个Dep实例,并在getter函数中通过Dep.target判断是否存在当前的Watcher对象。如果存在,则将当前Watcher添加到Dep的依赖列表中。
而在Watcher的构造函数中,它在调用get()方法时会将自身赋值给Dep.target,这样在读取响应式数据时,Dep就能够收集到该Watcher对象。
6. 总结
Vue.js利用了观察者模式和依赖收集来实现数据的双向绑定。当数据发生变化时,依赖收集会通知相关的观察者(Watcher)进行更新,而观察者则负责执行相应的更新操作,以保持数据和界面的同步。