data属性的响应式
遍历data对象,首先给data的每个属性new一个Dep;再通过 Object.defineProperty() 方法给data的每一个属性添加 get 和 set 方法
get: 依赖/观察者(Watcher) 收集 ; 使用data属性,会执行get()方法
set: 通知 依赖/观察者(Watcher) 执行; 修改data属性的值,会执行set()方法
data属性的依赖收集器(Dep)
data的每一个属性对应一个Dep
一个Dep是一个Watcher数组
有三种类型的Watcher
- render watcher 每个属性只对应一个
- computed watcher 每个属性可对应多个
- watch watcher 每个属性可对应多个
每个watcher中有一个回调函数
当属性值更改的时候,会通知Dep,Dep会通知这些Wacther执行回调函数。
<body>
<div id="app">
{{ name }}
{{info}}
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data() {
return {
name: '林三心'
}
},
computed: {
info () {
return this.name
}
},
watch: {
name(newVal) {
console.log(newVal)
}
},
methods:{
init: function(){
this.name = '123'
}
},
mounted(){
console.log(this)
this.init()
}
})
app.mount()
</script>
</body>
上面例子中的name对应的Dep如下:
Dep下有3个Watcher;
每个Watcher里面还有Dep
Watcher为何要反过来收集Dep?
dep是name的管家,他的职责是:name更新时,dep会带着主人的命令去通知subs里的Watcher去做该做的事,那么,dep收集Watcher很合理。那为什么Watcher也需要反过来收集dep呢?这是因为computed属性里面的变量没有自己的dep。
如何html里不依赖name这个变量,那么无论name再怎么变,他都不会主动去刷新视图,因为html没引用他(说专业点就是:name的dep里没有渲染Watcher),注意,这里说的是不会主动,但并不代表他不会被动去更新。什么情况下他会被动去更新呢?那就是computed有依赖他的属性变量。
computed属性里面的变量info依赖于name, 但是info是没有自己的dep的(因为他是computed属性变量),而name是有dep的。如何html里面只有info的引用没有name的引用,那么name一改变,按理说虽然info跟着变了,但是html不会重新渲染,因为name虽然有dep,有更新视图的能力,但是奈何人家html不引用他啊!info想要自己去更新视图,但是他却没有这个能力啊,毕竟他没有dep这个管家!这个时候name的computed Watcher里面的dep就派上用场了,可以借助这些dep去更新视图,达到更新html里的info的效果。
如何将render Watcher添加到data属性的Dep中
- mountComponent时, 首先new 一个render Watcher;
- 将该Watcher实例赋值给 Dep.target
- 执行该Watcher的回调函数,即执行 vm._update(vm._render(), hydrating);
- 执行render函数
- 执行render函数时,遇到data的属性,则调用data属性的 get 方法
- 在get方法中,会判断Dep.target(即render Watcher 实例)是否存在
- 存在,则将该Watcher实例添加到当前属性对应的Dep中。添加时会去重。