Vue3
核心的Typescript
,Proxy
響應式,Composition
解決代碼反覆橫跳都有很棒的文章剖析了, 我總結一下虛擬Dom
部分把,並對比一下React
, vdom
的重寫也是vue3
性能如此優秀的重要緣由javascript
總體尤大直播的過程,幾位大兄弟已經總結的賊棒了 ,直接移步html
先說結論,靜態標記,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出第四個參數出現了別的數字,根據後面註釋也很容易猜出,根據text
,props
等不一樣的標記,這樣再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, // 表示具備動態插槽的元素
}
複製代碼
若是同時有props
和text
的綁定呢, 位運算組合便可
<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,咱們能夠簡單的經過位運算來斷定須要作text
和props
的判斷, 按位與便可,只要不是0就是須要比較
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 */)
]))
}
複製代碼
不少人吐槽愈來愈像React
,其實愈來愈像的api
,表明着前端的兩個方向
沒有vdom
,徹底的響應式,每一個數據變化,都經過響應式通知機制來新建Watcher
幹活,就像獨立團規模小的時候,每一個戰士入伍和升職,都主動通知咱老李,管理方便
項目規模變大後,過多的Watcher
,會致使性能的瓶頸
而React15
時代,沒有響應式,數據變了,整個新數據和老的數據作diff
,算出差別 就知道怎麼去修改dom
了,就像老李指揮室有一個模型,每次人事變動,經過對比全部人先後差別,就知道了變化, 看起來有不少計算量,可是這種immutable
的數據結構對大型項目比較友好,並且Vdom
抽象成功後,換成別的平臺render成爲了可能,不管是打鬼子仍是打國軍,都用一個vdom
模式
碰到的問題同樣,若是dom
節點持續變多,每次diff
的時間超過了16ms
,就可能會形成卡頓(60fps)
引入vdom,控制了顆粒度,組件層面走watcher通知, 組件內部走vdom作diff,既不會有太多watcher,也不會讓vdom的規模過大,diff超過16ms,真是優秀啊 就像獨立團大了之後,只有營長排長級別的變更,纔會通知老李,內部的本身diff管理了
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);
}
}
複製代碼
這裏的靜態提高和事件緩存剛纔說過了,就不說了,其實我也很納悶,這些靜態標記和事件緩存,React
自己也能夠作,爲啥就不實現了,連shouldComponentUpdate
都得本身定義,爲啥不把默認的組件都變成pure
或者memo
呢,唉,也許這就是人生把
React
給你自由,Vue
讓你持久,可能也是如今國內Vue和React都如此受歡迎的緣由把
Vue3
經過Proxy
響應式+組件內部vdom
+靜態標記,把任務顆粒度控制的足夠細緻,因此也不太須要time-slice
了
人生啊,小孩才每天研究利弊, 成年人選擇我都要,也期待React17的新特性
最後提問期間,強如尤大,也無法迴避髮量的問題,惋惜沒推薦啥護髮素,我仔細看了一下,好像vue3發佈後,尤大發際線確實提高了 囧 祝你們技術提高的同時也能有烏黑的秀髮
歡迎點贊關注,個人主要愛好就是摸魚,推薦個摸魚羣把,這篇文章就是我上午摸魚寫出來的, 一塊兒來技術摸魚羣把,最近準備摸魚寫個vue3源碼全剖析系列