likes
comments
collection
share

vue3依赖收集和依赖触发(Link版)众所周知,vue是通过数据的变化进而改变视图的。从而就会有依赖收集和依赖触发这两

作者站长头像
站长
· 阅读数 52

众所周知,vue是通过数据的变化进而改变视图的。从而就会有依赖收集和依赖触发这两个说法,顾名思义就是收集数据依赖,在你数据改变的时候,去改变使用到这个数据的地方。

依赖收集

举个例子:

const a=ref(0)
watch(a,(nA,oA)=>{
console.log(nA,oA)
})

像上面的实例,watch会监听a的变化来打印a变化前和变化后的值,watch是怎么做到的呢?watch是怎么知道a什么时候变化的呢?这就我们要讲的依赖收集了。在vue3中,我们想要数据具有响应式数据,我们就得使用ref,reactive等api去定义我们的变量。这里就不描述其使用方法了。相信各位都会的。我们要讲的依赖收集其实就是在ref函数里面实现的。下面我会根据源码一步步的分析。

vue3依赖收集和依赖触发(Link版)众所周知,vue是通过数据的变化进而改变视图的。从而就会有依赖收集和依赖触发这两

上面代码不难看出在创建ref的时候会判断当前数据是不是ref,如果是就直接返回。不是就会进入RefImpl构造函数。

vue3依赖收集和依赖触发(Link版)众所周知,vue是通过数据的变化进而改变视图的。从而就会有依赖收集和依赖触发这两

看到RefImpl构造函数里面的get value()方法,我们要讲的依赖收集就是在这里面实现的。this.dep.track(),没错就是这一行代码做的依赖搜集。我们继续看看Dep是怎么搜集的依赖。传送到构造函数Dep,代码如下:

vue3依赖收集和依赖触发(Link版)众所周知,vue是通过数据的变化进而改变视图的。从而就会有依赖收集和依赖触发这两 这里我们主要关注track方法,这里我们得了解数据结构链表,目前最新的源码的添加依赖的方式是为链表的形式的

vue3依赖收集和依赖触发(Link版)众所周知,vue是通过数据的变化进而改变视图的。从而就会有依赖收集和依赖触发这两

这段代码是link初始化,列一下link的数据结构:

  1. dep:存的是this,也就是Dep自身
  2. sub:为当前活跃的调度器(effect)。后面会讲
  3. version:顾名思义就是版本的意思
  4. nextDep:当前链表后面指向的dep
  5. prevDep:当前链表前面指向的dep
  6. nextSub;当前链表后面指向的sub
  7. prevSub:当前链表后面指向的sub
  8. prevActiveLink:当前链表后面指向的Link

可能上面描述的不是很准确,大家简单了解一下。我们继续看流程,当我们执行tarck的时候就是会添加依赖会把当前的link加到sub里面去。

vue3依赖收集和依赖触发(Link版)众所周知,vue是通过数据的变化进而改变视图的。从而就会有依赖收集和依赖触发这两

vue3依赖收集和依赖触发(Link版)众所周知,vue是通过数据的变化进而改变视图的。从而就会有依赖收集和依赖触发这两

addSub的关键代码就是红框内的,熟悉链表数据结构很快知道这其实就往链表后面加数据的操作dep.subsHead,dep.subs表示调度器effect头和尾节点。说白了dep的作用其实就是ref和effect之间桥梁。ref是通过dep和effect绑定关系的。到这里依赖收集就讲完了。

总结:数据通过ref的get方法触发dep.track来把当前activeLink添加到link上。

在分析依赖触发之前得补充一下effect的工作原理,回顾到文章开头的例子watch中,看到watch的源码中

vue3依赖收集和依赖触发(Link版)众所周知,vue是通过数据的变化进而改变视图的。从而就会有依赖收集和依赖触发这两 熟悉的三个参数,分别是要监听的数据,数据改变后的回调函数,还有就是监听方式配置。

vue3依赖收集和依赖触发(Link版)众所周知,vue是通过数据的变化进而改变视图的。从而就会有依赖收集和依赖触发这两

进入watch后source数据会经过一些判断,比如是否为ref,是否为recative,是否为function等等,需要仔细了解的可以自行看源码。最终source经过这一系列判断后会赋值给gettet

vue3依赖收集和依赖触发(Link版)众所周知,vue是通过数据的变化进而改变视图的。从而就会有依赖收集和依赖触发这两

有了getter之后会到这里来创建effect,先不用管new ReactiveEffect()我们继续往下看

vue3依赖收集和依赖触发(Link版)众所周知,vue是通过数据的变化进而改变视图的。从而就会有依赖收集和依赖触发这两

这里会执行effect.run,job中的逻辑也是会执行effect.run的。这里我主要是讲流程我就不一一截图了,知道一定是会走到effect.run就好。接下来我们来看一下ReactiveEffect类的主要实现:

vue3依赖收集和依赖触发(Link版)众所周知,vue是通过数据的变化进而改变视图的。从而就会有依赖收集和依赖触发这两

根据上面的new ReactiveEffect(getter)我们可以知道,getter会通过constructor入参进来,这里的constructor写法入参使用public修饰了,要注意的是这里的public fn等同于this.fn=fn,就是创建了一个和入参同名的属性。从上面我们可以知道effect最终都会执行run方法。我们之间跳到run方法:

vue3依赖收集和依赖触发(Link版)众所周知,vue是通过数据的变化进而改变视图的。从而就会有依赖收集和依赖触发这两

run方法主要做的事情是把当前的实例添加到activeSub然后执行fn()也就是传进来的getter,触发dep.track就可以收集到dep依赖link上了。讲到这里依赖收集就已经完整了。

依赖触发

上面要是理解了依赖收集,依赖触发就很容易了。回到ref的源码中,也就是set value()方法

vue3依赖收集和依赖触发(Link版)众所周知,vue是通过数据的变化进而改变视图的。从而就会有依赖收集和依赖触发这两

我们可以看到set方法会调用dep.trigger(),我们跳到dep中

vue3依赖收集和依赖触发(Link版)众所周知,vue是通过数据的变化进而改变视图的。从而就会有依赖收集和依赖触发这两

在trigger中做了一些版本记录操作,然后执行this.notify()。在notify中主要看effect文件中的 startBatch()和endBatch()

vue3依赖收集和依赖触发(Link版)众所周知,vue是通过数据的变化进而改变视图的。从而就会有依赖收集和依赖触发这两

这两个函数简单理解就是批量执行了effect.trigger(),具体的实现方式大家可以翻看源码。继续看effect.trigger方法

vue3依赖收集和依赖触发(Link版)众所周知,vue是通过数据的变化进而改变视图的。从而就会有依赖收集和依赖触发这两

执行了this.scheduler(),我们回到watch的源码中:

vue3依赖收集和依赖触发(Link版)众所周知,vue是通过数据的变化进而改变视图的。从而就会有依赖收集和依赖触发这两

effect.scheduler其实就是job方法,就是我们wacth传进去的cb

vue3依赖收集和依赖触发(Link版)众所周知,vue是通过数据的变化进而改变视图的。从而就会有依赖收集和依赖触发这两

到这里我们就把整个watch的创建依赖到依赖监听的过程讲闭环了。

总结流程

vue3依赖收集和依赖触发(Link版)众所周知,vue是通过数据的变化进而改变视图的。从而就会有依赖收集和依赖触发这两

转载自:https://juejin.cn/post/7423006531056615487
评论
请登录