EP52 - 效能

更新於 發佈於 閱讀時間約 14 分鐘
Performance 效能這個東西真的是每個領域的目標
調教效能真的蠻重要~但也是很多知識點的地方
感覺這篇文章一定蠻多內容 Orz...

概述​ - Overview

Vue 被設計為在大多數常見使用情境中表現良好,無需過多手動優化。然而,在一些具有挑戰性的場景中,仍需進行額外的微調。本文將探討在 Vue 應用程式中應注意的性能問題。

首先,我們來討論網頁性能的兩個主要方面:

  • 頁面加載性能:應用程式在首次訪問時顯示內容並變得可交互的速度。通常使用網頁核心指標如最大內容繪製時間 (Largest Contentful Paint, LCP) 和首次輸入延遲 (First Input Delay, FID) 來衡量。
  • 更新性能:應用程式對用戶輸入的響應速度。例如,當用戶在搜索框中輸入時列表更新的速度,或用戶在單頁應用程式 (SPA) 中點擊導航鏈接時頁面切換的速度。

儘管理想情況下應同時最大化這兩方面的性能,但不同的前端架構會影響達成所需性能的難易度。此外,您正在構建的應用程式類型也會極大影響性能優化的優先順序。因此,確保最佳性能的第一步是為您構建的應用程式類型選擇合適的架構:

請參考《使用 Vue 的方法》,了解如何以不同方式利用 Vue。

Jason Miller 在《應用程式類型》一文中討論了各類網頁應用程式及其各自的理想實現/交付方式。

性能分析選項​ - Profiling Options​

要提升性能,首先需要了解如何測量性能。有許多優秀的工具可以幫助完成這項工作:

用於測量生產部署的加載性能:

用於本地開發期間的性能測試:

頁面加載優化​ - Page Load Optimizations​

優化頁面加載性能有許多與框架無關的方法,請查看這篇 web.dev 指南 以獲取全面的總結。在這裡,我們主要關注 Vue 特有的技術。

選擇合適的架構​ - Choosing the Right Architecture

如果您的用例對頁面加載性能敏感,避免將其作為純客戶端 SPA 發布。您希望服務器直接發送包含用戶想要看到的內容的 HTML。純客戶端渲染的缺點是內容顯示速度慢。這可以通過服務端渲染 (SSR)靜態網站生成 (SSG) 來緩解。請參閱 SSR 指南 了解如何使用 Vue 執行 SSR。如果您的應用程序不需要豐富的交互性,也可以使用傳統的後端服務器來渲染 HTML,並在客戶端使用 Vue 增強它。

如果您的主要應用程序必須是 SPA,但有一些營銷頁面(登陸頁面、關於頁面、博客),請將它們單獨發布!您的營銷頁面應該理想地作為靜態 HTML 部署,並使用最少的 JS,通過 SSG 來實現。

打包尺寸和瘦身技術​ - Bundle Size and Tree-shaking

提升頁面加載性能的最有效方法之一是減少 JavaScript 包的大小。以下是使用 Vue 時減少包大小的一些方法:

  • 如果可能,使用構建步驟。
  1. Vue 的許多 API 如果通過現代構建工具打包是 "tree-shakable" 的。例如,如果您不使用內置的 <Transition> 組件,它將不會包含在最終的生產包中。Tree-shaking 還可以移除源代碼中其他未使用的模塊。
  2. 使用構建步驟時,模板是預編譯的,因此我們不需要將 Vue 編譯器傳送到瀏覽器。這樣可以節省 14kb 壓縮後的 JavaScript 並避免運行時編譯成本。
  • 引入新依賴項時要謹慎!在真實世界的應用中,膨脹的包通常是引入了沉重的依賴項而未意識到它。
  1. 如果使用構建步驟,首選提供 ES 模塊格式並支持 tree-shaking 的依賴項。例如,首選 lodash-es 而非 lodash
  2. 檢查依賴項的大小並評估其提供的功能是否值得。注意如果依賴項支持 tree-shaking,實際的大小增加將取決於您實際導入的 API。可以使用 bundlejs.com 進行快速檢查,但使用實際構建設置進行測量始終是最準確的。
  • 如果您主要使用 Vue 進行漸進增強並希望避免構建步驟,請考慮使用 petite-vue(僅 6kb)。

代碼分割​ - Code Splitting

