1. Vue.js 2.x 通過標記靜態節點,優化 diff 的過程
2. Vue.js 3.x 通過標記和提升所有的靜態根節點,diff 的時候只需要對比動態節點內容
- Fragments ( 升級 Vetur 插件 )
- 靜態提升
- Patch flag
- 緩沖事件處理函數
詳細解釋:
此處我們用到線上編譯器來查看 vue 2.x 與 vue3.x 的編譯區別~
1. 首先看一下,當文件內部,不包含任何內容時,Vue2.x 編譯是空的,Vue 3.x 編譯內部包含 render 函數,返回為null
2. 我們先放入一個 Dom, 可以看到 vue2.x 和 vue 3.x 編譯的部分完完全全重構了,之前 Vue2.x 采用,_c 的模式創建標簽,_v 為 Vnode 節點, 而當前的 Vue 3.x 通過 _createBlock 生成 block tree
- Vue 2.x 數據更新並觸發重新渲染的粒度是組件級的,單個組件內部需要遍歷該組件的整個 vnode 樹
- Vue.js 3.0 做到了通過編譯階段對靜態模板的分析,編譯生成了 Block tree。Block tree 是一個將模版基於動態節點指令切割的嵌套區塊,每個區塊內部的節點結構是固定的。每個區塊只需要追蹤自身包含的動態節點。
3. 新引入Fragments(片段)特性:Vue 3.x 模板中不需要再創建一個唯一的根節點,模板里可以直接放文本內容或者很多同級的標簽, Vue2.x 需要唯一的節點
4. 靜態提升:靜態節點都會被提升到render 的外部,只有初始化時會被創建,再次調用render時不會再次創建,可以直接重用這些靜態節點對應的vnode
5. Patch flag
首先看下面這個案例,模版中有三個div標簽,其中只有最后一個標div簽的TEXT部分是動態的
在之前的VDOM中,如果msg值發生改變,整個模版中的所有元素都需要重新渲染。但在Vue3.0中,在這個模版編譯時,編譯器會在動態標簽末尾加上 /* Text*/
PatchFlag。只能帶patchFlag 的 Node 才被認為是動態的元素,會被追蹤屬性的修改。並且 PatchFlag 會標識動態的屬性類型有哪些,比如這里 的TEXT 表示只有節點中的文字是動態的。
6. cacheHandler 緩存事件處理函數減少了不必要的更新操作
正常情況下,當綁定一個事件:
<div> <p @click="handleClick">靜態代碼</p> </div>
模版會被編譯為
export function render(_ctx, _cache) { return (_openBlock(), _createBlock("div", null, [ _createVNode("p", { onClick: _ctx.handleClick }, "靜態代碼", 8 /* PROPS */, ["onClick"]) ])) }
其中事件會每次從全局上下文中獲取。而當開啟了cacheHandler之后
export function render(_ctx, _cache) { return (_openBlock(), _createBlock("div", null, [ _createVNode("p", { onClick: _cache[1] || (_cache[1] = ($event, ...args) => (_ctx.handleClick($event, ...args))) }, "靜態代碼") ])) }
編輯器會為你動態創建一個內聯函數,內聯函數里面再去飲用當前組件上最新的handler。之后編輯器會將內聯函數緩存。每次重新渲染時如果事件處理器沒有變,就會使用緩存中的事件處理而不會重新獲取事件處理器。這個節點就可以被看作是一個靜態的節點。這種優化更大的作用在於當其作用域組件時,之前每次重新渲染都會導致組件的重新渲染,在通過handler緩存之后,不會導致組件的重新渲染了。