亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

一文帶你搞懂Vue3.5的響應(yīng)式重構(gòu)

 更新時間:2024年11月26日 15:07:12   作者:前端歐陽  
在Vue3.5版本中最大的改動就是響應(yīng)式重構(gòu),重構(gòu)后性能竟然炸裂的提升了56%,本文我們來講講使用雙向鏈表后,Vue內(nèi)部是如何實現(xiàn)依賴收集和依賴觸發(fā)的,感興趣的可以了解下

前言

在Vue3.5版本中最大的改動就是響應(yīng)式重構(gòu),重構(gòu)后性能竟然炸裂的提升了56%。之所以重構(gòu)后的響應(yīng)式性能提升幅度有這么大,主要還是歸功于:雙向鏈表和版本計數(shù)。這篇文章我們來講講使用雙向鏈表后,Vue內(nèi)部是如何實現(xiàn)依賴收集和依賴觸發(fā)的。搞懂了這個之后你就能掌握Vue3.5重構(gòu)后的響應(yīng)式原理。

3.5版本以前的響應(yīng)式

在Vue3.5以前的響應(yīng)式中主要有兩個角色:Sub(訂閱者)、Dep(依賴)。其中的訂閱者有watchEffect、watch、render函數(shù)、computed等。依賴有ref、reactive等響應(yīng)式變量。

舉個例子:

<script setup lang="ts">
import { ref, watchEffect } from "vue";
let dummy1, dummy2;
//Dep1
const counter1 = ref(1);
//Dep2
const counter2 = ref(2);
//Sub1
watchEffect(() => {
  dummy1 = counter1.value + counter2.value;
  console.log("dummy1", dummy1);
});
//Sub2
watchEffect(() => {
  dummy2 = counter1.value + counter2.value + 1;
  console.log("dummy2", dummy2);
});

counter1.value++;
counter2.value++;
</script>

在上面的兩個watchEffect中都會去監(jiān)聽ref響應(yīng)式變量:counter1counter2

初始化時會分別執(zhí)行這兩個watchEffect中的回調(diào)函數(shù),所以就會對里面的響應(yīng)式變量counter1counter2進行讀操作,所以就會走到響應(yīng)式變量的get攔截中。

在get攔截中會進行依賴收集(此時的Dep依賴分別是變量counter1counter2)。

因為在依賴收集期間是在執(zhí)行watchEffect中的回調(diào)函數(shù),所以依賴對應(yīng)的Sub訂閱者就是watchEffect。

由于這里有兩個watchEffect,所以這里有兩個Sub訂閱者,分別對應(yīng)這兩個watchEffect。

在上面的例子中,watchEffect監(jiān)聽了多個ref變量。也就是說,一個Sub訂閱者(也就是一個watchEffect)可以訂閱多個依賴。

ref響應(yīng)式變量counter1被多個watchEffect給監(jiān)聽。也就是說,一個Dep依賴(也就是counter1變量)可以被多個訂閱者給訂閱。

Sub訂閱者和Dep依賴他們兩的關(guān)系是多對多的關(guān)系?。?!

上面這個就是以前的響應(yīng)式模型。

新的響應(yīng)式模型

在Vue3.5版本新的響應(yīng)式中,Sub訂閱者和Dep依賴之間不再有直接的聯(lián)系,而是新增了一個Link作為橋梁。Sub訂閱者通過Link訪問到Dep依賴,同理Dep依賴也是通過Link訪問到Sub訂閱者。如下圖:

把上面這個圖看懂了,你就能理解Vue新的響應(yīng)式系統(tǒng)啦。現(xiàn)在你直接看這個圖有可能看不懂,沒關(guān)系,等我講完后你就能看懂了。

首先從上圖中可以看到Sub訂閱者和Dep依賴之間沒有任何直接的連接關(guān)系了,也就是說Sub訂閱者不能直接訪問到Dep依賴,Dep依賴也不能直接訪問Sub訂閱者。

Dep依賴我們可以看作是X軸,Sub訂閱者可以看作是Y軸,這些Link就是坐標軸上面的坐標。