代碼分割是指構建工具將應用程序包分割成多個較小的塊,然後按需或並行加載。通過適當的代碼分割,可以立即下載頁面加載所需的功能,而其他塊僅在需要時延遲加載,從而提升性能。

例如,Rollup(Vite 基於此)或 webpack 可以通過檢測 ESM 動態導入語法自動創建分割塊:

// lazy.js 及其依賴項將被分割成一個單獨的塊
// 並且僅在調用 `loadLazy()` 時加載。
function loadLazy() {
return import('./lazy.js')
}

延遲加載最好用於初始頁面加載後不立即需要的功能。在 Vue 應用程序中,可以結合 Vue 的異步組件功能來為組件樹創建分割塊:

import { defineAsyncComponent } from 'vue'

// Foo.vue 及其依賴項將被創建成一個單獨的塊。
// 當異步組件在頁面上渲染時,它才會按需加載。
const Foo = defineAsyncComponent(() => import('./Foo.vue'))

對於使用 Vue Router 的應用程序,強烈建議對路由組件使用延遲加載。Vue Router 有專門支持延遲加載(lazy loading)的功能,從defineAsyncComponent分離出來,詳情請參閱 延遲加載路由

更新優化 - Update Optimizations​

Props 穩定性 - Props Stability

在 Vue 中,只有當接收到的 props 至少有一個發生改變時,子組件才會更新。考慮以下示例:

