Vue 的 三种 watcher

Souleigh ✨
• 阅读 1120

user-watcher

在页面中使用的watcher,即用户定义的watcher,用于观察一个属性的更新,支持数组定义多个,对象定义单个的形式,在initWatcher中进行watcher的初始化之后,在渲染函数进行数据的读取,触发依赖收集时会将user-watcher的依赖收集进去,data属性set更新时会被触发user-watcher所定义的回调函数(将新旧值传入),支持异步操作
可选项:immediate deep sync

  • immediate 为true时会将实例作为参数传入立即执行回调函数
  • deep 对对象、数组进行深度的依赖收集
  • sync 不把更新watcher放到nextTick队列 而是立即执行更新

render-watcher

每一个组件都有一个render-watcherdata/computed 中的有被依赖的属性改变的时候会触发render-watcher的更新,表达式为

  function(){
     vm._update(vm.render(), hydrating)
  }
  • 使用:在组件嵌套中,props触发的关联更新等,如父组件会将子组件的watcher收集用于视图更新的触发
  • 初始化:渲染函数的watchermounted之后,实例化初始化染函数观察者并触发对render-wather的收集,渲染函数也是一个对象,初始化过程,生成render-tree,获取结果时收集render-watcher依赖,render-tree更新也就是需要重新渲染视图
  • 防止重新收集this.depIds.has(dep.id)来避免重复依赖收集,因在渲染函数中,一个属性被引用多次是常见的
  • 更新data更新触发依赖,同时会触发user-watcher对回调函数完成新旧数据的派发,最后触发render-watcher更新视图,所有的watcher默认都是在nextTick队列中进行异步更新的,当所有的突变完成之后,一次性的执行队列中所有观察者的更新方法,同时清空队列,这样 多次更新同一个watcher只会更新一次,而频繁的更新可以整合为一次

computed-watcher

  • 使用:在initState阶段被初始化,具有缓存性质的惰性求值观察者,只有存在依赖性数据并且该数据更新了,computed值才会更新,否则就取缓存的值,依赖数据是data中的数据
    初始化computed 计算属性在mounted之后生成,实例化computed-watcher 初始化computed dirty属性为true,并不调用get方法读取属性
  • 计算:当渲染函数访问到computed属性,进行求值,并将dirty设置为false标识计算过,同时对依赖的属性读取时将computed-watcher当做依赖收集,并且订阅render-watcher,所以在内部数据触发set时会先计算后对比新旧值,有更新则重新渲染视图
  • 更新:内部依赖被触发后,设置dirtytrue,通过判断this.dep.subs.length 有没有订阅者,如果有则进行取值的计算(计算后将dirty置为false),否则等待该值被引用才进行计算

什么时候触发了依赖的收集

  • 准备:在createdbeforeMounted之间生成了ASTrender可执行函数,用到了compilecreateCompiler生成对象结构的AST语法树,再通过generator调用了之前初始化的方法处理各种vue内置的方法&指令,如v-for v-if等,通过createElementcreateTextVnode等生成虚拟DOM树,作为updateComponent函数返回

  • 渲染:在beforeMountedmounted之间通过将执行这个函数,这个函数会将虚拟DOM渲染成真正的DOM,内含有patch的布丁算法,在执行过程中会触发渲染函数,这时会触发数据属性的get函数,而这个过程的观察者就是渲染函数,在渲染过程中添加数据的订阅者,watcher订阅者是ObserverCompiler之间的桥梁,将在自身实例化的时候将自己添加到dep当中,watcher自身有一个dependupdate方法,待属性变动dep.notice通知调用自身的update方法触发Compile中绑定的回调

总结

这三种 watcher 执行顺序为 computed watcher => user watcher => render watcher,这样做 尽可能的保证了视图更新时数据是最新的
收集依赖是在render tree渲染时读取render tree的结构,触发依赖的收集,每一次都只会对一个观察者进行操作,所以一个时间点只有一个Dep.tatget,说明这个观察者是依赖于当前的数据,就会把这个观察者添加到该数据的subs里面,会通过id来防止重复添加,同时会将依赖添加到自身的deps中以便通过set调用 dep.notify()

点赞
收藏
评论区
推荐文章
秃头王路飞 秃头王路飞
6个月前
webpack5手撸vue2脚手架
webpack5手撸vue相信工作个12年的小伙伴们在面试的时候多多少少怕被问到关于webpack方面的知识,本菜鸟最近闲来无事,就尝试了手撸了下vue2的脚手架,第一次发帖实在是没有经验,望海涵。languageJavaScript"name":"vuecliversion2","version":"1.0.0","desc
blmius blmius
1年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
1年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
刚刚好 刚刚好
6个月前
css问题
1、在IOS中图片不显示(给图片加了圆角或者img没有父级)<div<imgsrc""/</divdiv{width:20px;height:20px;borderradius:20px;overflow:h
小森森 小森森
3天前
租房类微信小程序-基于微信云开发-小程序端集成了管理员后台-一键部署,快速发布
温馨提醒本项目使用MITLicense协议,仅适用于学习交流,并且不提供无偿的、不提供无偿的、不提供无偿的维护修改服务(但可提issue)。若直接将本项目用于商用,因本项目带来的所有后果由使用者自行承担。如需商用升级版,请联系我微信,微信二维码在本博客页面右上角在此奉劝某些人,请尊重作者的劳动成果,做人积点德吧!最近发现有人拿我的源码进行二次分
小森森 小森森
3天前
计划助手V1.0-微信小程序(QQ小程序)-源代码分享
疫情期间在家感觉好无聊啊,于是利用空闲时间做了一个用来记录和管理小目标时间的小程序,命名为《小沙漏》。QQ版本小程序同步上线,QQ小程序叫《时间小沙漏》,欢迎大家前来体验,后期也会添加其他的新功能哦【区别】:微信小程序的代码与QQ小程序的源码是不一样的。微信小程序的源码基于微信小程序云开发,需要在有网络的情况下使用,具有同步功能,所有记录在删除小
小森森 小森森
6个月前
校园表白墙微信小程序V1.0 SayLove -基于微信云开发-一键快速搭建,开箱即用
后续会继续更新,敬请期待2.0全新版本欢迎添加左边的微信一起探讨!项目地址:(https://www.aliyun.com/activity/daily/bestoffer?userCodesskuuw5n)\2.Bug修复更新日历2.情侣脸功能大家不要使用了,现在阿里云的接口已经要收费了(土豪请随意),\\和注意
Stella981 Stella981
1年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Wesley13 Wesley13
1年前
MySQL查询按照指定规则排序
1.按照指定(单个)字段排序selectfromtable_nameorderiddesc;2.按照指定(多个)字段排序selectfromtable_nameorderiddesc,statusdesc;3.按照指定字段和规则排序selec
Wesley13 Wesley13
1年前
PHP中的NOW()函数
是否有一个PHP函数以与MySQL函数NOW()相同的格式返回日期和时间?我知道如何使用date()做到这一点,但是我问是否有一个仅用于此的函数。例如,返回:2009120100:00:001楼使用此功能:functiongetDatetimeNow(){
helloworld_34035044 helloworld_34035044
8个月前
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为