vue3.0做了哪些優(yōu)化

https://v3.cn.vuejs.org/guide/migration/introduction.html#%E6%A6%82%E8%A7%88

首先熟空,為什么要優(yōu)化?

?????Vue3 組合式 API(Composition API) 主要用于在大型組件中提高代碼邏輯的可復(fù)用性。
?????vue2.x option Api的傳統(tǒng)組件隨著業(yè)務(wù)復(fù)雜度越來越高锅棕,代碼量會不斷的加大,整個代碼邏輯都不易閱讀和理解衣式。
?????Vue3 使用組合式 API 的地方為 setup俯艰。
?????在 setup 中,我們可以按邏輯關(guān)注點對部分代碼進行分組缰揪,然后提取邏輯片段并與其他組件共享代碼。因此葱淳,組合式 API(Composition API) 允許我們編寫更有條理的代碼钝腺。

沒有對比就沒有傷害~

另,compositon api提供了以下幾個函數(shù):

  • setup
  • ref
  • reactive
  • watchEffect
  • watch
  • computed
  • toRefs
  • 生命周期的hooks



優(yōu)化

一赞厕、 源碼的優(yōu)化

1. 更好的代碼管理方式:monorepo

vue2.x vue3.x
Vue.js 2.x 的源碼托管在 src 目錄艳狐,然后分別依據(jù)功能拆分出了:
1. compiler(模板編譯的相關(guān)代碼)
2. core(與平臺無關(guān)的通用運行時代碼)
3. platforms(平臺專有代碼)
4. server(服務(wù)端渲染的相關(guān)代碼)
5. sfc(.vue 單文件解析相關(guān)代碼)
6. shared(共享工具代碼) 等目錄:
Vue.js 3.0 ,整個源碼是通過 monorepo 的方式維護
根據(jù)功能將不同的模塊拆分到packages 目錄下面不同的子目錄中
vue2.x代碼結(jié)構(gòu)
vue3.x代碼結(jié)構(gòu)
優(yōu)點:
  • 相對于 Vue.js 2.x 的源碼組織方式皿桑,monorepo 把這些模塊拆分到不同的 package 中毫目,每個 package有各自的API蔬啡、類型定義和測試。這樣使得模塊拆分更細化镀虐,職責劃分更明確箱蟆,模塊之間的依賴關(guān)系也更加明確,開發(fā)人員也更容易閱讀刮便、理解和更改所有模塊源碼空猜,提高代碼的可維護性。
  • 另外一些 package(比如 reactivity 響應(yīng)式庫)是可以獨立于 Vue.js 使用的恨旱,這樣用戶如果只想使用 Vue.js 3.0 的響應(yīng)式能力辈毯,可以單獨依賴這個響應(yīng)式庫而不用去依賴整個 Vue.js,減小了引用包的體積大小搜贤,而 Vue.js 2 .x 是做不到這一點的谆沃。

2. 有類型的 JavaScript:TypeScript

vue2.x vue3.x
Flow

Flow 是 Facebook 出品的 JavaScript 靜態(tài)類型檢查工具

優(yōu)點:它可以以非常小的成本對已有的 JavaScript 代碼遷入,非常靈活

缺點:但是 Flow 對于一些復(fù)雜場景類型的檢查仪芒,支持得并不好
TypeScript

TypeScript提供了更好的類型檢查管毙,能支持復(fù)雜的類型推導(dǎo)
由于源碼就使用 TypeScript 編寫,也省去了單獨維護 d.ts 文件的麻煩




二桌硫、 性能優(yōu)化

1. 源碼體積優(yōu)化

  • 移除一些冷門的 feature(比如 filter夭咬、inline-template 等);
  • 引入 tree-shaking 的技術(shù)铆隘,減少打包體積卓舵。

tree-shaking原理
tree-shaking 依賴 ES2015 模塊語法的靜態(tài)結(jié)構(gòu)(即 import 和 export),通過編譯階段的靜態(tài)分析膀钠,找到?jīng)]有引入的模塊并打上標記掏湾。

舉個栗子,一個 math 模塊定義了 2 個方法 square(x) 和 cube(x) :

export function square(x) {
  return x * x
}
export function cube(x) {
  return x * x * x
}

我們在另一個模塊外面只引入了 cube 方法:

import { cube } from './math.js'
// do something with cube

最終 math 模塊會被 webpack 打包生成如下代碼:

/* 1 */
/***/ 
(function(module, __webpack_exports__, __webpack_require__) {
  'use strict';
  /* unused harmony export square */
  /* harmony export (immutable) */ __webpack_exports__['a'] = cube;
  function square(x) {
    return x * x;
  }
  function cube(x) {
    return x * x * x;
  }
});