Vue響應(yīng)式系統(tǒng)的核心還是沒有變,只是多了一個Link,依然還是以前的那一套依賴收集和依賴觸發(fā)的流程。

在依賴收集的過程中就會畫出上面這個圖,這個不要急,我接下來會仔細去講圖是如何畫出來的。

那么依賴觸發(fā)的時候又是如何利用上面這種圖從而實現(xiàn)觸發(fā)依賴的呢?我們來看個例子。

上面的這張圖其實對應(yīng)的是我之前舉的例子:

<script setup lang="ts">
import { ref, watchEffect } from "vue";
let dummy1, dummy2;
//Dep1
const counter1 = ref(1);
//Dep2
const counter2 = ref(2);
//Sub1
watchEffect(() => {
  dummy1 = counter1.value + counter2.value;
  console.log("dummy1", dummy1);
});
//Sub2
watchEffect(() => {
  dummy2 = counter1.value + counter2.value + 1;
  console.log("dummy2", dummy2);
});

counter1.value++;
counter2.value++;
</script>

圖中的Dep1依賴對應(yīng)的就是變量counter1,Dep2依賴對應(yīng)的就是變量counter2。Sub1訂閱者對應(yīng)的就是第一個watchEffect函數(shù),Sub2訂閱者對應(yīng)的就是第二個watchEffect函數(shù)。

當執(zhí)行counter1.value++時,就會被變量counter1(也就是Dep1依賴)的set函數(shù)攔截。從上圖中可以看到Dep1依賴有個箭頭(對照表中的sub屬性)指向Link3,并且Link3也有一個箭頭(對照表中的sub屬性)指向Sub2。

前面我們講過了這個Sub2就是對應(yīng)的第二個watchEffect函數(shù),指向Sub2后我們就可以執(zhí)行Sub2中的依賴,也就是執(zhí)行第二個watchEffect函數(shù)。這就實現(xiàn)了counter1.value++變量改變后,重新執(zhí)行第二個watchEffect函數(shù)。

執(zhí)行了第二個watchEffect函數(shù)后我們發(fā)現(xiàn)Link3在Y軸上面還有一個箭頭(對照表中的preSub屬性)指向了Link1。同理Link1也有一個箭頭(對照表中的sub屬性)指向了Sub1

前面我們講過了這個Sub1就是對應(yīng)的第一個watchEffect函數(shù),指向Sub1后我們就可以執(zhí)行Sub1中的依賴,也就是執(zhí)行第一個watchEffect函數(shù)。這就實現(xiàn)了counter1.value++變量改變后,重新執(zhí)行第一個watchEffect函數(shù)。

至此我們就實現(xiàn)了counter1.value++變量改變后,重新去執(zhí)行依賴他的兩個watchEffect函數(shù)。

我們此時再來回顧一下我們前面畫的新的響應(yīng)式模型圖,如下圖:

我們從這張圖來總結(jié)一下依賴觸發(fā)的的規(guī)則:

響應(yīng)式變量Dep1改變后,首先會指向Y軸(Sub訂閱者)的隊尾的Link節(jié)點。然后從Link節(jié)點可以直接訪問到Sub訂閱者,訪問到訂閱者后就可以觸發(fā)其依賴,這里就是重新執(zhí)行對應(yīng)的watchEffect函數(shù)。

接著就是順著Y軸的隊尾隊頭移動,每移動到一個新的Link節(jié)點都可以指向一個新的Dep依賴,在這里觸發(fā)其依賴就會重新指向?qū)?yīng)的watchEffect函數(shù)。

看到這里有的同學有疑問如果是Dep2對應(yīng)的響應(yīng)式變量改變后指向Link4,那這個Link4又是怎么指向Sub2的呢?他們中間不是還隔了一個Link3嗎?

每一個Link節(jié)點上面都有一個sub屬性直接指向Y軸上面的Sub依賴,所以這里的Link4有個箭頭(對照表中的sub屬性)可以直接指向Sub2,然后進行依賴觸發(fā)。

這就是Vue3.5版本使用雙向鏈表改進后的依賴觸發(fā)原理,接下來我們會去講依賴收集過程中是如何將上面的模型圖畫出來的。

