watcher与dep的关系程序运行分析

初始化执行 _init => initState() + vm.$mounted

initState => initData + initWatch + initComputed

  1. initData 中执行observer(),进行响应式处理
  • 创建Observer类 => Observer类中对data进行遍历defineReactive,修改get和set函数
  • defineReactive函数中创建私有变量dep(new Dep),每一个get和set对应一个dep
  • get中判断Dep类的静态属性target有值,当前watcher的deps添加当前dep,当前dep的subs添加当前watcher;并返回value;
  • set中赋值后执行dep.notify函数,执行dep的subs中的watcher循环update(执行queueWatcher=>flushWatcher=>延迟队列queue中watcher.run=>执行再次更新);
  1. initWatch
  • 截取watch的key属性和监听需要处理的handler函数
  • 对每一个key创建一个watcher,watcher函数中如果有user则获取到值后再次执行handler函数,实现监听功能
//            vm key handler
new Watcher(vm,exprOrfn,handler,{...options,user:true}
  1. initComputed
  • 获取到所有computed属性的key,handler
  • 创建vm._computedWatcher,遍历赋值key:new Watcher
for(let key in computed){// getters 为computed的handler计算函数// lazy:true在watcher中区分时computed的属性,执行函数为计算函数// watcher中用dirty取初始化lazy,默认为true,表示第一次或者需要再次执行handler计算函数,为false读取缓存的值,即watcher的value_computedWatcher[key] = new Watcher(vm,getters||handler,()=>{},{lazy:true})
  • 遍历computed,进行defineComputed,对每一个key进行劫持修改get函数
  • get的值为_computedWatcher对应key的watcher.value
  • 如果dirty为true(默认值,或修改值后赋为true),执行watcher.evaluate方法即computed的handler方法,执行完dirty = false
  • 判断如果Dep.target有,收集渲染watcher

vm.$mounted 挂载dom

  1. 拿到vm.el的outerHTML
  2. 转换为ast语法树
  3. 转为render函数(_s 解析变量 _v解析文本,_c解析标签),通过with函数实现解析_s函数this.变量的值。
  4. 执行mountedComponent函数
  5. 执行各个运行的钩子函数(beforeMounted,updated,mounted)
  6. 创建watcher实例 (lifecyle.js)
    // _render拿到的是虚拟dom解构数据let updateComponent = ()=>{vm._update(vm._render())}new watcher(vm,updateComponent,()=>{callHook(vm,'updated') //订阅},true)
  1. watcher中执行get方法
    pushTarget(this) const value = this.getters.call(this.vm,this)popTarget() 
  1. pushTarget:先给Dep添加当前watcher至target
  2. 执行getters即_update方法:通过with函数实现解析_s函数this.变量的值,此时会调用该变量的obsercer中定义的get方法,Dep.target有值,执行dep.depend(),进行watcher dep互相收集,最后移除Dep的target
//  watcher为当前渲染watcher,dep为每一个definReactive中的私有变量 dep = new Dep()dep.depend() =>  Dep.target.addDep(this)  =>  watcher.addDep(dep) =>watcher.addDep(dep)=>{// 在watcher中deps添加depthis.deps.push(dep)this.depsId.add(id)//dep中subs也添加当前watcherdep.addSub(this(watcher)) }
  1. 运行patch函数,通过diff算法实现最小量更新渲染真实DOM

总结

new watcher的地方有三处

  1. lifecycle中:mountComponent方法挂载dom。此watcher为一个渲染watcher,自动执行的get函数为渲染dom函数,回调函数为upadted钩子函数
    new watcher(vm,updateComponent,()=>{callHook(vm,'updated') },true)
  1. initState中,initWatch方法,对每一个key创建一个watcher,watcher函数中如果有user则获取到值后再次执行handler函数,实现监听功能
//            vm key handler
new Watcher(vm,exprOrfn,handler,{...options,user:true}

new watcher 默认执行get函数,读取一次vm.data的值,调用observe的get,进行依赖收集,defineReactive函数中的私有变量dep再次收集watch监听的watcher。set该变量时,dep.notify会挨个通知执行subs中的watcher

  1. initState中,**执行initComputed方法,此watcher是一个计算watcher,get函数为computed配置项里的计算函数。**无回调函数,options参数lazy=true,在watcher中区分是computed的watcher,初始化watcher不执行get函数。读取computed属性时,触发defineProperty劫持的getter方法,最终取值为watcher.value。其中如果第一次取值dirty为ture,计算watcher.evaluate,缓存value。以后取值读取缓存value,dirty赋为false,除非调用该watcher的update方法,dirty为true,再次调用才会重新计算evaluate
 new Watcher(vm,getters||handler,()=>{},{lazy:true})

Q:computed的get方法中:判断if(Dep.target)再进行watcher.depend()互相收集依赖,Dep.target不是每次赋值完就pop吗?Dep中,stack什么情况下length>1?

A:当计算computed的值时,取到vm的变量,触发observe中的getter,此时Dep.target为computed的watcher,再observe中
会将各个变量 fisrtName lastName的dep中push computed-watcher,再computed-watcher的deps中push firstName和
lastName的dep.

new Dep

在给每个data进行响应式处理时,defineReactive函数里定义私有变量,dep=Dep(),没一个变量都可以找到一个对应的dep

Q: 一个变量对应一个dep,对应一个渲染watcher,可以有多个computed watcher,一个watch watcher, 对不对?
A:一个变量对应一个dep:data中每一个变量进行数据劫持(定义getter)时,定义一个私有变量dep,只能通过变量getter时获取得到
watcher有以下几种

  1. 渲染watcher 即render watcher, 在初始化数据劫持后执行后执行,初始执行函数为vm._update 渲染dom
  2. computed watcher,1 执行完毕后执行初始化绑定opiton里的wathcer配置项,主执行函数为计算函数,计算时会取vm的变量,调用observe的getter,此时Dep.target有值,会进行子变量的dep和当前计算watcher的互相收集
  3. watch watcher,1 执行完毕后初始化,拿到options watch配置的key和handler,初始化watcher,此时主函数是个字符串key,初始执行为自定义watcher的getter计算watcher.value来获取oldValue,该过程中调用vm.变量,同样进行2中的互相收集,该watcher的cb函数为配置项的handler:即监听后的用户处理回调;最后一个配置项user:true,用来区分update时,获取到新值后再执行回调函数。

附上本人github的学习仓库 learn-vue2-source-code (有大量注释
p:求求给点个star吧

当我精通vue2的源码dep和watcher的关系时相关推荐

  1. Vue2.0源码解析——编译原理

    Vue2.0源码解析--编译原理 前言:本篇文章主要对Vue2.0源码的编译原理进行一个粗浅的分析,其中涉及到正则.高阶函数等知识点,对js的考察是非常的深的,因此我们来好好啃一下这个编译原理的部分. ...

  2. 【手写 Vue2.x 源码】第二十八篇 - diff 算法-问题分析与 patch 优化

    一,前言 首先,对 6 月的更文内容做一下简单回顾: Vue2.x 源码环境的搭建 Vue2.x 初始化流程介绍 对象的单层.深层劫持 数组的单层.深层劫持 数据代理的实现 对象.数组数据变化的观测 ...

  3. 前端进阶-手写Vue2.0源码(三)|技术点评

    前言 今天是个特别的日子 祝各位女神女神节快乐哈 封面我就放一张杀殿的帅照表达我的祝福 哈哈 此篇主要手写 Vue2.0 源码-初始渲染原理 上一篇咱们主要介绍了 Vue 模板编译原理 它是 Vue ...

  4. OSChina 周三乱弹 —— 以后面试可以说自己精通B站源码了吧

    2019独角兽企业重金招聘Python工程师标准>>> Osc乱弹歌单(2019)请戳(这里) [今日歌曲] @Cobbage :分享The xx的单曲<Intro>: ...

  5. 3000门徒内部训练绝密视频(泄密版)第2课:Scala面向对象彻底精通及Spark源码阅读

    Scala面向对象彻底精通及Spark源码阅读 不用写public class中的public class Person {private var myName = "flink" ...

  6. 3000门徒内部训练绝密视频(泄密版)第3课:Scala中函数式编程彻底精通及Spark源码阅读

    Scala中函数式编程彻底精通及Spark源码阅读 函数可以不依赖于类,函数可以作为函数的参数,函数可以作为函数的返回值 =>表明对左面的参数进行右面的加工 函数赋值给变量需要在函数名后面加空格 ...

  7. Vue2.x 源码 - VNode渲染过程(update、patch)

    上一篇:Vue2.x 源码 - render 函数生成 VNode Vue 的渲染过程: 上一篇写了 render 函数生成 VNode 的过程,这一篇就来看看 VNode 是怎么渲染成真实 DOM ...

  8. Vue2.x 源码 - render 函数生成 VNode

    上一篇:Vue2.x 源码 - 编译过程(compile) Vue 的渲染过程: 上一篇看了一下编译,编译之后返回了 render 函数,那么 render 又是如何生成 vnode 的呢?本文来看一 ...

  9. Vue源码之渲染watcher

    1. 前文回顾 在上一篇文章<Vue源码之计算属性watcher>中,我们学习了计算属性watcher是如何与计算属性的computedGetter协作,在计算属性所依赖的数据发生变化时, ...

最新文章

  1. CSS之 :before :after的用法,伪类和伪元素的区别
  2. 坑爹的SQL ISNUMERIC
  3. 怎么用计算机算p a,老师,(P/A,12%,10)这个值用计算器怎么算出来?
  4. C语言试题三之计算并输出 s=1+(1+2^(0.5))+(1+2^(0.5)+3^(0.5))+…+(1+2^(0.5)+3^(0.5)+…+n^(0.5))
  5. 论文浅尝 | 六篇2020年知识图谱预训练论文综述
  6. 使电动机反转的matlab仿真图,基于simulink的Matlab仿真作业(电气工程专业)2
  7. Python oct()函数
  8. 基于Proteus学习单片机系列(十)——LCD1602
  9. 比ietest 更好的浏览器调试工具 Browser Sandbox 使用教程
  10. 从818悟空榜看苏宁全场景零售的数据赋能
  11. 世界上最复杂的函数_世界上最伟大的10个公式,其中一个人尽皆知
  12. JavaScript弹性透明的图片放大代码
  13. Word中下划线自动换行版式不…
  14. 用ec怎么修改网络服务器数值,ec服务器
  15. 刀片服务器改台式电脑_详解刀片服务器如何走向融合
  16. IDEA引用Class文件失败问题解决
  17. 如何有效地阅读文献并做笔记
  18. source insight 绿色护眼主题暗色护眼主题
  19. 怎么利用完成端口监听多个不同端口的socket
  20. 简单使用discord.com教程

热门文章

  1. 计算机桌面图标出现蓝框,win10桌面快捷图标蓝框怎么去除 附黑色方块去除/白色方块修复的方法步骤...
  2. Docker容器之Consul部署
  3. Triggers — 触发响应
  4. iOS 判断系统版本
  5. 认识多种处理芯片的特性和实战(上篇)
  6. php 跳转邮箱,实例详解JS简单实现点击跳转登陆邮箱功能的方法
  7. 凯撒密码解密加密(C语言)
  8. c语言程序 强制关机程序,怎么用C或者C++编写一个电脑强制关机程序
  9. SPSS Modeler 建模前准备—数据平衡与特征选择(指南 第十一章)
  10. fatal: detected dubious ownership in repository git报错解决