尤大Vue3.0直播虛擬Dom總結(和React對比)

Vue3核心的TypescriptProxy響應式,Composition解決代碼反覆橫跳都有很棒的文章剖析了, 我總結一下虛擬Dom部分把,並對比一下Reactvdom的重寫也是vue3性能如此優秀的重要緣由javascript

總體尤大直播的過程,幾位大兄弟已經總結的賊棒了 ,直接移步html

  1. 瞭解Vue3源碼這一篇就夠了
  2. Vue 3.0 這個迷人的小妖精,到底好在哪裏?(更新原理對比)
  3. 抄筆記:尤雨溪在Vue3.0 Beta直播裏聊到了這些…

Vue3的虛擬dom

先說結論,靜態標記,upadte性能提高1.3~2倍,ssr提高2~3倍,怎麼作到的呢前端

編譯模板的靜態標記

咱們來看一段很常見的代碼vue

<div id="app">
    <h1>技術摸魚</h1>
    <p>今每天氣真不錯</p>
    <div>{{name}}</div>

</div>

複製代碼

vue2中會解析java

function render() {
  with(this) {
    return _c('div', {
      attrs: {
        "id": "app"
      }
    }, [_c('h1', [_v("技術摸魚")]), _c('p', [_v("今每天氣真不錯")]), _c('div', [_v(
      _s(name))])])
  }
}

複製代碼

其中前面兩個標籤是徹底靜態的,後續的渲染中不會產生任何變化,Vue2中依然使用_c新建成vdom,在diff的時候須要對比,有一些額外的性能損耗react

咱們看下vue3中的解析結果git

import { createVNode as _createVNode, toDisplayString as _toDisplayString, openBlock as _openBlock, createBlock as _createBlock } from "vue"

export function render(_ctx, _cache) {
  return (_openBlock(), _createBlock("div", { id: "app" }, [
    _createVNode("h1", null, "技術摸魚"),
    _createVNode("p", null, "今每天氣真不錯"),
    _createVNode("div", null, _toDisplayString(_ctx.name), 1 /* TEXT */)
  ]))
}
// Check the console for the AST

複製代碼

最後一個_createVNode第四個參數1,只有帶這個參數的,纔會被真正的追蹤,靜態節點不須要遍歷,這個就是vue3優秀性能的主要來源,再看複雜一點的github

<div id="app">
    <h1>技術摸魚</h1>
    <p>今每天氣真不錯</p>

    <div>{{name}}</div>
    <div :class="{red:isRed}">摸魚符</div>
    <button @click="handleClick">戳我</button>
    <input type="text" v-model="name">
    
</div>

複製代碼

解析的結果 在線預覽api

import { createVNode as _createVNode, toDisplayString as _toDisplayString, openBlock as _openBlock, createBlock as _createBlock } from "vue"

export function render(_ctx, _cache) {
  return (_openBlock(), _createBlock("div", { id: "app" }, [
    _createVNode("h1", null, "技術摸魚"),
    _createVNode("p", null, "今每天氣真不錯"),
    _createVNode("div", null, _toDisplayString(_ctx.name), 1 /* TEXT */),
    _createVNode("div", {
      class: {red:_ctx.isRed}
    }, "摸魚符", 2 /* CLASS */),
    _createVNode("button", { onClick: _ctx.handleClick }, "戳我", 8 /* PROPS */, ["onClick"])
  ]))
}

// Check the console for the AST

複製代碼

_createVNode出第四個參數出現了別的數字,根據後面註釋也很容易猜出,根據textprops等不一樣的標記,這樣再diff的時候,只須要對比text或者props,不用再作無畏的props遍歷, 優秀! 借鑑一下勸退大兄弟的註釋瀏覽器

