前言
在Vue3.5版本中最大的改動就是響應式重構
,重構後效能竟然炸裂的提升了56%
。之所以重構後的響應式效能提升幅度有這麼大,主要還是歸功於:雙向連結串列
和版本計數
。這篇文章我們來講講使用雙向連結串列
後,Vue內部是如何實現依賴收集
和依賴觸發
的。搞懂了這個之後你就能掌握Vue3.5重構後的響應式原理,至於版本計數
如果大家感興趣可以在評論區留言,關注的人多了歐陽後面會再寫一篇版本計數
的文章。
關注公眾號:【前端歐陽】,給自己一個進階vue的機會
3.5版本以前的響應式
在Vue3.5以前的響應式中主要有兩個角色:Sub
(訂閱者)、Dep
(依賴)。其中的訂閱者有watchEffect、watch、render函式、computed等。依賴有ref、reactive等響應式變數。
舉個例子:
<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中都會去監聽ref響應式變數:counter1
和counter2
。
初始化時會分別執行這兩個watchEffect中的回撥函式,所以就會對裡面的響應式變數counter1
和counter2
進行讀操作
,所以就會走到響應式變數的get攔截中。
在get攔截中會進行依賴收集(此時的Dep依賴分別是變數counter1
和counter2
)。
因為在依賴收集期間是在執行watchEffect
中的回撥函式,所以依賴對應的Sub訂閱者
就是watchEffect。
由於這裡有兩個watchEffect,所以這裡有兩個Sub訂閱者
,分別對應這兩個watchEffect。
在上面的例子中,watchEffect監聽了多個ref變數。也就是說,一個Sub訂閱者
(也就是一個watchEffect)可以訂閱多個依賴。
ref響應式變數counter1
被多個watchEffect給監聽。也就是說,一個Dep依賴
(也就是counter1
變數)可以被多個訂閱者給訂閱。
Sub訂閱者
和Dep依賴
他們兩的關係是多對多
的關係!!!
上面這個就是以前的響應式模型。
新的響應式模型
在Vue3.5版本新的響應式中,Sub訂閱者和Dep依賴之間不再有直接的聯絡,而是新增了一個Link作為橋樑。Sub訂閱者透過Link訪問到Dep依賴,同理Dep依賴也是透過Link訪問到Sub訂閱者。如下圖:
把上面這個圖看懂了,你就能理解Vue新的響應式系統啦。現在你直接看這個圖有可能看不懂,沒關係,等我講完後你就能看懂了。
首先從上圖中可以看到Sub訂閱者和Dep依賴之間沒有任何直接的連線關係了,也就是說Sub訂閱者不能直接訪問到Dep依賴,Dep依賴也不能直接訪問Sub訂閱者。
Dep依賴我們可以看作是X軸,Sub訂閱者可以看作是Y軸,這些Link就是座標軸上面的座標。
Vue響應式系統的核心還是沒有變,只是多了一個Link,依然還是以前的那一套依賴收集
和依賴觸發
的流程。
在依賴收集
的過程中就會畫出上面這個圖,這個不要急,我接下來會仔細去講圖是如何畫出來的。
那麼依賴觸發的時候又是如何利用上面這種圖從而實現觸發依賴的呢?我們來看個例子。
上面的這張圖其實對應的是我之前舉的例子:
<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依賴
對應的就是變數counter1
,Dep2依賴
對應的就是變數counter2
。Sub1訂閱者
對應的就是第一個watchEffect
函式,Sub2訂閱者
對應的就是第二個watchEffect
函式。
當執行counter1.value++
時,就會被變數counter1
(也就是Dep1依賴
)的set函式攔截。從上圖中可以看到Dep1依賴
有個箭頭(對照表中的sub
屬性)指向Link3
,並且Link3
也有一個箭頭(對照表中的sub
屬性)指向Sub2
。
前面我們講過了這個Sub2
就是對應的第二個watchEffect
函式,指向Sub2
後我們就可以執行Sub2
中的依賴,也就是執行第二個watchEffect
函式。這就實現了counter1.value++
變數改變後,重新執行第二個watchEffect
函式。
執行了第二個watchEffect
函式後我們發現Link3
在Y軸上面還有一個箭頭(對照表中的preSub
屬性)指向了Link1
。同理Link1
也有一個箭頭(對照表中的sub
屬性)指向了Sub1
。
前面我們講過了這個Sub1
就是對應的第一個watchEffect
函式,指向Sub1
後我們就可以執行Sub1
中的依賴,也就是執行第一個watchEffect
函式。這就實現了counter1.value++
變數改變後,重新執行第一個watchEffect
函式。
至此我們就實現了counter1.value++
變數改變後,重新去執行依賴他的兩個watchEffect
函式。
我們此時再來回顧一下我們前面畫的新的響應式模型圖,如下圖:
我們從這張圖來總結一下依賴觸發的的規則:
響應式變數Dep1
改變後,首先會指向Y軸(Sub訂閱者
)的隊尾
的Link節點。然後從Link節點可以直接訪問到Sub訂閱者,訪問到訂閱者後就可以觸發其依賴,這裡就是重新執行對應的watchEffect
函式。
接著就是順著Y軸的隊尾
向隊頭
移動,每移動到一個新的Link節點都可以指向一個新的Dep依賴,在這裡觸發其依賴就會重新指向對應的watchEffect
函式。
看到這裡有的同學有疑問如果是Dep2
對應的響應式變數改變後指向Link4
,那這個Link4
又是怎麼指向Sub2
的呢?他們中間不是還隔了一個Link3
嗎?
每一個Link節點上面都有一個sub
屬性直接指向Y軸上面的Sub依賴,所以這裡的Link4
有個箭頭(對照表中的sub
屬性)可以直接指向Sub2
,然後進行依賴觸發。
這就是Vue3.5版本使用雙向連結串列
改進後的依賴觸發原理,接下來我們會去講依賴收集過程中是如何將上面的模型圖畫出來的。
Dep、Sub和Link
在講Vue3.5版本依賴收集之前,我們先來了解一下新的響應式系統中主要的三個角色:Dep依賴
、Sub訂閱者
、Link節點
。
這三個角色其實都是class類,依賴收集和依賴觸發的過程中實際就是在操作這些類new出來的的物件。
我們接下來看看這些類中有哪些屬性和方法,其實在前面的響應式模型圖中我們已經使用箭頭標明瞭這些類上面的屬性。
Dep依賴
簡化後的Dep
類定義如下:
class Dep {
// 指向Link連結串列的尾部節點
subs: Link
// 收集依賴
track: Function
// 觸發依賴
trigger: Function
}
Dep依賴上面的subs
屬性就是指向佇列的尾部
,也就是佇列中最後一個Sub訂閱者對應的Link節點。
比如這裡的Dep1
,豎向的Link1
和Link3
就組成了一個佇列。其中Link3
是佇列的隊尾,Dep1
的subs
屬性就是指向Link3
。
其次就是track
函式,對響應式變數進行讀操作時會觸發。觸發這個函式後會進行依賴收集,後面我會講。
同樣trigger
函式用於依賴觸發,對響應式變數進行寫操作時會觸發,後面我也會講。
Sub訂閱者
簡化後的Sub
訂閱者定義如下:
interface Subscriber {
// 指向Link連結串列的頭部節點
deps: Link
// 指向Link連結串列的尾部節點
depsTail: Link
// 執行依賴
notify: Function
}
想必細心的你發現了這裡的Subscriber
是一個interface
介面,而不是一個class類。因為實現了這個Subscriber
介面的class類都是訂閱者,比如watchEffect、watch、render函式、computed等。
比如這裡的Sub1
,橫向的Link1
和Link2
就組成一個佇列。其中的隊尾就是Link2
(depsTail
屬性),隊頭就是Link1
(deps
屬性)。
還有就是notify
函式,執行這個函式就是在執行依賴。比如對於watchEffect來說,執行notify
函式後就會執行watchEffect的回撥函式。
Link節點
簡化後的Link
節點類定義如下:
class Link {
// 指向Subscriber訂閱者
sub: Subscriber
// 指向Dep依賴
dep: Dep
// 指向Link連結串列的後一個節點(X軸)
nextDep: Link
// 指向Link連結串列的前一個節點(X軸)
prevDep: Link
// 指向Link連結串列的下一個節點(Y軸)
nextSub: Link
// 指向Link連結串列的上一個節點(Y軸)
prevSub: Link
}
前面我們講過了新的響應式模型中Dep依賴
和Sub訂閱者
之間不會再有直接的關聯,而是透過Link作為橋樑。
那麼作為橋樑的Link節點肯定需要有兩個屬效能夠讓他直接訪問到Dep依賴
和Sub訂閱者
,也就是sub
和dep
屬性。
其中的sub
屬性是指向Sub訂閱者
,dep
屬性是指向Dep依賴
。
我們知道Link是座標軸的點,那這個點肯定就會有上、下、左、右四個方向。
比如對於Link1
可以使用nextDep
屬性來訪問後面這個節點Link2
,Link2
可以使用prevDep
屬性來訪問前面這個節點Link1
。
請注意,這裡名字雖然叫nextDep
和prevDep
,但是他們指向的卻是Link節點。然後透過這個Link節點的dep
屬性,就可以訪問到後一個Dep依賴
或者前一個Dep依賴
。
同理對於Link1
可以使用nextSub
訪問後面這個節點Link3
,Link3
可以使用prevSub
訪問前面這個節點Link1
。
同樣的這裡名字雖然叫nextSub
和prevSub
,但是他們指向的卻是Link節點。然後透過這個Link節點的sub
屬性,就可以訪問到下一個Sub訂閱者
或者上一個Sub訂閱者
。
如何收集依賴
搞清楚了新的響應式模型中的三個角色:Dep依賴
、Sub訂閱者
、Link節點
,我們現在就可以開始搞清楚新的響應式模型是如何收集依賴的。
接下來我將會帶你如何一步步的畫出前面講的那張新的響應式模型圖。
還是我們前面的那個例子,程式碼如下:
<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>
大家都知道響應式變數有get
和set
攔截,當對變數進行讀操作時會走到get
攔截中,進行寫操作時會走到set
攔截中。
上面的例子第一個watchEffect
我們叫做Sub1
訂閱者,第二個watchEffect
叫做Sub2
訂閱者.
初始化時watchEffect
中的回撥會執行一次,這裡有兩個watchEffect
,會依次去執行。
在Vue內部有個全域性變數叫activeSub
,裡面存的是當前active的Sub訂閱者。
執行第一個watchEffect
回撥時,當前的activeSub
就是Sub1
。
在Sub1
中使用到了響應式變數counter1
和counter2
,所以會對這兩個變數依次進行讀操作。
第一個watchEffect
對counter1
進行讀操作
先對counter1
進行讀操作時,會走到get
攔截中。核心程式碼如下:
class RefImpl {
get value() {
this.dep.track();
return this._value;
}
}
從上面可以看到在get攔截中直接呼叫了dep依賴的track
方法進行依賴收集。
在執行track
方法之前我們思考一下當前響應式系統中有哪些角色,分別是Sub1
和Sub2
這兩個watchEffect
回撥函式訂閱者,以及counter1
和counter2
這兩個Dep依賴。此時的響應式模型如下圖:
從上圖可以看到此時只有X座標軸的Dep依賴,以及Y座標軸的Sub訂閱者,沒有一個Link節點。
我們接著來看看dep依賴的track
方法,核心程式碼如下:
class Dep {
// 指向Link連結串列的尾部節點
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);
}
}
從上面的程式碼可以看到,每執行一次track
方法,也就是說每次收集依賴,都會執行new Link
去生成一個Link節點。
並且傳入兩個引數,activeSub
為當前active的訂閱者,在這裡就是Sub1
(第一個watchEffect
)。第二個引數為this
,指向當前的Dep依賴物件,也就是Dep1
(counter1
變數)。
先不看track
後面的程式碼,我們來看看Link
這個class的程式碼,核心程式碼如下:
class Link {
// 指向Link連結串列的後一個節點(X軸)
nextDep: Link;
// 指向Link連結串列的前一個節點(X軸)
prevDep: Link;
// 指向Link連結串列的下一個節點(Y軸)
nextSub: Link;
// 指向Link連結串列的上一個節點(Y軸)
prevSub: Link;
- constructor(public sub: Subscriber, public dep: Dep) {
// ...省略
}
}
細心的小夥伴可能發現了在Link
中沒有宣告sub
和dep
屬性,那麼為什麼前面我們會說Link節點中的sub
和dep
屬性分別指向Sub訂閱者和Dep依賴呢?
因為在constructor建構函式中使用了public
關鍵字,所以sub
和dep
就作為屬性暴露出來了。
執行完let link = new Link(activeSub, this)
後,在響應式系統模型中初始化出來第一個Link節點,如下圖:
從上圖可以看到Link1
的sub
屬性指向Sub1
訂閱者,dep
屬性指向Dep1
依賴。
我們接著來看track
方法中剩下的程式碼,如下:
class Dep {
// 指向Link連結串列的尾部節點
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
就是Sub1
的deps
屬性,也就是Sub1
佇列上的第一個Link。
從上圖中可以看到此時的Sub1
並沒有箭頭指向Link1
,所以if (!activeSub.deps)
為true,程式碼會執行
activeSub.deps = activeSub.depsTail = link;
deps
和depsTail
屬性分別指向Sub1
佇列的頭部和尾部,當前佇列中只有Link1
這一個節點,那麼頭部和尾部當然都指向Link1
。
執行完這行程式碼後響應式模型圖就變成下面這樣的了,如下圖:
從上圖中可以看到Sub1
的佇列中只有Link1
這一個節點,所以佇列的頭部和尾部都指向Link1
。
處理完Sub1
的佇列,但是Dep1
的佇列還沒處理,Dep1
的佇列是由addSub(link)
函式處理的。addSub
函式程式碼如下:
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節點,所以此時在addSub
函式中主要是執行第三塊程式碼:link.dep.subs = link
。`
link.dep
是指向Dep1
,前面我們講過了Dep依賴的subs
屬性指向佇列的尾部。所以link.dep.subs = link
就是將Link1
指向Dep1
的佇列的尾部,執行完這行程式碼後響應式模型圖就變成下面這樣的了,如下圖:
到這裡對第一個響應式變數counter1
進行讀操作進行的依賴收集就完了。
第一個watchEffect
對counter2
進行讀操作
在第一個watchEffect中接著會對counter2
變數進行讀操作。同樣會走到get
攔截中,然後執行track
函式,程式碼如下:
class Dep {
// 指向Link連結串列的尾部節點
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)
,然後把新生成的Link2
的sub
和dep
屬性分別指向Sub1
和Dep2
。執行後的響應式模型圖如下圖:
從上面的圖中可以看到此時Sub1
的deps
屬性是指向Link1
的,所以這次程式碼會走進else
模組中。else
部分程式碼如下:
link.prevDep = activeSub.depsTail;
activeSub.depsTail.nextDep = link;
activeSub.depsTail = link;
activeSub.depsTail
指向Sub1
佇列尾部的Link,值是Link1
。所以執行link.prevDep = activeSub.depsTail
就是將Link2
的prevDep
屬性指向Link1
。
同理activeSub.depsTail.nextDep = link
就是將Link1
的nextDep
屬性指向Link2
,執行完這兩行程式碼後Link1
和Link2
之間就建立關係了。如下圖:
從上圖中可以看到此時Link1
和Link2
之間就有箭頭連線,可以互相訪問到對方。
最後就是執行activeSub.depsTail = link
,這行程式碼是將Sub1
佇列的尾部指向Link2
。執行完這行程式碼後模型圖如下:
Sub1
訂閱者的佇列就處理完了,接著就是處理Dep2
依賴的佇列。Dep2
的處理方式和Dep1
是一樣的,讓Dep2
佇列的隊尾指向Link2
,處理完了後模型圖如下:
到這裡第一個watchEffect(也就是Sub1
)對其依賴的兩個響應式變數counter1
(也就是Dep1
)和counter2
(也就是Dep2
),進行依賴收集的過程就執行完了。
第二個watchEffect
對counter1
進行讀操作
接著我們來看第二個watchEffect
,同樣的還是會對counter1
進行讀操作。然後觸發其get
攔截,接著執行track
方法。回憶一下track
方法的程式碼,如下:
class Dep {
// 指向Link連結串列的尾部節點
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)
建立一個Link3
節點,節點的sub
和dep
屬性分別指向Sub2
和Dep1
。如下圖:
同樣的Sub2
佇列上此時還沒任何值,所以if (!activeSub.deps)
為true,和之前一樣會去執行activeSub.deps = activeSub.depsTail = link;
將Sub2
佇列的頭部和尾部都設定為Link3
。如下圖:
處理完Sub2
佇列後就應該呼叫addSub
函式來處理Dep1
的佇列了,回憶一下addSub
函式,程式碼如下:
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)
也就是判斷Link1
和Link3
是否相等,很明顯不相等,就會走到if的裡面去。
接著就是執行link.prevSub = currentTail
,前面講過了此時link
就是Link3
,currentTail
就是Link1
。執行這行程式碼就是將Link3
的prevSub
屬性指向Link1
。
接著就是執行currentTail.nextSub = link
,這行程式碼是將Link1
的nextSub
指向Link3
。
執行完上面這兩行程式碼後Link1
和Link3
之間就建立聯絡了,可以透過prevSub
和nextSub
屬性訪問到對方。如下圖:
接著就是執行link.dep.subs = link
,將Dep1
佇列的尾部指向Link3
,如下圖:
到這裡第一個響應式變數counter1
進行依賴收集就完成了。
第二個watchEffect
對counter2
進行讀操作
在第二個watchEffect中接著會對counter2
變數進行讀操作。同樣會走到get
攔截中,然後執行track
函式,程式碼如下:
class Dep {
// 指向Link連結串列的尾部節點
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)
建立一個Link4
節點,節點的sub
和dep
屬性分別指向Sub2
和Dep2
。如下圖:
此時的activeSub
就是Sub2
,activeSub.deps
就是指向Sub2
佇列的頭部。所以此時頭部是指向Link3
,程式碼會走到else模組中。
在else中首先會執行link.prevDep = activeSub.depsTail
,activeSub.depsTail
是指向Sub2
佇列的尾部,也就是Link3
。執行完這行程式碼後會將Link4
的prevDep
指向Link3
。
接著就是執行activeSub.depsTail!.nextDep = link
,前面講過了activeSub.depsTail
是指向Link3
。執行完這行程式碼後會將Link3
的nextDep
屬性指向Link4
。
執行完上面這兩行程式碼後Link3
和Link4
之間就建立聯絡了,可以透過nextDep
和prevDep
屬性訪問到對方。如下圖:
接著就是執行activeSub.depsTail = link
,將Sub2
佇列的尾部指向Link4
。如下圖:
接著就是執行addSub
函式處理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)
也就是判斷Link2
和Link4
是否相等,很明顯不相等,就會走到if的裡面去。
接著就是執行link.prevSub = currentTail
,currentTail
就是Link2
。執行這行程式碼就是將Link4
的prevSub
屬性指向Link2
。
接著就是執行currentTail.nextSub = link
,這行程式碼是將Link2
的nextSub
指向Link4
。
執行完上面這兩行程式碼後Link2
和Link4
之間就建立聯絡了,可以透過prevSub
和nextSub
屬性訪問到對方。如下圖:
最後就是執行link.dep.subs = link
將Dep2
佇列的尾部指向Link4
,如下圖:
至此整個依賴收集過程就完成了,最終就畫出了Vue新的響應式模型。
依賴觸發
當執行counter1.value++
時,就會被變數counter1
(也就是Dep1依賴
)的set函式攔截。
此時就可以透過Dep1
的subs
屬性指向佇列的尾部,也就是指向Link3
。
Link3
中可以直接透過sub
屬性訪問到訂閱者Sub2
,也就是第二個watchEffect
,從而執行第二個watchEffect
的回撥函式。
接著就是使用Link的preSub
屬性從隊尾依次移動到隊頭,從而觸發Dep1
佇列中的所有Sub訂閱者。
在這裡就是使用preSub
屬性訪問到Link1
(就到佇列的頭部啦),Link1
中可以直接透過sub
屬性訪問到訂閱者Sub1
,也就是第一個watchEffect
,從而執行第一個watchEffect
的回撥函式。
總結
這篇文章講了Vue新的響應式模型,裡面主要有三個角色:Dep依賴
、Sub訂閱者
、Link節點
。
Dep依賴
和Sub訂閱者
不再有直接的聯絡,而是透過Link節點
作為橋樑。
依賴收集的過程中會構建Dep依賴
的佇列,佇列是由Link節點
組成。以及構建Sub訂閱者
的佇列,佇列同樣是由Link節點
組成。
依賴觸發時就可以透過Dep依賴
的佇列的隊尾出發,Link節點
可以訪問和觸發對應的Sub訂閱者
。
然後依次從隊尾向隊頭移動,依次觸發佇列中每個Link節點
的Sub訂閱者
。
關注公眾號:【前端歐陽】,給自己一個進階vue的機會
另外歐陽寫了一本開源電子書vue3編譯原理揭秘,看完這本書可以讓你對vue編譯的認知有質的提升。這本書初、中級前端能看懂,完全免費,只求一個star。