前面三篇文章都分析了响应式原理的实现,本篇会简单总结一下, 之后一起看一个有意思的例子
- Observer 建立数据劫持
- Dep 收集依赖,通知更新
- Watcher 提供update 接口,触发render 的调用,render的调用过程中一步一步的收集依赖,建立 Dep 和 Watcher的依赖关系
关系图
任何依赖的收集过程都凭借对应的更新函数:
render watcher 和 所依赖的 data 靠 render 函数的执行
computed watcher 和 所依赖的 data 靠 computed handler 的执行
user watcher 和所依赖的data 靠 watch handler 的执行
这里有个特殊的数组类型的数据,特殊点在于通过重写Array原型上的7个方法进行处理,但归根结底就是遍历数组元素,对每个元素走observe 的逻辑。 。
pop, push, shift, unshift, reverse, sort, splice
看个🌰
<template>
<section>
<div v-for="item in list" :key="item.id">
{{ item.name }} : {{ item.value}}
</div>
<div v-for="item in list2" :key="item">
{{ item }}
</div>
<button @click="handleChangeListData" name="button">changeListData </button>
</section>
</template>
<script>
export default {
name: 'demo',
data() {
return {
list: [
{
id: 1,
name: 'first',
value: 1,
},
{
id: 2,
name: 'second',
value: 2,
},
{
id: 3,
name: 'third',
value: 3,
}
],
list2: [1,2,3,4]
}
},
methods: {
handleChangeListData() {
// this.list[2].name = 'aaaa'
this.list2[2] = 'aaaa'
},
}
}
</script>
点击按钮,页面并未发生任何变化。
换种方式,如果我们放开handleChangeListData方法内的第一句的注释看下效果:
显然页面更新了,什么原理呢,list 类型为 Array<obj>, 收集list依赖时,遍历list, 对于 object 类型的数据,在收集依赖的时候,去建立了render wacther 和 objDep 的关系,所以我直接调用list[2].name = 'aaaa',可以触发对应dep 的notify, 最终造成render 函数的重新渲染,也就解释了为什么list2 的数据也变成了最新的,list2[2] 虽然未触发render,但list[2].name 'aaaa' 触发了,就是被捎带着执行了。