思维导图
前言
上一篇文章里面我们已经做完了单个根组件数据到页面的渲染过程。Vue 源码初探(三)单组件挂载(渲染) 。
现在我们来做一件事情,更改属性之后手动调用_update()函数让页面触发更新。我们发现页面也进行了更新。
<body>
<div id="app">
<li>{{name}}</li>
<li>{{age}}</li>
</div>
<script src="./vue.js"></script>
<!-- <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14"></script> -->
<!-- <script src="./test.js"></script> -->
<script>
var vm = new Vue({
el: '#app',
data: {
name: '小明',
age: 20
}
})
vm.name = '天明'
vm._update(vm._render())
</script>
</body>
正文
依赖收集与触发更新
function defineReactive(data, key, value){
//如果当前对象的key 再是一个对象就进行深层监测
//给每个属性都添加一个dep
let dep = new Dep()
observe(value)
Object.defineProperty(data, key,{
get() {
if(Dep.target){ //dep.target 现在保存着watcher
dep.depend()
}
return value
},
set(newValue) {
//如果新值和老值相等 就不做处理
if(value === newValue) return
//如果设置的是一个对象的话,要再次对这个新的对象进行劫持。
observe(newValue)
value = newValue
//告诉所有的watcher可以更新了
dep.notify()
}
})
}
每个属性的dep
let id = 0;
class Dep{
constructor() { //要把watcher放到dep中
this.subs = []
this.id = id++
}
depend() {
Dep.target.addDep(this) //让watcher记住dep
}
addSub(watcher){ //让dep记住watcher
this.subs.push(watcher)
}
notify(){
this.subs.forEach((watcher)=>{
watcher.update()
})
}
}
//做标识,用来存放watcher
Dep.target = null //相当于创建了一个全局变量,静态属性
export default Dep
每个视图的watcher
import Dep from "./dep"
let id = 0
class Watcher {
constructor(vm, fn, cb, options) {
this.vm = vm;
this.fn = fn;
this.cb = cb;
this.options = options;
this.id = id++;
this.depsId = new Set()
this.deps = [] //存放dep
this.getter = fn; //fn 就是外面的渲染逻辑
this.get(); //第一次做组件渲染
}
addDep(dep) {
let did = dep.id
if(!this.depsId.has(did)){
this.depsId.add(did)
this.deps.push(dep)
dep.addSub(this) //让dep记住watcher
}
}
get() {
//执行渲染逻辑
Dep.target = this;
this.getter(); //执行渲染逻辑的时候会走render函数,会触发到数据响应式里面的get函数
Dep.target = null;
}
update() {
//会多次更新
console.log('update')
this.get()
}
}
export default Watcher
dep和watcher的关系图
总结
- vue里面用到了观察者模式,默认组件渲染的时候会创建一个watcher,(并渲染视图)。
- 当渲染视图的时候,通过render函数的时候,就会走响应式里面的get方法,这时让这个属性的dep记录当前的这个watcher。
- 同时也让watcher记住这个dep(现在还没有用到)dep和watcher是多对多的关系,因为一个属性可能对应多个视图,一个视图对应多个数据。
- 如果数据发生变化,会通知对应属性的dep,依次通知存放的watcher去更新页面。
后续写当页面中多次修改同一个属性值的时候需要去重操作。nextTick()