本章要实现依赖收集
0.先看看下面案例,理一下思路
const app = new HVue({
template: `
<div>
<span>{{msg1}}<span>
<span>{{msg2}}<span>
<span>{{msg3}}<span>
</div>
`,
data: {
msg1: 'msg1',
msg2: 'msg2',
msg3: 'msg3'
},
created() {
this.msg1 = 'new msg1'
this.msg3 = 'new msg3'
}
})
msg1被修改,视图有两处要更新;
msg2被修改,视图更新;
msg3被修改,视图不更新。
如何实现呢,需要扫描视图收集依赖,知道视图中哪些地方对数据有依赖,这样当数据变化时就能触发相应视图更新了。
1、每一个属性对应一个dep,每个dep可以对应多个watcher,创建Dep 、Watcher用于依赖收集
// hvue.js
class HVue {
}
class Dep {
constructor() {
this.deps = [] // 每一属性都对应一个dep,每个dep可以对应多个watcher,所以用一个数组存储相应的watcher
}
addDep(dep) {
this.deps.push(dep)
}
notify() { // 通知更新视图
this.deps.forEach(dep => dep.update())
}
}
class Watcher {
construcotr() {
Dep.target = this // 每新建一个watcher实例时,会将Dep的静态属性指向新的实例
}
update() {
console.log('属性更新了');
}
}
2、每个性都对应一个dep,属性响应化时都调用defineReactive方法添加,所以在该方法里为每个属性都创建一个dep实例。
// hvue.js
class HVue {
defineReactive(obj, key, val) {
const dep = new Dep() // 为每个属性都创建一个dep实例
Object.defineProperty(obj, key, {
get() {
Dep.target && dep.addDep(Dep.target) // 当我们创建一个weatcher实例时,如果一读某个属性时我们就把这个依赖添加到deps数组里,这样将来数据更新时,就可以通知去通知谁了
return val;
},
set(newVal) {
if (newVal !== val) {
val = newVal
// console.log(`${key}更新了,新值是:${val}`);
dep.notify() // 通知更新
}
}
})
this.observe(val) // data对中的每个属性值也可能是对象,通过递归实现响应化
}
}
现在可以测试一下代码
// hvue.js
class HVue {
constructor(options) {
this.$options = options
this.$data = options.data
this.observe(this.$data) // 实现$data响应式
new Watcher()
this.$data.message
new Watcher()
this.$data.foo.bar
}
}
看到这日志说明代码生效了