Dep、Sub和Link

在講Vue3.5版本依賴收集之前,我們先來了解一下新的響應(yīng)式系統(tǒng)中主要的三個角色:Dep依賴、Sub訂閱者、Link節(jié)點。

這三個角色其實都是class類,依賴收集和依賴觸發(fā)的過程中實際就是在操作這些類new出來的的對象。

我們接下來看看這些類中有哪些屬性和方法,其實在前面的響應(yīng)式模型圖中我們已經(jīng)使用箭頭標明了這些類上面的屬性。

Dep依賴

簡化后的Dep類定義如下:

class Dep {
  // 指向Link鏈表的尾部節(jié)點
  subs: Link
  // 收集依賴
  track: Function
  // 觸發(fā)依賴
  trigger: Function
}

Dep依賴上面的subs屬性就是指向隊列的尾部,也就是隊列中最后一個Sub訂閱者對應(yīng)的Link節(jié)點。

比如這里的Dep1,豎向的Link1Link3就組成了一個隊列。其中Link3是隊列的隊尾,Dep1subs屬性就是指向Link3

其次就是track函數(shù),對響應(yīng)式變量進行讀操作時會觸發(fā)。觸發(fā)這個函數(shù)后會進行依賴收集,后面我會講。

同樣trigger函數(shù)用于依賴觸發(fā),對響應(yīng)式變量進行寫操作時會觸發(fā),后面我也會講。

Sub訂閱者

簡化后的Sub訂閱者定義如下:

interface Subscriber {
  // 指向Link鏈表的頭部節(jié)點
  deps: Link
  // 指向Link鏈表的尾部節(jié)點
  depsTail: Link
  // 執(zhí)行依賴
  notify: Function
}

想必細心的你發(fā)現(xiàn)了這里的Subscriber是一個interface接口,而不是一個class類。因為實現(xiàn)了這個Subscriber接口的class類都是訂閱者,比如watchEffect、watch、render函數(shù)、computed等。

比如這里的Sub1,橫向的Link1Link2就組成一個隊列。其中的隊尾就是Link2depsTail屬性),隊頭就是Link1deps屬性)。

還有就是notify函數(shù),執(zhí)行這個函數(shù)就是在執(zhí)行依賴。比如對于watchEffect來說,執(zhí)行notify函數(shù)后就會執(zhí)行watchEffect的回調(diào)函數(shù)。

Link節(jié)點

簡化后的Link節(jié)點類定義如下:

class Link {
  // 指向Subscriber訂閱者
  sub: Subscriber
  // 指向Dep依賴
  dep: Dep
  // 指向Link鏈表的后一個節(jié)點(X軸)
  nextDep: Link
  // 指向Link鏈表的前一個節(jié)點(X軸)
  prevDep: Link
  // 指向Link鏈表的下一個節(jié)點(Y軸)
  nextSub: Link
  // 指向Link鏈表的上一個節(jié)點(Y軸)
  prevSub: Link
}

前面我們講過了新的響應(yīng)式模型中Dep依賴Sub訂閱者之間不會再有直接的關(guān)聯(lián),而是通過Link作為橋梁。

那么作為橋梁的Link節(jié)點肯定需要有兩個屬性能夠讓他直接訪問到Dep依賴Sub訂閱者,也就是subdep屬性。

其中的sub屬性是指向Sub訂閱者,dep屬性是指向Dep依賴。

我們知道Link是坐標軸的點,那這個點肯定就會有上、下、左、右四個方向。

比如對于Link1可以使用nextDep屬性來訪問后面這個節(jié)點Link2,Link2可以使用prevDep屬性來訪問前面這個節(jié)點Link1。

請注意,這里名字雖然叫nextDepprevDep,但是他們指向的卻是Link節(jié)點。然后通過這個Link節(jié)點的dep屬性,就可以訪問到后一個Dep依賴或者前一個Dep依賴。

同理對于Link1可以使用nextSub訪問后面這個節(jié)點Link3,Link3可以使用prevSub訪問前面這個節(jié)點Link1