由上可得肿嘲,未被引入的 square 模塊被標記了融击, 然后壓縮階段會利用例如 uglify-js、terser 等壓縮工具真正地刪除這些沒有用到的代碼雳窟。
也就是說尊浪,利用 tree-shaking 技術(shù),任何一個函數(shù)封救,僅僅在用到的時候才打包拇涤,沒用到的模塊都被搖掉,打包的整體體積變小誉结。

2. 響應(yīng)式系統(tǒng)——數(shù)據(jù)劫持優(yōu)化

通過數(shù)據(jù)劫持來更新視圖的大體流程

vue2.x中采用 Object.defineProperty 來劫持整個對象鹅士,然后進行深度遍歷所有屬性,給每個屬性添加getter和setter惩坑,實現(xiàn)響應(yīng)式:

  • 缺點:它必須預(yù)先知道要攔截的 key 是什么掉盅,所以它并不能檢測對象屬性的添加和刪除也拜,所以提供了vue.setvue.delete方式進行處理,但是對于開發(fā)者來說顯然是不那么友好的趾痘。

vue3.x采用proxy重寫了響應(yīng)式系統(tǒng)慢哈,因為proxy可以對整個對象進行監(jiān)聽,所以不需要深度遍歷:

  • 可以監(jiān)聽動態(tài)屬性的添加
  • 可以監(jiān)聽到數(shù)組的索引和數(shù)組length屬性
  • 可以監(jiān)聽刪除屬性

3. 編譯階段的優(yōu)化

  • diff算法優(yōu)化
  • 靜態(tài)提升
  • 事件監(jiān)聽緩存
  • SSR優(yōu)化
3.1 diff算法優(yōu)化

雖然 Vue 能保證觸發(fā)更新的組件最小化扼脐,但在單個組件內(nèi)部依然需要遍歷該組件的整個 vnode 樹,舉個例子奋刽,比如我們要更新這個組件:

<div>
  <p>老八食堂</p>
  <p>{{ message }}</p>
</div>

則整個 diff 過程如圖所示(在 Vue 2.x 的全量對比模式下):

可以看到瓦侮,這段代碼中只有一個動態(tài)節(jié)點,所以這里有很多 diff 和遍歷其實都是不需要的佣谐,這就會導(dǎo)致 vnode 的性能跟模版大小正相關(guān)肚吏,跟動態(tài)節(jié)點的數(shù)量無關(guān),當一些組件的整個模版內(nèi)只有少量動態(tài)節(jié)點時狭魂,這些遍歷都是性能的浪費罚攀。
而對于上述例子,理想狀態(tài)只需要 diff 這個綁定 message 動態(tài)節(jié)點的 p 標簽即可雌澄。

在 Vue 3.0 中斋泄,對 diff 算法進行了優(yōu)化,在創(chuàng)建虛擬 DOM 時镐牺,根據(jù) DOM 內(nèi)容是否會發(fā)生變化炫掐,而給予相對應(yīng)類型的靜態(tài)標記(PatchFlag),如下圖所示:

觀察上圖睬涧,不難發(fā)現(xiàn)試圖的更新只對帶有 flag 標記的標簽進行了對比(diff)募胃,所以只進行了 1 次比較,而相同情況下畦浓,Vue 2.x 則進行了 3 次比較痹束。這便是 Vue 3.0 比 Vue2.x 性能好的第一個原因。

  • 關(guān)于靜態(tài)類型枚舉如下:
export const enum PatchFlags {
  TEXT = 1,// 動態(tài)的文本節(jié)點
  CLASS = 1 << 1,  // 2 動態(tài)的 class
  STYLE = 1 << 2,  // 4 動態(tài)的 style
  PROPS = 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é)點順序的 Fragment
  KEYED_FRAGMENT = 1 << 7, // 128 帶有 key 屬性的 Fragment
  UNKEYED_FRAGMENT = 1 << 8, // 256 子節(jié)點沒有 key 的 Fragment
  NEED_PATCH = 1 << 9,   // 512
  DYNAMIC_SLOTS = 1 << 10,  // 動態(tài) solt
  HOISTED = -1,  // 特殊標志是負整數(shù)表示永遠不會用作 diff
  BAIL = -2 // 一個特殊的標志,指代差異算法
}


3.2 靜態(tài)提升

Vue3中對不參與更新的元素夺溢,會做靜態(tài)提升抹蚀,只會被創(chuàng)建一次,在渲染時直接復(fù)用企垦。
這樣就免去了重復(fù)的創(chuàng)建節(jié)點环壤,大型應(yīng)用會受益于這個改動,免去了重復(fù)的創(chuàng)建操作钞诡,優(yōu)化了運行時候的內(nèi)存占用郑现。

