Vue 的 三种 watcher

Souleigh ✨
• 阅读 1454

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()

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
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
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Stella981 Stella981
3年前
JS 对象数组Array 根据对象object key的值排序sort,很风骚哦
有个js对象数组varary\{id:1,name:"b"},{id:2,name:"b"}\需求是根据name或者id的值来排序,这里有个风骚的函数函数定义:function keysrt(key,desc) {  return function(a,b){    return desc ? ~~(ak
Stella981 Stella981
3年前
HIVE 时间操作函数
日期函数UNIX时间戳转日期函数: from\_unixtime语法:   from\_unixtime(bigint unixtime\, string format\)返回值: string说明: 转化UNIX时间戳(从19700101 00:00:00 UTC到指定时间的秒数)到当前时区的时间格式举例:hive   selec
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
10个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这