vue源码解析pdf_从源码中学Vue(五 ) 解析「观察者」Watcher做了什么?

欢迎来到我的《从源码中学Vue》专题系列文章,更多精彩内容持续更新中,欢迎关注 :)

00e2a3f0a2ab3fa932dd726b38ec23f8.png

上一章节我们说到了在Vue中的数据劫持(Observer)的基本原理是通过ES5的API Object.defineProperty来实现的。

本章目标

  • Vue初始化的时候,为什么会主动渲染模板数据
  • Vue的发布订阅模式

单纯的只是实现数据劫持是远远不够的,我们需要在数据发生变化的时候需要去更新视图(html),那么Vue中是通过Watcher类就是用来观察数据的变化并且去通知patch去更新dom

Vue初始化的时候,为什么会主动渲染模板数据

通过Observer类我们知道,我们只有访问或者修改data上的属性的时候,才会触发get和set方法,这时候才会触发dom更新,那么当我们第一次new Vue实例的时候,我们并没有对data数据进行任何的访问和获取,那html上的模板数据第一次是如何渲染出来的呢?

这时候我们就不得不提到Watcher类了。

在Watcher类中,有一个很关键的方法,get(),这个方法在Watcher类的构造方法中被调用

this.value = this.lazy? undefined : this.get()

那么在什么情况下会调用get方法呢,通过我前面的文章Vue生命周期详解 中知道,在mounted之前,会实例化一个Watcher

28ca959160bb823a2a6e927ed9e2f37f.png

可以看出来我们在实例化Watcher的时候,第4个参数我们没有传入lazy值,所以这个时候回过头看

this.value = this.lazy? undefined : this.get()

this.get方法会被调用。

再来看下get方法的源码

eec395c194881c3c76053ba32f27fb96.png

它调用了this.getter方法,而

9380f2a84dd8a1a8e3f9e99028deb692.png

这个this.getter方法,实际就是我们传入的第二个参数expOrFn

Watcher的第二个参数为expOrFn它可以是一个string 或者function

e4ddb804e50fd8970399cb2a1905c4f1.png

我们实例化的时候传入的实参是updateComponent,它是一个function类型

717e2b09e5e55790c286caeab3d40405.png

所以最终是调用的updateComponent()方法,这个方法里面再调用了。vm.__update()方法。

我们再在当前的文件下找到了_update方法的定义

faf349509a32b7a2bb3b683816246275.png

挂载在Vue原型下的方法,可以看出来我圈出来的两行代码。都会触发一个叫vm.__patch__的方法

patch英文的意思就是补丁。它也是虚拟dom的核心方法。内部做了模板的解析。

到了这一步,我们实际上已经找了虚拟dom算法的位置了。这块还是比较复杂的,小编我还没来得及阅读这块的源码,这里就不再深入了。

Vue的发布订阅模式

我们现在知道了了Vue是通过Observer类去劫持数据,Watcher类实现数据的监听并通知更新dom,其实在Vue中还有一个很重要的概念,那就是发布订阅模式(Dep类)

Dep类是连接Watcher和Observer的桥梁。具体我们通过源码去看下它们三者之间的关系

8f0ace6f553c8b851f0b7e7244194c03.png

它里面有一个subs数组,这个数据就是用来收集watcher,再来看下notify方法,可以看到它最终循环了subs中所有的watcher对象然后依次调用了它们的update方法。

我们也同样很容易想到这个notify通知所有的Watcher方法是在当数据发生变化的时候去调用的,我们找下源码

3d892aa71a7855db5435bba07fc7fd75.png

再回过头我们去看下watcher的update方法。

cd8194dde8e2d3d04043ab5f19354655.png

先来看run 方法

17748963e1a807c26474a6341043409e.png

最终调用的是cb方法,cb顾名思义,它是callback,回调方法。也就是说,在我们初始化Watcher的时候,并不会去主动调用这个cb方法,而是当数据发生变化的时候触发了set,然后再去调用每一个watcher对应的cb回调。

最后再来看下在Dep类下有一个静态属性target

6c519d7b168bca5fbc19f5540e74d2da.png

在watcher类中调用get方法的时候,先调用了pushTarget(this)

b9910a04ae54cc88794292a13138c1b5.png

这个this就是指向的我们当前的Watcher实例。

为什么给Dep加一个静态属性呢?

利用这个中间变量, 缓存已有的target, 在 pushTarget 函数, 使用传入的target 代替 Dep.target; 然后使用 Dep.target

最后使用 popTarget 还原 ; 主要是为了Observe中的get方法, 判断当前是否有watcher(Dep.target), 如果有就在dep增加这个属性的依赖, Dep.target.depend( dep1 )

比如 methods 的每一个方法可以是 一个 watcher, 这个wactcher可能会依赖多个 data里面的属性

这里真的很绕 ^_^

总结:

  • Dep类是用来连接Watcher和Observer的桥梁。当数据发生变化的时候,通知Watcher去更新
  • 我们在第一次实例化Watcher的时候,会去调用__patch__方法去更新模板上的视图。

这里是畅哥聊技术 《从源码中学Vue》系列文章,更多精彩内容持续更新中,敬请期待。

未完待续。。。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值