《深入浅出Vue.js》Object变化侦测读书笔记
《深入浅出Vue.js》的作者是参考2.5.2版本进行撰写的,因此我下载相同版本进行学习2.5.2版本下载
1.基本概念
Vue.js的特性?
响应式系统...
响应式系统是什么?
响应式系统是通过改变数据模型实现视图的更新。它的核心是变化侦测。
变化侦测作用是什么?
侦测到数据的变化,通知视图进行更新和渲染。
渲染是什么?
通过状态生成DOM,输出到页面显示,这个过程称为渲染。在运行时状态会不断变化,需要不停的重新渲染。
2.Object变化侦测的原理
Vue.js变化侦测的原理是什么?
一个状态绑定多个依赖,当状态发生变化时,通知绑定的依赖更新。
针对上一句,我们找大象、冰箱和动作?
状态、依赖、状态绑定依赖、状态发生变化、通知绑定的依赖、依赖更新
状态是什么?
数据data。
依赖是什么?
依赖是使用数据的地方,比如模板、watch等。
特别注意,因为依赖有很多类型,我们可以在数据和依赖中间加一个观察者角色Watcher。它的作用就像商家和顾客中间的快递员。商家要发货给顾客,因为顾客很多,商家把货物丢给快递员就行,然后快递员自己看着办。
因为多加了一个观察者,我们优化一下大象、冰箱和动作?
data、Watcher、依赖、data绑定Watcher、data发生变化、通知绑定的Watcher、Watcher更新依赖
data怎么绑定Watcher?
第一步找到存储Watcher的容器。第二步把Watcher放进去。
收集Watcher的容器是什么?
我们使用数组进行收集,给数组取名为subs。
Watcher怎么放进去?
我们使用Object.defineProperty,重写属性的get方法,在getter中收集依赖,只要有地方获取值触发getter,就可以自动收集依赖。每个属性配备一个subs。
当数据发生变化时,怎么通知依赖更新?
同样我们使用Object.defineProperty,重写属性的set方法, 当setter被触发时,我们循环该状态的subs,通知里面的每一个Watcher,由Watcher通知依赖更新。
怎么侦测到数据的所有属性?
Observe类,递归侦测所有属性。
能整一个流程图吗?
2.4 Object的变化侦测demo
运行结果
执行
let observer = new Observer(data)
console.log("侦测对象data:", observer)
执行
new Watcher(data, 'person.name', function (val, newVal) {
console.log("Watcher回调")
})
当触发person.name的getter函数,我们会发现Dep的subs中新增了一个Watcher对象,而Watcher对象中有depIds和deps记录自己会被哪些Dep通知。
由于获取person.name值时,会触发person的getter函数和name的getter函数,因此我们会看到控制台打印了两条数据,两个dep的id不同,subs里面存储了同一个Watcher对象,Watcher对象的deps里记录它可以被id为0和id为1的对象通知到。
执行
data.person.name = {
firstName: 'hongan',
lastName: 'Zhang',
newName:'111'
}
修改name的值,id为1的dep存储的Watcher被通知
《深入浅出Vue.js》Array变化侦测读书笔记
3.Array变化侦测原理
Array变化侦测与Object变化侦测的异同?
两者基本相同,都在getter中收集依赖。区别在于Object是setter中触发依赖,而Array是在拦截器中触发依赖。
为什么在拦截器中触发依赖?
只有给数组重新赋值的时候,才会触发setter。(代码参考) 修改数组内容时,我们一般使用数组的原型方法,比如push,pop,splice等。因此我们可以用一个拦截器覆盖Array.property。
拦截器怎么覆盖Array.property?(代码参考)
第一步创建变量arrayMethods继承Array.prototype
第二步arrayMethods上使用 Object.defineProperty改变数组自身方法
第三步将拦截器arrayMethods赋值给value.proto
拦截器触发依赖,那依赖的保存位置dep必须是拦截器能访问到的,怎么做?
在Object侦测demo中,我们发现value身上会有一个ob属性,ob的值就是当前Observer的实例,ob可以避免重复创建Observe实例。
拦截器是原型方法,可以直接通过this.ob访问Observe实例。
我们只需要把数组的dep保存到Observer实例上,在getter和拦截器中我们就可以访问到。
3.1Array变化侦测Demo
运行结果
执行
let observer = new Observer(data)
console.log("侦测对象data:", observer)
我们发现dependArray(value)这段代码对数组里的元素也变成响应式的。
执行
new Watcher(data, 'person.hobbies', function (val, newVal) {
console.log("Watcher回调")
})
执行
data.person.hobbies.push('1')
从上面两张图中我们发现,Array的依赖存放在Observer中,在拦截器中触发的时候通知的依赖是存在Observer对象上的dep 。
3.2 注意点
Array的变化侦测是通过拦截器的方式实现的。因此this.arr[0]=2这种直接修改数组的值是无法侦测到的。清空数组操作this.arr.length=0这种方式也是无法侦测到数组变化的。
《深入浅出Vue.js》变化侦测相关API读书笔记
4变化侦测相关API
4.1 vm.$set
它的出现是有原因的
Vue.js通过Object.defineProperty来将对象的key转换成getter/setter的形式追踪变化,但是只能追踪到数据的修改,无法追踪到数据新增和删除。
官网API截图
vm.$set相关代码查看
4.2 vm.$delete
官网API截图
vm.$delete相关代码查看
4.3 vm.$watch
官网API截图
vm.$watch的options的两个参数
参数immediate代码参考 参数deep代码参考
5.变化侦测总结
5.1数组和对象的侦测变化
通过Observer递归侦测数据变化。对象递归使用Object.defineProperty的getter方法收集依赖,使用setter方法侦测通知依赖。数组同样使用Object.defineProperty的getter方法收集依赖,使用拦截器侦测通知依赖。
5.2依赖存放位置
数组的依赖存放在Observer中,使用拦截器侦测通知取的也是Obserser实例中取的dep。
5.3 vm.$set
vm.$set的target是数组,使用拦截器方法,会自动通知。如果target是对象,使用Object.defineProperty,再手动通知。
5.4 vm.$delete
vm.$delete的target是数组,使用拦截器方法,会自动通知。如果target是对象,使用delete,再手动通知。
5.4 vm.$watch
options里面有两个参数,immediate立即执行和deep监听所有子值变化。