Vue3.0 效能優化及新特性深度解析
最近,Vue作者尤雨溪在B站的演講中分享了Vue3.0的六大亮點:
- 效能
- Tree-shaking 支援
- Composition API
- Fragment、Teleport、Suspense
- 更好的 TS 支援
- 自定義渲染API
編譯時對VDom的效能優化
PatchFlag
首先看下面這個案例,模版中有三個P標籤,其中只有最後一個P標籤的TEXT部分是動態的
在之前的VDOM中,如果msg值發生改變,整個模版中的所有元素都需要重新渲染。但在Vue3.0中,在這個模版編譯時,編譯器會在動態標籤末尾加上 /* Text*/
PatchFlag。只能帶patchFlag 的 Node 才被認為是動態的元素,會被追蹤屬性的修改。並且 PatchFlag 會標識動態的屬性型別有哪些,比如這裡 的TEXT 表示只有節點中的文字是動態的。
每一個Block中的節點,就算很深,也是直接跟Block一層繫結的,可以直接跳轉到動態節點而不需要逐個逐層遍歷。
既有VDOM的靈活性,又有效能保證。
hoistStatic 靜態節點提升
當使用hoistStatic時,所有 靜態的節點都被提升到render方法之外。這意味著,他們只會在應用啟動的時候被建立一次,而後隨著每次的渲染被不停的複用。
在大型應用中對於記憶體有很大的優化。
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,{
onClick: _cache[1] || (_cache[1] = ($event,...args ) => (_ctx.handleClick($event,...args)))
},"靜態程式碼")
]))
}
複製程式碼
編輯器會為你動態建立一個行內函數,行內函數裡面再去飲用當前元件上最新的handler。之後編輯器會將行內函數快取。每次重新渲染時如果事件處理器沒有變,就會使用快取中的事件處理而不會重新獲取事件處理器。這個節點就可以被看作是一個靜態的節點。這種優化更大的作用在於當其作用域元件時,之前每次重新渲染都會導致元件的重新渲染,在通過handler快取之後,不會導致元件的重新渲染了。
SSR 服務端渲染
當開啟SSR了之後,如果我們模版中有一些靜態標籤,這些靜態標籤會被直接轉化成文字。
其中的動態繫結依然是一個單獨的字串內嵌進去。這個效能肯定比React 轉成VDOM在專為HTML快很多。
StaticNode 靜態節點
剛才提到在SSR中靜態的節點會被轉化為純字串。如果在客戶端,當靜態節點巢狀足夠多的時候,VUE編譯器也會將VDOM轉化為純字串的HTML。即 StaticNode。
通過這些操作,我們可以看下,跟vue2比可以快一倍以上,記憶體佔用可以小一倍以上。
Tree Shaking
因為ES6模組是靜態引用的,所以我們可以在編譯時正確的判斷到底載入了哪些程式碼。對程式碼全域性做一個分析,找到那些沒用被用到的模組、函式、變數,並把這些去掉。
複製程式碼
當使用一個 bundle (webpack etc.)的時候,預設會加上 TreeShaking。Vue 3.0 中沒有被用到的模組可以不被打包到編譯後的檔案中,被 TreeShake 掉。當只有一個HelloWorld的時候 Vue3打包後 13.5kb。所有的元件全部載入進來時是 22.5kb
Composition API
隨著Vue元件的增大,元件內程式碼變得越來越難以理解和維護。其中的一些可以複用的程式碼很難被抽離出來。同時 Vue2.0還缺少 TS支援。在Vue2中,邏輯概念(功能)被管理在元件中,但是功能和元件並不是一對一關係。一個功能可以被多個元件使用同時一個元件可以有多個功能。在Vue中,一個功能可能需要依賴多個Options(components、props、data、computed、methods及生命週期方法)。
在 Composition API中提供可 setup 方法。以一個有搜尋功能和 排序功能元件為例:
<script>
export default {
setup() {
}
}
function useSearch() {
return {
...useSearch(),...useSorting()
}
}
function useSorting() {
}
</script>
複製程式碼
Vue2 中的程式碼複用
Mixin
在Vue2中有幾種方式可以複用程式碼,其中之一就是 Mixins。
- Mixins可以實現組織功能
- 容易發生衝突
- 很難說明依賴關係
- 程式碼不容易複用
Mixin 工廠
- 可以方便複用
- 明確的依賴關係
- 弱名稱空間
- 隱性的屬性新增
Scoped Slots
- 解決了 Mixin 的問題
- 增加了層級關係導致更難以理解
- 很多配置資訊
- 靈活性更少
- 效能較差
核心 API
- reactive
- ref
- computed
- readonly
- watchEffect
- watch
- Lifecycle Hooks
Fragments
Vue3中不在要求模版的跟節點必須是隻能有一個節點。跟節點和和render函式返回的可以是純文字、陣列、單個節點,如果是陣列,會自動轉化為 Fragments。
Teleport
對標 React Portal。可以做一些關於響應式的設計,如果螢幕寬度比較寬的時候,加入某些元素,螢幕變窄後移除。
Suspense
等待巢狀的非同步依賴。再把一個巢狀的元件樹渲染到頁面上之前,先在記憶體中進行渲染,並記錄所有的存在非同步依賴的元件。只有所有的非同步依賴全部被resolve之後,才會把整個書渲染到dom中。當你的元件中有一個 async的 setup函式,這個元件可以被看作是一個Async Component,只有當這個元件被Resolve之後,再把整個樹渲染出來
- async setup()
- Async Component
Typescript
Vue3原始碼使用 TS重寫,但不意味著vue3的專案也要使用TS。但Vue3會對 TS有更好的支援
- 支援 TSX
- 支援 Class component
- 程式碼會變大一些