傻瓜式網站制作軟件seo百科
文章目錄
- 一、編譯階段
- diff算法優(yōu)化
- 靜態(tài)提升
- 事件監(jiān)聽緩存
- SSR優(yōu)化
- 二、源碼體積
- 三、響應式系統(tǒng)
- 參考文獻
一、編譯階段
回顧Vue2
,我們知道每個組件實例都對應一個 watcher
實例,它會在組件渲染的過程中把用到的數據property
記錄為依賴,當依賴發(fā)生改變,觸發(fā)setter
,則會通知watcher
,從而使關聯(lián)的組件重新渲染
試想一下,一個組件結構如下圖
<template><div id="content"><p class="text">靜態(tài)文本</p><p class="text">靜態(tài)文本</p><p class="text">{{ message }}</p><p class="text">靜態(tài)文本</p>...<p class="text">靜態(tài)文本</p></div>
</template>
可以看到,組件內部只有一個動態(tài)節(jié)點,剩余一堆都是靜態(tài)節(jié)點,所以這里很多 diff
和遍歷其實都是不需要的,造成性能浪費
因此,Vue3
在編譯階段,做了進一步優(yōu)化。主要有如下:
- diff算法優(yōu)化
- 靜態(tài)提升
- 事件監(jiān)聽緩存
- SSR優(yōu)化
diff算法優(yōu)化
vue3
在diff
算法中相比vue2
增加了靜態(tài)標記
關于這個靜態(tài)標記,其作用是為了會發(fā)生變化的地方添加一個flag
標記,下次發(fā)生變化的時候直接找該地方進行比較
下圖這里,已經標記靜態(tài)節(jié)點的p
標簽在diff
過程中則不會比較,把性能進一步提高
關于靜態(tài)類型枚舉如下
export const enum PatchFlags {TEXT = 1,// 動態(tài)的文本節(jié)點CLASS = 1 << 1, // 2 動態(tài)的 classSTYLE = 1 << 2, // 4 動態(tài)的 stylePROPS = 1 << 3, // 8 動態(tài)屬性,不包括類名和樣式FULL_PROPS = 1 << 4, // 16 動態(tài) key,當 key 變化時需要完整的 diff 算法做比較HYDRATE_EVENTS = 1 << 5, // 32 表示帶有事件監(jiān)聽器的節(jié)點STABLE_FRAGMENT = 1 << 6, // 64 一個不會改變子節(jié)點順序的 FragmentKEYED_FRAGMENT = 1 << 7, // 128 帶有 key 屬性的 FragmentUNKEYED_FRAGMENT = 1 << 8, // 256 子節(jié)點沒有 key 的 FragmentNEED_PATCH = 1 << 9, // 512DYNAMIC_SLOTS = 1 << 10, // 動態(tài) soltHOISTED = -1, // 特殊標志是負整數表示永遠不會用作 diffBAIL = -2 // 一個特殊的標志,指代差異算法
}
靜態(tài)提升
Vue3
中對不參與更新的元素,會做靜態(tài)提升,只會被創(chuàng)建一次,在渲染時直接復用
這樣就免去了重復的創(chuàng)建節(jié)點,大型應用會受益于這個改動,免去了重復的創(chuàng)建操作,優(yōu)化了運行時候的內存占用
<span>你好</span><div>{{ message }}</div>
沒有做靜態(tài)提升之前
export function render(_ctx, _cache, $props, $setup, $data, $options) {return (_openBlock(), _createBlock(_Fragment, null, [_createVNode("span", null, "你好"),_createVNode("div", null, _toDisplayString(_ctx.message), 1 /* TEXT */)], 64 /* STABLE_FRAGMENT */))
}
做了靜態(tài)提升之后
const _hoisted_1 = /*#__PURE__*/_createVNode("span", null, "你好", -1 /* HOISTED */)export function render(_ctx, _cache, $props, $setup, $data, $options) {return (_openBlock(), _createBlock(_Fragment, null, [_hoisted_1,_createVNode("div", null, _toDisplayString(_ctx.message), 1 /* TEXT */)], 64 /* STABLE_FRAGMENT */))
}// Check the console for the AST
靜態(tài)內容_hoisted_1
被放置在render
函數外,每次渲染的時候只要取 _hoisted_1
即可
同時 _hoisted_1
被打上了 PatchFlag
,靜態(tài)標記值為 -1 ,特殊標志是負整數表示永遠不會用于 Diff
事件監(jiān)聽緩存
默認情況下綁定事件行為會被視為動態(tài)綁定,所以每次都會去追蹤它的變化
<div><button @click = 'onClick'>點我</button>
</div>
沒開啟事件監(jiān)聽器緩存
export const render = /*#__PURE__*/_withId(function render(_ctx, _cache, $props, $setup, $data, $options) {return (_openBlock(), _createBlock("div", null, [_createVNode("button", { onClick: _ctx.onClick }, "點我", 8 /* PROPS */, ["onClick"])// PROPS=1<<3,// 8 //動態(tài)屬性,但不包含類名和樣式]))
})
開啟事件偵聽器緩存后
export function render(_ctx, _cache, $props, $setup, $data, $options) {return (_openBlock(), _createBlock("div", null, [_createVNode("button", {onClick: _cache[1] || (_cache[1] = (...args) => (_ctx.onClick(...args)))}, "點我")]))
}
上述發(fā)現(xiàn)開啟了緩存后,沒有了靜態(tài)標記。也就是說下次diff
算法的時候直接使用
SSR優(yōu)化
當靜態(tài)內容大到一定量級時候,會用createStaticVNode
方法在客戶端去生成一個static node,這些靜態(tài)node
,會被直接innerHtml
,就不需要創(chuàng)建對象,然后根據對象渲染
div><div><span>你好</span></div>... // 很多個靜態(tài)屬性<div><span>{{ message }}</span></div>
</div>
編譯后
import { mergeProps as _mergeProps } from "vue"
import { ssrRenderAttrs as _ssrRenderAttrs, ssrInterpolate as _ssrInterpolate } from "@vue/server-renderer"export function ssrRender(_ctx, _push, _parent, _attrs, $props, $setup, $data, $options) {const _cssVars = { style: { color: _ctx.color }}_push(`<div${_ssrRenderAttrs(_mergeProps(_attrs, _cssVars))}><div><span>你好</span>...<div><span>你好</span><div><span>${_ssrInterpolate(_ctx.message)}</span></div></div>`)
}
二、源碼體積
相比Vue2
,Vue3
整體體積變小了,除了移出一些不常用的API,再重要的是Tree shanking
任何一個函數,如ref
、reavtived
、computed
等,僅僅在用到的時候才打包,沒用到的模塊都被搖掉,打包的整體體積變小
import { computed, defineComponent, ref } from 'vue';
export default defineComponent({setup(props, context) {const age = ref(18)let state = reactive({name: 'test'})const readOnlyAge = computed(() => age.value++) // 19return {age,state,readOnlyAge}}
});
三、響應式系統(tǒng)
vue2
中采用 defineProperty
來劫持整個對象,然后進行深度遍歷所有屬性,給每個屬性添加getter
和setter
,實現(xiàn)響應式
vue3
采用proxy
重寫了響應式系統(tǒng),因為proxy
可以對整個對象進行監(jiān)聽,所以不需要深度遍歷
- 可以監(jiān)聽動態(tài)屬性的添加
- 可以監(jiān)聽到數組的索引和數組
length
屬性 - 可以監(jiān)聽刪除屬性
關于這兩個 API 具體的不同,我們下篇文章會進行一個更加詳細的介紹
參考文獻
- https://juejin.cn/post/6903171037211557895