<ListItem
v-for="item in list"
:id="item.id"
:active-id="activeId" />`

<ListItem> 組件內部,它使用 idactiveId props 來判斷是否是當前活動的項目。雖然這種方法有效,但問題在於每次 activeId 改變時,列表中的每個 <ListItem> 都必須更新!

理想情況下,只有那些活動狀態改變的項目應該更新。我們可以通過將活動狀態的計算移到父組件中,並使 <ListItem> 直接接受一個 active prop 來實現:

<ListItem
v-for="item in list"
:id="item.id"
:active="item.id === activeId" />

現在,對於大多數組件來說,當 activeId 改變時,active prop 將保持不變,因此它們不再需要更新。總的來說,保持傳遞給子組件的 props 盡可能穩定是關鍵。

v-once

v-once 是一個內置指令,可用於渲染依賴運行時數據但不需要更新的內容。使用 v-once 的整個子樹將在所有未來的更新中被跳過。詳細信息請參閱其 API 參考

v-memo

v-memo 是一個內置指令,可用於有條件地跳過大型子樹或 v-for 列表的更新。詳細信息請參閱其 API 參考

計算屬性穩定性 - Computed Stability

在 Vue 3.4 及更高版本中,計算屬性僅在其計算值與前一個值發生變化時才會觸發效果。例如,以下 isEven 計算屬性僅在返回值從 true 變為 false 或反之時才會觸發效果:

const count = ref(0)
const isEven = computed(() => count.value % 2 === 0)

watchEffect(() => console.log(isEven.value)) // true

// 不會觸發新的日志,因為計算值保持為 `true`
count.value = 2
count.value = 4

這減少了不必要的效果觸發,但如果計算屬性每次計算都會創建一個新對象,則無法生效:

const computedObj = computed(() => {
return {
isEven: count.value % 2 === 0
}
})

由於每次都創建了一個新對象,技術上新值總是與舊值不同。即使 isEven 屬性保持不變,Vue 也無法知道,除非進行深度比較,而這種比較可能代價高昂且不值得。

相反,我們可以通過手動比較新值與舊值,並在確定沒有變化時有條件地返回舊值來進行優化:

const computedObj = computed((oldValue) => {
const newValue = {
isEven: count.value % 2 === 0
}
if (oldValue && oldValue.isEven === newValue.isEven) {
return oldValue
}
return newValue
})

Try it in the playground

注意,你應該始終在比較並返回舊值之前完成完整的計算,以便在每次運行時都能收集相同的依賴項。

一般優化 - General Optimizations​

以下技巧影響頁面加載和更新性能。

虛擬化大型列表

在所有前端應用中,渲染大型列表是最常見的性能問題之一。無論框架有多高效,由於瀏覽器需要處理的大量 DOM 節點,渲染包含數千項的列表都會變得緩慢。

然而,我們並不一定需要預先渲染所有這些節點。在大多數情況下,用戶的屏幕尺寸只能顯示我們大型列表中的一小部分。我們可以通過列表虛擬化技術來大幅提高性能,這種技術僅渲染當前在視口或接近視口的項目。

實現列表虛擬化並不容易,但幸運的是,有現成的社區庫可以直接使用:

減少大型不可變結構的響應性開銷 - Reduce Reactivity Overhead for Large Immutable Structures

Vue 的響應性系統默認是深度的。雖然這使狀態管理直觀,但當數據量很大時,會產生一定程度的開銷,因為每次訪問屬性都會觸發代理陷阱來進行依賴追蹤。這通常在處理大型深層嵌套對象數組時變得顯著,其中單次渲染需要訪問 100,000 多個屬性,因此僅會影響特定用例。

Vue 提供了一個逃生口來選擇退出深度響應性,使用 shallowRef()shallowReactive()。淺層 API 創建的狀態僅在根級別是響應的,並且暴露所有未修改的嵌套對象。這使嵌套屬性訪問速度更快,代價是我們現在必須將所有嵌套對象視為不可變的,並且只能通過替換根狀態來觸發更新:

const shallowArray = shallowRef([
/* 大量深層對象列表 */
])

// 這不會觸發更新...
shallowArray.value.push(newObject)
// 這會觸發:
shallowArray.value = [...shallowArray.value, newObject]

// 這不會觸發更新...
shallowArray.value[0].foo = 1
// 這會觸發:
shallowArray.value = [
{
...shallowArray.value[0],
foo: 1
},
...shallowArray.value.slice(1)
]

避免不必要的組件抽象 - Avoid Unnecessary Component Abstractions

有時我們可能會為了更好的抽象或代碼組織,創建無渲染組件或高階組件(即使用額外 props 渲染其他組件的組件)。雖然這本身並沒有問題,但請記住,組件實例比純 DOM 節點昂貴得多,由於抽象模式創建過多組件實例將會產生性能成本。

請注意,減少少量實例不會有顯著效果,因此如果組件在應用中只渲染幾次,不要過於擔心。最佳情況是再次考慮在大型列表中進行此優化。想象一個包含 100 個項目的列表,其中每個項目組件包含許多子組件。在這裡移除一個不必要的組件抽象可能會導致數百個組件實例的減少。

看完這些還是不太清楚,效能差異多少?
要來實際操作看看那些效能工具吧!!!
先記住有這些方法跟工具吧~www


留言
avatar-img
留言分享你的想法!
avatar-img
卡關的人生
2會員
71內容數
分享生活趣事~
卡關的人生的其他內容
2024/11/10
Vue 提供了多種動畫技術來提升應用程式的互動性,包括基於 CSS 類別的動畫、基於狀態的動畫,以及使用監視器來動畫化數值。基於類別的動畫可通過動態添加 CSS 類別來觸發,像是觸發按鈕搖動效果。基於狀態的動畫則是透過樣式綁定,根據互動動態調整元素的外觀,例如根據滑鼠位置改變背景顏色。
Thumbnail
2024/11/10
Vue 提供了多種動畫技術來提升應用程式的互動性,包括基於 CSS 類別的動畫、基於狀態的動畫,以及使用監視器來動畫化數值。基於類別的動畫可通過動態添加 CSS 類別來觸發,像是觸發按鈕搖動效果。基於狀態的動畫則是透過樣式綁定,根據互動動態調整元素的外觀,例如根據滑鼠位置改變背景顏色。
Thumbnail
2024/11/09
Web Components 是一組網頁原生 API,允許開發者創建可重複使用的自訂元素。Vue 與 Web Components 是互補的技術,Vue 支援整合和創建自訂元素。
Thumbnail
2024/11/09
Web Components 是一組網頁原生 API,允許開發者創建可重複使用的自訂元素。Vue 與 Web Components 是互補的技術,Vue 支援整合和創建自訂元素。
Thumbnail
2024/11/08
Vue 建議使用模板構建應用程式,但在需要 JavaScript 的全程式化功能時,渲染函數可派上用場。渲染函數通過 h() 函數創建 vnode,h 是 hyperscript 的簡寫,能生成 HTML 的 JavaScript。
Thumbnail
2024/11/08
Vue 建議使用模板構建應用程式,但在需要 JavaScript 的全程式化功能時,渲染函數可派上用場。渲染函數通過 h() 函數創建 vnode,h 是 hyperscript 的簡寫,能生成 HTML 的 JavaScript。
Thumbnail
看更多
你可能也想看
Thumbnail
「欸!這是在哪裡買的?求連結 🥺」 誰叫你太有品味,一發就讓大家跟著剁手手? 讓你回購再回購的生活好物,是時候該介紹出場了吧! 「開箱你的美好生活」現正召喚各路好物的開箱使者 🤩
Thumbnail
「欸!這是在哪裡買的?求連結 🥺」 誰叫你太有品味,一發就讓大家跟著剁手手? 讓你回購再回購的生活好物,是時候該介紹出場了吧! 「開箱你的美好生活」現正召喚各路好物的開箱使者 🤩
Thumbnail
頁面加載性能可透過PageSpeed Insights、WebPageTest等工具測量,而更新性能則需使用Chrome DevTools、Vue DevTools等進行分析。提升頁面加載性能的技術包括選擇合適架構(SSR或SSG)、(tree-shaking)、(code splitting)等。
Thumbnail
頁面加載性能可透過PageSpeed Insights、WebPageTest等工具測量,而更新性能則需使用Chrome DevTools、Vue DevTools等進行分析。提升頁面加載性能的技術包括選擇合適架構(SSR或SSG)、(tree-shaking)、(code splitting)等。
Thumbnail
在當今數位時代,網站的可爬行性對搜尋引擎優化(SEO)至關重要。當搜尋引擎如Google爬取網站時,它會判斷網站結構是否易於理解和索引,進而影響網站的排名。因此,如何提升網站的可爬行性成為了所有網路行銷公司關注的焦點,特別是像八拓科技有限公司這樣的專業SEO公司。本文將討論如何通過SEO技術來提升網
Thumbnail
在當今數位時代,網站的可爬行性對搜尋引擎優化(SEO)至關重要。當搜尋引擎如Google爬取網站時,它會判斷網站結構是否易於理解和索引,進而影響網站的排名。因此,如何提升網站的可爬行性成為了所有網路行銷公司關注的焦點,特別是像八拓科技有限公司這樣的專業SEO公司。本文將討論如何通過SEO技術來提升網
Thumbnail
網站速度優化至少可以從主機位置、減少HTTP Requests次數、壓縮網頁程式碼、移除未使用的CSS、JS、HTML、精簡圖片大小、減少HTML元素、延遲載入、善用CDN服務、伺服器端程式演算法和資料庫表單欄位結構等10個面向進行。
Thumbnail
網站速度優化至少可以從主機位置、減少HTTP Requests次數、壓縮網頁程式碼、移除未使用的CSS、JS、HTML、精簡圖片大小、減少HTML元素、延遲載入、善用CDN服務、伺服器端程式演算法和資料庫表單欄位結構等10個面向進行。
Thumbnail
今天想透過這篇文章與各位分享如何透過 Chrome Devtool 的 Performance Tab 來檢測網頁在執行時的各種效能指標,讓網頁的 Runtime Performance 不再成為你 debug 時的瓶頸!
Thumbnail
今天想透過這篇文章與各位分享如何透過 Chrome Devtool 的 Performance Tab 來檢測網頁在執行時的各種效能指標,讓網頁的 Runtime Performance 不再成為你 debug 時的瓶頸!
Thumbnail
本篇為隨筆紀錄,將看完的影片疏理為短小筆記 。 特別有感的是,現在回頭看,發現影片中每個提及的知識點或是誤區自己都踩過好幾輪了,這是在學習及練習中很常會反覆發生、需要注意的地方,提醒自己在接下來的練習中,能將老師提及的思維及作法吸取並應用。
Thumbnail
本篇為隨筆紀錄,將看完的影片疏理為短小筆記 。 特別有感的是,現在回頭看,發現影片中每個提及的知識點或是誤區自己都踩過好幾輪了,這是在學習及練習中很常會反覆發生、需要注意的地方,提醒自己在接下來的練習中,能將老師提及的思維及作法吸取並應用。
Thumbnail
在當今數位時代,新聞網站已成為人們獲取資訊的主要途徑之一。然而,面對競爭激烈的環境,如何在保持優質內容的同時,改善用戶體驗,進而優化新聞網站的SEO效果,成為了極為重要的課題。以下將探討一些關鍵的策略與實踐。
Thumbnail
在當今數位時代,新聞網站已成為人們獲取資訊的主要途徑之一。然而,面對競爭激烈的環境,如何在保持優質內容的同時,改善用戶體驗,進而優化新聞網站的SEO效果,成為了極為重要的課題。以下將探討一些關鍵的策略與實踐。
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News