export const enum PatchFlags {
  
  TEXT = 1,// 表示具備動態textContent的元素
  CLASS = 1 << 1,  // 表示有動態Class的元素
  STYLE = 1 << 2,  // 表示動態樣式(靜態如style="color: red",也會提高至動態)
  PROPS = 1 << 3,  // 表示具備非類/樣式動態道具的元素。
  FULL_PROPS = 1 << 4,  // 表示帶有動態鍵的道具的元素,與上面三種相斥
  HYDRATE_EVENTS = 1 << 5,  // 表示帶有事件監聽器的元素
  STABLE_FRAGMENT = 1 << 6,   // 表示其子順序不變的片斷(沒懂)。 
  KEYED_FRAGMENT = 1 << 7, // 表示帶有鍵控或部分鍵控子元素的片斷。
  UNKEYED_FRAGMENT = 1 << 8, // 表示帶有無key綁定的片斷
  NEED_PATCH = 1 << 9,   // 表示只須要非屬性補丁的元素,例如ref或hooks
  DYNAMIC_SLOTS = 1 << 10,  // 表示具備動態插槽的元素
}
複製代碼

若是同時有propstext的綁定呢, 位運算組合便可

<div id="app">
    <h1>技術摸魚</h1>
    <p>今每天氣真不錯</p>
    <div :id="userid"">{{name}}</div>
</div>


複製代碼
import { createVNode as _createVNode, toDisplayString as _toDisplayString, openBlock as _openBlock, createBlock as _createBlock } from "vue"

export function render(_ctx, _cache) {
  return (_openBlock(), _createBlock("div", { id: "app" }, [
    _createVNode("h1", null, "技術摸魚"),
    _createVNode("p", null, "今每天氣真不錯"),
    _createVNode("div", {
      id: _ctx.userid,
      "\"": ""
    }, _toDisplayString(_ctx.name), 9 /* TEXT, PROPS */, ["id"])
  ]))
}

// Check the console for the AST

複製代碼

text是1,props是8,組合在一塊兒就是9,咱們能夠簡單的經過位運算來斷定須要作textprops的判斷, 按位與便可,只要不是0就是須要比較

位運算來作類型組合 自己就是一個最佳實踐,react大兄弟也是同樣的 代碼

export const PLUGIN_EVENT_SYSTEM = 1;
export const RESPONDER_EVENT_SYSTEM = 1 << 1;
export const USE_EVENT_SYSTEM = 1 << 2;
export const IS_TARGET_PHASE_ONLY = 1 << 3;
export const IS_PASSIVE = 1 << 4;
export const PASSIVE_NOT_SUPPORTED = 1 << 5;
export const IS_REPLAYED = 1 << 6;
export const IS_FIRST_ANCESTOR = 1 << 7;
export const LEGACY_FB_SUPPORT = 1 << 8;

複製代碼

事件緩存

綁定的@click會存在緩存裏 連接

<div id="app">
  <button @click="handleClick">戳我</button>
</div>

複製代碼
export function render(_ctx, _cache) {
  return (_openBlock(), _createBlock("div", { id: "app" }, [
    _createVNode("button", {
      onClick: _cache[1] || (_cache[1] = $event => (_ctx.handleClick($event)))
    }, "戳我")
  ]))
}

複製代碼

傳入的事件會自動生成並緩存一個內聯函數再cache裏,變爲一個靜態節點。這樣就算咱們本身寫內聯函數,也不會致使多餘的重複渲染 真是優秀啊

靜態提高

代碼

<div id="app">
    <h1>技術摸魚</h1>
    <p>今每天氣真不錯</p>
    <div>{{name}}</div>
    <div :class="{red:isRed}">摸魚符</div>
</div>

複製代碼
const _hoisted_1 = { id: "app" }
const _hoisted_2 = _createVNode("h1", null, "技術摸魚", -1 /* HOISTED */)
const _hoisted_3 = _createVNode("p", null, "今每天氣真不錯", -1 /* HOISTED */)

