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

在这里插入图片描述

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添加dep
        this.deps.push(dep)
        this.depsId.add(id)
        //dep中subs也添加当前watcher
        dep.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吧😍

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值