《深入浅出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类,递归侦测所有属性。

  • 能整一个流程图吗?

dfd32b98f29bfcc195c5d1ba8e1e438c.png

2.4 Object的变化侦测demo

  • 运行结果 b4aab2de234c21a407d728f689d31bb3.png

执行

let observer = new Observer(data)

console.log("侦测对象data:", observer)

1a10699cb3bedfb5de6044cda72327d5.png

执行

new Watcher(data, 'person.name', function (val, newVal) {

console.log("Watcher回调")

})

565f921efd9d537f9a92d297a0af83ff.png

当触发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'

}

e49c58b175ff4ea84fda987e950cc642.png

修改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

  • 运行结果

53a92411148d8d45e618d810a611399a.png

执行

let observer = new Observer(data)

console.log("侦测对象data:", observer)

09a012e9edcab79f8fbb77922c2379c1.png

我们发现dependArray(value)这段代码对数组里的元素也变成响应式的。

执行

new Watcher(data, 'person.hobbies', function (val, newVal) {

console.log("Watcher回调")

})

36e8a62137f511257fb0f2b48944584e.png

执行

data.person.hobbies.push('1')

feed185e3bd9774337f224efe20c4dca.png

从上面两张图中我们发现,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截图 194b08ac277ed998aac5daf26db02a60.png

  • vm.$set相关代码查看

4.2 vm.$delete

  • 官网API截图 e81f48f088378085398e48b20585ca9b.png

  • vm.$delete相关代码查看

4.3 vm.$watch

  • 官网API截图 53b4cb7e147b489b3cc032c97a72a726.png

  • 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监听所有子值变化。

Logo

前往低代码交流专区

更多推荐