export function render(_ctx, _cache) {
  return (_openBlock(), _createBlock("div", _hoisted_1, [
    _hoisted_2,
    _hoisted_3,
    _createVNode("div", null, _toDisplayString(_ctx.name), 1 /* TEXT */),
    _createVNode("div", {
      class: {red:_ctx.isRed}
    }, "摸魚符", 2 /* CLASS */)
  ]))
}

複製代碼

vue3和react fiber的vdom

不少人吐槽愈來愈像React,其實愈來愈像的api,表明着前端的兩個方向

Vue1.x

沒有vdom,徹底的響應式,每一個數據變化,都經過響應式通知機制來新建Watcher幹活,就像獨立團規模小的時候,每一個戰士入伍和升職,都主動通知咱老李,管理方便

項目規模變大後,過多的Watcher,會致使性能的瓶頸

React15x

React15時代,沒有響應式,數據變了,整個新數據和老的數據作diff,算出差別 就知道怎麼去修改dom了,就像老李指揮室有一個模型,每次人事變動,經過對比全部人先後差別,就知道了變化, 看起來有不少計算量,可是這種immutable的數據結構對大型項目比較友好,並且Vdom抽象成功後,換成別的平臺render成爲了可能,不管是打鬼子仍是打國軍,都用一個vdom模式

碰到的問題同樣,若是dom節點持續變多,每次diff的時間超過了16ms,就可能會形成卡頓(60fps)

Vue2.x

引入vdom,控制了顆粒度,組件層面走watcher通知, 組件內部走vdom作diff,既不會有太多watcher,也不會讓vdom的規模過大,diff超過16ms,真是優秀啊 就像獨立團大了之後,只有營長排長級別的變更,纔會通知老李,內部的本身diff管理了

React 16 Fiber

React走了另一條路,既然主要問題是diff致使卡頓,因而React走了相似cpu調度的邏輯,把vdom這棵樹,微觀變成了鏈表,利用瀏覽器的空閒時間來作diff,若是超過了16ms,有動畫或者用戶交互的任務,就把主進程控制權還給瀏覽器,等空閒了繼續,特別像等待女神的備胎

diff的邏輯,變成了單向的鏈表,任什麼時候候主線程女神有空了,咱們在繼續蹭上去接盤作diff,你們研究下requestIdleCallback就知道,從瀏覽器角度看 是這樣的

大概代碼

requestIdelCallback(myNonEssentialWork);
// 等待女神空閒
function myNonEssentialWork (deadline) {
    // deadline.timeRemaining()>0 主線程女神還有事件
    // 還有diff任務沒算玩
    while (deadline.timeRemaining() > 0 && tasks.length > 0) {
    doWorkIfNeeded();
    }
    // 女神沒時間了,把女神還回去🤣
    if (tasks.length > 0){
        requestIdleCallback(myNonEssentialWork);
    }
}

複製代碼

Vue3

這裏的靜態提高和事件緩存剛纔說過了,就不說了,其實我也很納悶,這些靜態標記和事件緩存,React自己也能夠作,爲啥就不實現了,連shouldComponentUpdate都得本身定義,爲啥不把默認的組件都變成pure或者memo呢,唉,也許這就是人生把

React給你自由,Vue讓你持久,可能也是如今國內Vue和React都如此受歡迎的緣由把

Vue3經過Proxy響應式+組件內部vdom+靜態標記,把任務顆粒度控制的足夠細緻,因此也不太須要time-slice

人生啊,小孩才每天研究利弊, 成年人選擇我都要,也期待React17的新特性

直播小細節

最後提問期間,強如尤大,也無法迴避髮量的問題,惋惜沒推薦啥護髮素,我仔細看了一下,好像vue3發佈後,尤大發際線確實提高了 囧 祝你們技術提高的同時也能有烏黑的秀髮

小廣告

歡迎點贊關注,個人主要愛好就是摸魚,推薦個摸魚羣把,這篇文章就是我上午摸魚寫出來的, 一塊兒來技術摸魚羣把,最近準備摸魚寫個vue3源碼全剖析系列

相關文章
相關標籤/搜索