<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)內(nèi)容_hoisted_1被放置在render 函數(shù)外湃崩,每次渲染的時候只要取 _hoisted_1 即可。
同時 _hoisted_1 被打上了 PatchFlag 接箫,靜態(tài)標記值為 -1 攒读,特殊標志是負整數(shù)表示永遠不會用于 Diff。

3.3 事件監(jiān)聽緩存(cacheHandler)

默認情況下 @click 事件被認為是動態(tài)變量辛友,所以每次更新視圖的時候都會追蹤它的變化薄扁。但是正常情況下,我們的 @click 事件在視圖渲染前和渲染后废累,都是同一個事件邓梅,基本上不需要去追蹤它的變化,所以 Vue 3.0 對此作出了相應(yīng)的優(yōu)化叫事件監(jiān)聽緩存邑滨,舉個栗子:

<div>
  <p @click="handleClick">屋里一giao</p>
</div>

編譯后如下圖所示(還未開啟 cacheHandler):


在未開啟事件監(jiān)聽緩存的情況下日缨,我們看到這串代碼編譯后被靜態(tài)標記為 8,之前講解過被靜態(tài)標記的標簽就會被拉去做比較掖看,而靜態(tài)標記 8 對應(yīng)的是“動態(tài)屬性匣距,不包括類名和樣式”。@click 被認為是動態(tài)屬性哎壳,所以我們需要開啟 Options 下的 cacheHandler 屬性毅待,如下圖所示:

開啟 cacheHandler之后,編譯后的代碼已經(jīng)沒有靜態(tài)標記(PatchFlag)归榕,也就表明圖中 P 標簽不再被追蹤比較變化恩静,也就是說下次diff算法的時候直接使用,進而提升了 Vue 的性能蹲坷。

3.4 SSR優(yōu)化

當靜態(tài)內(nèi)容大到一定量級時候驶乾,會用createStaticVNode方法在客戶端去生成一個static node,這些靜態(tài)node循签,會被直接innerHtml级乐,就不需要創(chuàng)建虛擬DOM對象,然后根據(jù)對象渲染

<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>`)
}





參考文獻:
https://juejin.cn/post/6903171037211557895
https://vue3js.cn/interview/vue3/performance.html
https://juejin.cn/post/6850418112878575629#heading-5

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末县匠,一起剝皮案震驚了整個濱河市风科,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌乞旦,老刑警劉巖贼穆,帶你破解...
    沈念sama閱讀 212,454評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異兰粉,居然都是意外死亡故痊,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評論 3 385
  • 文/潘曉璐 我一進店門玖姑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來愕秫,“玉大人慨菱,你說我怎么就攤上這事〈魉Γ” “怎么了贞盯?”我有些...
    開封第一講書人閱讀 157,921評論 0 348
  • 文/不壞的土叔 我叫張陵妇多,是天一觀的道長啼辣。 經(jīng)常有香客問我粥喜,道長,這世上最難降的妖魔是什么缴川? 我笑而不...
    開封第一講書人閱讀 56,648評論 1 284
  • 正文 為了忘掉前任茉稠,我火速辦了婚禮,結(jié)果婚禮上二跋,老公的妹妹穿的比我還像新娘战惊。我一直安慰自己流昏,他們只是感情好扎即,可當我...
    茶點故事閱讀 65,770評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著况凉,像睡著了一般谚鄙。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上刁绒,一...
    開封第一講書人閱讀 49,950評論 1 291
  • 那天闷营,我揣著相機與錄音,去河邊找鬼知市。 笑死傻盟,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的嫂丙。 我是一名探鬼主播娘赴,決...
    沈念sama閱讀 39,090評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼跟啤!你這毒婦竟也來了诽表?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,817評論 0 268
  • 序言:老撾萬榮一對情侶失蹤隅肥,失蹤者是張志新(化名)和其女友劉穎竿奏,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體腥放,經(jīng)...
    沈念sama閱讀 44,275評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡泛啸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,592評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了秃症。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片平痰。...
    茶點故事閱讀 38,724評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡汞舱,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出宗雇,到底是詐尸還是另有隱情昂芜,我是刑警寧澤,帶...
    沈念sama閱讀 34,409評論 4 333
  • 正文 年R本政府宣布赔蒲,位于F島的核電站泌神,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏舞虱。R本人自食惡果不足惜欢际,卻給世界環(huán)境...
    茶點故事閱讀 40,052評論 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望矾兜。 院中可真熱鬧损趋,春花似錦、人聲如沸椅寺。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽返帕。三九已至桐玻,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間荆萤,已是汗流浹背镊靴。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留链韭,地道東北人偏竟。 一個月前我還...
    沈念sama閱讀 46,503評論 2 361
  • 正文 我出身青樓,卻偏偏與公主長得像敞峭,于是被迫代替她去往敵國和親踊谋。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,627評論 2 350

推薦閱讀更多精彩內(nèi)容