​大概幾集下飯劇時間就能懂的 VUE3 原理

大概幾集下飯劇時間就能懂的 VUE3 原理

大家好,我是卡頌。

最近中午沒胃口,找來VUE源碼相關視頻來當下飯劇。幾頓飯下去,人胖了,VUE也整明白了。

這篇文章爲你帶來一份VUE3原理速成指南。

模塊劃分

如果我們用**「VUE 的模版語法」**定義:

<div>hello</div>

最終VUE會幫我們在瀏覽器中渲染對應的DOM節點

這之間對這段節點的描述會經歷 4 次變化,橫跨**「編譯時」**與**「運行時」**:

「模版語法」在編譯時會被「編譯器」轉化爲「render 函數」,類似:

render(h) {
  return h('div''hello');
}

在運行時,**「render 函數」執行後返回的「h 函數的執行結果」**就是VNode(也就是虛擬DOM),類似:

{
  tag: "div",
  children: [
    {
      text: "Hello"
    }
  ]
}

最終,VUE根據VNode的信息,在瀏覽器渲染對應DOM

那麼,是誰在驅動這一流程?

mount 和 patch

組件有兩種不同的渲染邏輯:「首次渲染」「更新」

**「首次渲染」**意味着從無到有,比如上文的VNode

{
  tag: "div",
  children: [
    {
      text: "Hello"
    }
  ]
}

可能對應如下DOM操作:

const node = document.createElement(VNode.tag);
node.textConent = 'Hello';
contanerDOM.appendChild(node);

**「更新」**則需要對比更新前後VNode,對變化部分執行DOM操作。

比如,以上VNode如果變爲:

{
  tag: "div",
  children: [
    {
      // text改變
      text: "world"
    }
  ]
}

則最終執行:

node.textContent = 'world';

VUE的**「首次渲染」**對應mount模塊,**「更新」**對應patch模塊。

所以,render函數執行後返回VNode,根據情況不同,會走mountpatch的渲染邏輯:

如果想深入虛擬DOM相關知識,推薦閱讀 snabbdom[1] 源碼。這是個優秀的虛擬 DOM 庫,VUE2虛擬DOM部分就是fork這個庫改造的。

那麼是誰在什麼時機調用了render函數呢?

響應式更新

VUE中,狀態變化會實時反映到視圖上,比如:

<div @click="count++">{{count}}</div>

點擊div後:

  1. 觸發點擊事件,count變化

  2. count變化觸發回調,回調中更新視圖

當前我們已經知道第二步是由於觸發瞭如下流程:

所以只需要建立count變化到執行render函數的聯繫即可。

具體來說,我們希望實現reactivewatchEffect

// 定義狀態
const state = reactive({count: 0});

// 監聽狀態變化
watchEffect(() ={
  console.log(state.count);
})

// 改變狀態
state.count++;

reactive定義狀態。

watchEffect根據回調執行的情況決定監聽哪些狀態。

比如watchEffect回調執行了console.log(state.count);,他就會監聽state的變化。

當執行state.count++;,由於watchEffect監聽了state的變化,則其回調會觸發,打印state.count

這就是Reactivity模塊。

VUE 官方推出了 VUE3 響應式原理 [2] 課程講解Reactivity的實現,這是 B 站鏈接。如果經濟允許,請支持正版 [3]

當實現了Reactivity模塊,我們就能將**「組件狀態」**與後續流程串聯起來。

剛纔講過,render函數是編譯器根據**「模版語法」**生成的。在面對帶狀態的模版語法時,比如上文的count

<div @click="count++">{{count}}</div>

render函數內的count是響應式的(即:count實際是reactive({count: 0}))。

那麼就能用watchEffect監聽count的變化。

所以,在應用初始化時,會有類似邏輯:

let isMounted = false;
let oldVNode;
watchEffect(() ={
  if (!isMounted) {
    // mount邏輯
    // 調用render函數
    oldVNode = component.render();
    // mount
    mount(oldVNode);
  } else {
    // patch邏輯
    // 調用render函數
    newVNode = component.render();
    patch(oldVNode, newVNode);
    oldVNode = newVNode;
  }
})

其中component.render()(render 函數的執行)達到上文**「監聽狀態變化」**的效果:

// 監聽狀態變化
watchEffect(() ={
  console.log(state.count);
})

所以,該組件內任何狀態變化都會觸發watchEffect的執行,watchEffect回調內會觸發後續流程。

總結

VUE3按原理大體可以劃分爲:

VUE官方推出了實現簡易 VUE3 教程 [4],感興趣的朋友可以去看看。如果有能力,記得去支持正版 [5] 哦。

參考資料

[1]

snabbdom: https://github.com/snabbdom/snabbdom

[2]

VUE3 響應式原理: https://www.bilibili.com/video/BV1SZ4y1x7a9/?spm_id_from=333.788.b_7265636f5f6c697374.6

[3]

正版: https://www.vuemastery.com/free-weekend/?gclid=Cj0KCQjwpreJBhDvARIsAF1_BU16x7gElbhGqGzZZ1geo5RzOqz_PuaJzBM41jHcAAC6CPwPSPvo8G8aAkdhEALw_wcB

[4]

實現簡易 VUE3 教程: https://www.bilibili.com/video/BV1rC4y187Vw?p=10

[5]

正版: https://www.vuemastery.com/free-weekend/?gclid=Cj0KCQjwpreJBhDvARIsAF1_BU16x7gElbhGqGzZZ1geo5RzOqz_PuaJzBM41jHcAAC6CPwPSPvo8G8aAkdhEALw_wcB

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/fsyqkj5FhjHxzMLw9MgjZQ