同樣的這里名字雖然叫nextSubprevSub,但是他們指向的卻是Link節(jié)點。然后通過這個Link節(jié)點的sub屬性,就可以訪問到下一個Sub訂閱者或者上一個Sub訂閱者。

如何收集依賴

搞清楚了新的響應(yīng)式模型中的三個角色:Dep依賴Sub訂閱者、Link節(jié)點,我們現(xiàn)在就可以開始搞清楚新的響應(yīng)式模型是如何收集依賴的。

接下來我將會帶你如何一步步的畫出前面講的那張新的響應(yīng)式模型圖。

還是我們前面的那個例子,代碼如下:

<script setup lang="ts">
import { ref, watchEffect } from "vue";
let dummy1, dummy2;
//Dep1
const counter1 = ref(1);
//Dep2
const counter2 = ref(2);
//Sub1
watchEffect(() => {
  dummy1 = counter1.value + counter2.value;
  console.log("dummy1", dummy1);
});
//Sub2
watchEffect(() => {
  dummy2 = counter1.value + counter2.value + 1;
  console.log("dummy2", dummy2);
});

counter1.value++;
counter2.value++;
</script>

大家都知道響應(yīng)式變量有getset攔截,當對變量進行讀操作時會走到get攔截中,進行寫操作時會走到set攔截中。

上面的例子第一個watchEffect我們叫做Sub1訂閱者,第二個watchEffect叫做Sub2訂閱者.

初始化時watchEffect中的回調(diào)會執(zhí)行一次,這里有兩個watchEffect,會依次去執(zhí)行。

在Vue內(nèi)部有個全局變量叫activeSub,里面存的是當前active的Sub訂閱者。

執(zhí)行第一個watchEffect回調(diào)時,當前的activeSub就是Sub1

Sub1中使用到了響應(yīng)式變量counter1counter2,所以會對這兩個變量依次進行讀操作。

第一個watchEffect對counter1進行讀操作

先對counter1進行讀操作時,會走到get攔截中。核心代碼如下:

class RefImpl {
get value() {
  this.dep.track();
  return this._value;
}
}

從上面可以看到在get攔截中直接調(diào)用了dep依賴的track方法進行依賴收集。

在執(zhí)行track方法之前我們思考一下當前響應(yīng)式系統(tǒng)中有哪些角色,分別是Sub1Sub2這兩個watchEffect回調(diào)函數(shù)訂閱者,以及counter1counter2這兩個Dep依賴。此時的響應(yīng)式模型如下圖:

從上圖可以看到此時只有X坐標軸的Dep依賴,以及Y坐標軸的Sub訂閱者,沒有一個Link節(jié)點。

我們接著來看看dep依賴的track方法,核心代碼如下:

class Dep {
// 指向Link鏈表的尾部節(jié)點
subs: Link;
track() {
  let link = new Link(activeSub, this);
  if (!activeSub.deps) {
    activeSub.deps = activeSub.depsTail = link;
  } else {
    link.prevDep = activeSub.depsTail;
    activeSub.depsTail!.nextDep = link;
    activeSub.depsTail = link;
  }
  addSub(link);
}
}

從上面的代碼可以看到,每執(zhí)行一次track方法,也就是說每次收集依賴,都會執(zhí)行new Link去生成一個Link節(jié)點。

并且傳入兩個參數(shù),activeSub為當前active的訂閱者,在這里就是Sub1(第一個watchEffect)。第二個參數(shù)為this,指向當前的Dep依賴對象,也就是Dep1counter1變量)。

先不看track后面的代碼,我們來看看Link這個class的代碼,核心代碼如下:

class Link {
// 指向Link鏈表的后一個節(jié)點(X軸)
nextDep: Link;
// 指向Link鏈表的前一個節(jié)點(X軸)
prevDep: Link;
// 指向Link鏈表的下一個節(jié)點(Y軸)
nextSub: Link;
// 指向Link鏈表的上一個節(jié)點(Y軸)
prevSub: Link;
- constructor(public sub: Subscriber, public dep: Dep) {
  // ...省略
}
}

