Vue 的双向数据绑定即数据更新视图,视图更新数据。在了解具体过程之前,我们先来定义实现过程中需要的三个类:
- Observer 类:将 Data 对象所有的属性转换成 getter/setter 的形式来追踪变化,技术实现可以使用 Object.defineProperty 或者 Proxy;
- Dep 类:负责收集依赖,删除依赖和发送通知;
- Watcher 类:负责处理依赖(依赖的类型比较多),同时它也是连接视图和数据之前的桥梁。
Vue 双向数据绑定的具体实现过程:
- 先把 Data 通过 Observer 转换成 getter/setter 的形式来追踪变化;
- 在 getter 收集依赖,在 setter 中触发依赖;
- 当外界通过 Watcher 读取数据时,会触发 getter 从而将 Watcher 添加到依赖中;
- 当数据发生变化时,会触发 setter,从而向 Dep 中的依赖发送通知;
- Watcher 接收到通知之后,会向外界发送通知,变化通知到外界后可能会触发视图更新,也有可能触发用户的某个回调函数。
所谓的依赖,就是使用数据的地方。提前收集使用数据的依赖,等到数据改变的时候,再将收集好的依赖循环触发一遍就好。
Oberver 类的简单实现
// Oberver类,负责将对象属性转换为getter和setter形式
class Oberver {
constructor(data) {
this.data = data
if (!Array.isArray(data)) {
// 跳过数组的检测
this.walk(data)
}
}
walk(obj) {
const keys = Object.keys(obj)
for (let i = 0; i < keys.length; i++) {
defineReactive(obj, keys[i]; obj[keys[i]])
}
}
}
function defineReactive(data, key, value) {
// 递归子属性
if (value && typeof value === 'object') {
new Oberver(value)
}
let dep = new Dep()
Object.defineProperty(data, key, {
configurable: true,
enumerable: true,
get() {
dep.depend() // 收集依赖
return value
},
set(newVal) {
if (value === newVal) {
return
}
val = newVal
dep.notify() // 发送通知
}
})
}
Dep 类的简单实现
// Dep类负责新增依赖,删除依赖和发送通知
class Dep {
constructor() {
this.subs = []
}
addSub(sub) {
this.subs.push(sub)
}
// 新增依赖
depend() {
if (window.target) {
this.addSub(window.target)
}
}
// 发送通知
notify() {
this.subs.forEach(function(sub) {
sub.update();
});
}
// 删除依赖
remove(arr, item) {
if (arr.length) {
const index = arr.indexOf(item)
if (index > -1) {
return arr.splice(index, 1)
}
}
}
}
Watcher 类的简单实现
class Watcher {
constructor(vm, exp, cb) {
this.vm = vm
this.exp = exp
this.cb = cb
this.value = this.get() // 将自己添加到订阅器的操作
}
get() {
window.target = this // 缓存自己
var value = this.vm.data[this.exp] // 强制执行监听器里的get函数
window.target = null // 释放自己
return value
}
update() {
var value = this.vm.data[this.exp]
var oldVal = this.value
if (value !== oldVal) {
this.value = value
this.cb.call(this.vm, value, oldVal)
}
}
}