細心的小伙伴可能發(fā)現(xiàn)了在Link中沒有聲明subdep屬性,那么為什么前面我們會說Link節(jié)點中的subdep屬性分別指向Sub訂閱者和Dep依賴呢?

因為在constructor構(gòu)造函數(shù)中使用了public關(guān)鍵字,所以subdep就作為屬性暴露出來了。

執(zhí)行完let link = new Link(activeSub, this)后,在響應(yīng)式系統(tǒng)模型中初始化出來第一個Link節(jié)點,如下圖:

從上圖可以看到Link1sub屬性指向Sub1訂閱者,dep屬性指向Dep1依賴。

我們接著來看track方法中剩下的代碼,如下:

class Dep {
// 指向Link鏈表的尾部節(jié)點
subs: Link;
track() {
  let link = new Link(activeSub, this);
  if (!activeSub.deps) {
    activeSub.deps = activeSub.depsTail = link;
  } else {
    link.prevDep = activeSub.depsTail;
    activeSub.depsTail!.nextDep = link;
    activeSub.depsTail = link;
  }
  addSub(link);
}
}

先來看if (!activeSub.deps),activeSub前面講過了是Sub1。activeSub.deps就是Sub1deps屬性,也就是Sub1隊列上的第一個Link。

從上圖中可以看到此時的Sub1并沒有箭頭指向Link1,所以if (!activeSub.deps)為true,代碼會執(zhí)行

activeSub.deps = activeSub.depsTail = link;

depsdepsTail屬性分別指向Sub1隊列的頭部和尾部,當前隊列中只有Link1這一個節(jié)點,那么頭部和尾部當然都指向Link1。

執(zhí)行完這行代碼后響應(yīng)式模型圖就變成下面這樣的了,如下圖:

從上圖中可以看到Sub1的隊列中只有Link1這一個節(jié)點,所以隊列的頭部和尾部都指向Link1。

處理完Sub1的隊列,但是Dep1的隊列還沒處理,Dep1的隊列是由addSub(link)函數(shù)處理的。addSub函數(shù)代碼如下:

function addSub(link: Link) {
const currentTail = link.dep.subs;
if (currentTail !== link) {
  link.prevSub = currentTail;
  if (currentTail) currentTail.nextSub = link;
}
link.dep.subs = link;
}

由于Dep1隊列中沒有Link節(jié)點,所以此時在addSub函數(shù)中主要是執(zhí)行第三塊代碼:link.dep.subs = link。`

link.dep是指向Dep1,前面我們講過了Dep依賴的subs屬性指向隊列的尾部。所以link.dep.subs = link就是將Link1指向Dep1的隊列的尾部,執(zhí)行完這行代碼后響應(yīng)式模型圖就變成下面這樣的了,如下圖:

到這里對第一個響應(yīng)式變量counter1進行讀操作進行的依賴收集就完了。

第一個watchEffect對counter2進行讀操作

在第一個watchEffect中接著會對counter2變量進行讀操作。同樣會走到get攔截中,然后執(zhí)行track函數(shù),代碼如下:

class Dep {
  // 指向Link鏈表的尾部節(jié)點
  subs: Link;
  track() {
    let link = new Link(activeSub, this);

    if (!activeSub.deps) {
      activeSub.deps = activeSub.depsTail = link;
    } else {
      link.prevDep = activeSub.depsTail;
      activeSub.depsTail!.nextDep = link;
      activeSub.depsTail = link;
    }

    addSub(link);
  }
}

同樣的會執(zhí)行一次new Link(activeSub, this),然后把新生成的Link2subdep屬性分別指向Sub1Dep2。執(zhí)行后的響應(yīng)式模型圖如下圖:

從上面的圖中可以看到此時Sub1deps屬性是指向Link1的,所以這次代碼會走進else模塊中。else部分代碼如下:

link.prevDep = activeSub.depsTail;
activeSub.depsTail.nextDep = link;
activeSub.depsTail = link;

activeSub.depsTail指向Sub1隊列尾部的Link,值是Link1。所以執(zhí)行link.prevDep = activeSub.depsTail就是將Link2prevDep屬性指向Link1。

同理activeSub.depsTail.nextDep = link就是將Link1nextDep屬性指向Link2,執(zhí)行完這兩行代碼后Link1Link2之間就建立關(guān)系了。如下圖:

從上圖中可以看到此時Link1Link2之間就有箭頭連接,可以互相訪問到對方。

最后就是執(zhí)行activeSub.depsTail = link,這行代碼是將Sub1隊列的尾部指向Link2。執(zhí)行完這行代碼后模型圖如下:

Sub1訂閱者的隊列就處理完了,接著就是處理Dep2依賴的隊列。Dep2的處理方式和Dep1是一樣的,讓Dep2隊列的隊尾指向Link2,處理完了后模型圖如下:

到這里第一個watchEffect(也就是Sub1)對其依賴的兩個響應(yīng)式變量counter1(也就是Dep1)和counter2(也就是Dep2),進行依賴收集的過程就執(zhí)行完了。

第二個watchEffect對counter1進行讀操作

接著我們來看第二個watchEffect,同樣的還是會對counter1進行讀操作。然后觸發(fā)其get攔截,接著執(zhí)行track方法?;貞浺幌?code>track方法的代碼,如下:

class Dep {
  // 指向Link鏈表的尾部節(jié)點
  subs: Link;
  track() {
    let link = new Link(activeSub, this);

    if (!activeSub.deps) {
      activeSub.deps = activeSub.depsTail = link;
    } else {
      link.prevDep = activeSub.depsTail;
      activeSub.depsTail!.nextDep = link;
      activeSub.depsTail = link;
    }

    addSub(link);
  }
}

這里還是會使用new Link(activeSub, this)創(chuàng)建一個Link3節(jié)點,節(jié)點的subdep屬性分別指向Sub2Dep1。如下圖:

同樣的Sub2隊列上此時還沒任何值,所以if (!activeSub.deps)為true,和之前一樣會去執(zhí)行activeSub.deps = activeSub.depsTail = link;Sub2隊列的頭部和尾部都設(shè)置為Link3。如下圖:

處理完Sub2隊列后就應(yīng)該調(diào)用addSub函數(shù)來處理Dep1的隊列了,回憶一下addSub函數(shù),代碼如下:

function addSub(link: Link) {
  const currentTail = link.dep.subs;
  if (currentTail !== link) {
    link.prevSub = currentTail;
    if (currentTail) currentTail.nextSub = link;
  }

  link.dep.subs = link;
}

link.dep指向Dep1依賴,link.dep.subs指向Dep1依賴隊列的尾部。從前面的圖可以看到此時隊列的尾部是Link1,所以currentTail的值就是Link1

if (currentTail !== link)也就是判斷Link1Link3是否相等,很明顯不相等,就會走到if的里面去。

接著就是執(zhí)行link.prevSub = currentTail,前面講過了此時link就是Link3,currentTail就是Link1。執(zhí)行這行代碼就是將Link3prevSub屬性指向Link1。

接著就是執(zhí)行currentTail.nextSub = link,這行代碼是將Link1nextSub指向Link3。

執(zhí)行完上面這兩行代碼后Link1Link3之間就建立聯(lián)系了,可以通過prevSubnextSub屬性訪問到對方。如下圖:

接著就是執(zhí)行link.dep.subs = link,將Dep1隊列的尾部指向Link3,如下圖:

到這里第一個響應(yīng)式變量counter1進行依賴收集就完成了。

第二個watchEffect對counter2進行讀操作

在第二個watchEffect中接著會對counter2變量進行讀操作。同樣會走到get攔截中,然后執(zhí)行track函數(shù),代碼如下:

class Dep {
  // 指向Link鏈表的尾部節(jié)點
  subs: Link;
  track() {
    let link = new Link(activeSub, this);

    if (!activeSub.deps) {
      activeSub.deps = activeSub.depsTail = link;
    } else {
      link.prevDep = activeSub.depsTail;
      activeSub.depsTail!.nextDep = link;
      activeSub.depsTail = link;
    }

    addSub(link);
  }
}

這里還是會使用new Link(activeSub, this)創(chuàng)建一個Link4節(jié)點,節(jié)點的subdep屬性分別指向Sub2Dep2。如下圖:

此時的activeSub就是Sub2,activeSub.deps就是指向Sub2隊列的頭部。所以此時頭部是指向Link3,代碼會走到else模塊中。

在else中首先會執(zhí)行link.prevDep = activeSub.depsTail,activeSub.depsTail是指向Sub2隊列的尾部,也就是Link3。執(zhí)行完這行代碼后會將Link4prevDep指向Link3

接著就是執(zhí)行activeSub.depsTail!.nextDep = link,前面講過了activeSub.depsTail是指向Link3。執(zhí)行完這行代碼后會將Link3nextDep屬性指向Link4。

執(zhí)行完上面這兩行代碼后Link3Link4之間就建立聯(lián)系了,可以通過nextDepprevDep屬性訪問到對方。如下圖:

接著就是執(zhí)行activeSub.depsTail = link,將Sub2隊列的尾部指向Link4。如下圖:

接著就是執(zhí)行addSub函數(shù)處理Dep2的隊列,代碼如下:

function addSub(link: Link) {
  const currentTail = link.dep.subs;
  if (currentTail !== link) {
    link.prevSub = currentTail;
    if (currentTail) currentTail.nextSub = link;
  }

  link.dep.subs = link;
}

link.dep指向Dep2依賴,link.dep.subs指向Dep2依賴隊列的尾部。從前面的圖可以看到此時隊列的尾部是Link2,所以currentTail的值就是Link2。前面講過了此時link就是Link4,if (currentTail !== link)也就是判斷Link2Link4是否相等,很明顯不相等,就會走到if的里面去。

接著就是執(zhí)行link.prevSub = currentTail,currentTail就是Link2。執(zhí)行這行代碼就是將Link4prevSub屬性指向Link2。

接著就是執(zhí)行currentTail.nextSub = link,這行代碼是將Link2nextSub指向Link4。

執(zhí)行完上面這兩行代碼后Link2Link4之間就建立聯(lián)系了,可以通過prevSubnextSub屬性訪問到對方。如下圖:

最后就是執(zhí)行link.dep.subs = linkDep2隊列的尾部指向Link4,如下圖:

至此整個依賴收集過程就完成了,最終就畫出了Vue新的響應(yīng)式模型。

依賴觸發(fā)

當執(zhí)行counter1.value++時,就會被變量counter1(也就是Dep1依賴)的set函數(shù)攔截。

此時就可以通過Dep1subs屬性指向隊列的尾部,也就是指向Link3。

Link3中可以直接通過sub屬性訪問到訂閱者Sub2,也就是第二個watchEffect,從而執(zhí)行第二個watchEffect的回調(diào)函數(shù)。

接著就是使用Link的preSub屬性從隊尾依次移動到隊頭,從而觸發(fā)Dep1隊列中的所有Sub訂閱者。

在這里就是使用preSub屬性訪問到Link1(就到隊列的頭部啦),Link1中可以直接通過sub屬性訪問到訂閱者Sub1,也就是第一個watchEffect,從而執(zhí)行第一個watchEffect的回調(diào)函數(shù)。

總結(jié)

本文講了Vue新的響應(yīng)式模型,里面主要有三個角色:Dep依賴、Sub訂閱者、Link節(jié)點

Dep依賴Sub訂閱者不再有直接的聯(lián)系,而是通過Link節(jié)點作為橋梁。

依賴收集的過程中會構(gòu)建Dep依賴的隊列,隊列是由Link節(jié)點組成。以及構(gòu)建Sub訂閱者的隊列,隊列同樣是由Link節(jié)點組成。

依賴觸發(fā)時就可以通過Dep依賴的隊列的隊尾出發(fā),Link節(jié)點可以訪問和觸發(fā)對應(yīng)的Sub訂閱者

然后依次從隊尾向隊頭移動,依次觸發(fā)隊列中每個Link節(jié)點Sub訂閱者。

以上就是一文帶你搞懂Vue3.5的響應(yīng)式重構(gòu)的詳細內(nèi)容,更多關(guān)于Vue3.5響應(yīng)式重構(gòu)的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論