Vue中的性能優(yōu)化方案
最近使用 Vue 開發(fā)的過程中使用到一些對于性能有所提升的編碼方式,所以特別梳理出來,可以作為后續(xù) Vue 開發(fā)的編碼規(guī)范使用
性能優(yōu)化方案主要分為三類,下面就詳細講講這三類優(yōu)化方案的應(yīng)用
- 減少響應(yīng)式使用
- 減少 DOM 渲染
- 減少打包體積
減少響應(yīng)式使用
Vue 中使用最方便的就是響應(yīng)式的變量,在讀?。╣et)對象屬性的時候收集副作用函數(shù)(effect)依賴,在寫入(set)屬性時取出副作用函數(shù)依賴執(zhí)行,但是收集依賴、觸發(fā)依賴執(zhí)行畢竟都會影響到性能,所以在明確知道不需要使用響應(yīng)式變量的場景下,就應(yīng)該減少響應(yīng)式變量的使用
1. 使用 computed 緩存計算結(jié)果
computed 和普通方法的區(qū)別在于:computed 會緩存計算結(jié)果,只有當(dāng)計算的內(nèi)容改變的時候才會重新計算,而普通方法每次都會重新計算。所以對于有計算邏輯的取值,建議盡量都通過 computed 來封裝一層
比如下面這個示例就是簡單的將 props 通過 computed 封裝一層后共 template 使用
const getTooltipStyle = computed((): CSSProperties => { return { color: props.color, fontSize: props.fontSize, }; });
2. 本地化響應(yīng)式變量
根據(jù) Vue 響應(yīng)式變量的原理,每次訪問響應(yīng)式數(shù)據(jù)時,都會收集依賴,所以在需要頻繁使用響應(yīng)式變量的時候,可以先將響應(yīng)式變量用一個本地變量存儲,轉(zhuǎn)換為一個非響應(yīng)式的變量
在 Vue3 中可以使用 unref
這個 api 來獲取到響應(yīng)式變量參數(shù)本身(Vue2 中直接通過 this
賦值就好)
const tableData = ref([]) const unrefTableData = unref(tableData) // 本地化變量后再做大量操作 unrefTableData.forEach(item => { // 具體操作 })
3. 函數(shù)式組件(Vue2)
函數(shù)式組件是指:只接受一些 prop 參數(shù),無響應(yīng)式數(shù)據(jù),無實例的組件,主要應(yīng)用在創(chuàng)建簡單的展示組件,比如標(biāo)題 header、純展示的表單等等。因為沒有響應(yīng)式數(shù)據(jù)和實例,所以初始化速度比普通有狀態(tài)的組件快很多,并且還支持返回多個節(jié)點
在 Vue2 中聲明函數(shù)式組件的方式如下
<!-- template 中的聲明方式 --> <template functional> </template> <!-- jsx 中的聲明方式 --> Vue.component("list", { functional: true, })
但是在 Vue3 中,有狀態(tài)的組件性能已經(jīng)大大提升,和無狀態(tài)組件(函數(shù)式組件)幾乎沒有差異,并且有狀態(tài)組件也支持了返回多個節(jié)點,所以官方也移除了 functional
定義函數(shù)式組件的方式,注意 Vue3 中是不兼容 Vue2 的函數(shù)式組件定義,所以如果未來打算升級 Vue3 的小伙伴就不建議使用函數(shù)式組件了
減少 DOM 渲染壓力
1. DOM 頻繁切換展示的情況使用 v-show
這是一個老身長談的優(yōu)化方案了,原理在于 v-if
和 v-show
實現(xiàn)方式的區(qū)別,對于 v-if
在不符合條件的情況下不會渲染 DOM 節(jié)點,對于 v-show
則是將各個條件情況都渲染出來,在通過 display: block / none
進行切換,所以在頻繁切換 DOM 展示情況的場景下,使用 v-show
的性能會相對更好,比如一個可編輯單元格需要頻繁切換編輯和保存后的狀態(tài)的時候
但 v-show
也不是沒有缺點,因為會把各個分支情況都提前渲染出來,如果節(jié)點很多并且不需要頻繁切換狀態(tài),用 v-if
會是更好的選擇
2. keep-alive 緩存組件狀態(tài)
在 Vue 中切換組件時,組件內(nèi)部的狀態(tài)也會丟失,比如我們在填寫一個表單的時候,切換到另外一個組件填寫其他信息,在切換回之前的表單組件后,原來填寫的信息會被刷新掉,這種情況下就會使用到 keep-alive 組件緩存組件狀態(tài)
比較常用的做法是在 <router-view>
標(biāo)簽內(nèi)嵌套一層 <transition>
標(biāo)簽增加組件切換時的過渡動畫效果,再嵌套一層 <keep-alive>
標(biāo)簽緩存組件狀態(tài),最后使用 <component>
渲染動態(tài)組件或者元素
<router-view> <template #default="{ Component, route }"> <transition> <keep-alive> <component :is="Component" :key="route.path" /> </keep-alive> </transition> </template> </router-view>
3. 路由懶加載
我們都知道 Vue 是單頁面頁面應(yīng)用,如果在首屏加載的時候就把所有需要使用的路由都加載出來的話,那就太浪費性能了,所以使用懶加載的方式加載路由,減少首屏加載的壓力,才是更合理的方案
在 vue-router 中使用路由懶加載需要通過箭頭函數(shù)返回一個 import
組件的路徑,這樣在運行到這個組件的時候,才會運行 import
編譯加載組件
const form: AppRouteRecordRaw = { path: "/basicForm", name: "BasicForm", component: () => import("/@/views/form/index.vue"), meta: { title: "基礎(chǔ)表單", }, }; export default form;
4. 圖片懶加載
圖片使用懶加載的原因和路由懶加載類似,都是為了減少不必要的渲染。比如我們有一張很長的頁面有很多數(shù)據(jù)或者圖片需要展示,而顯示屏幕的可視高度卻是固定的,所以在屏幕高度外的內(nèi)容完全可以等到頁面需要的時候再加載,從而減少了可是屏幕區(qū)域內(nèi)的渲染壓力
圖片懶加載的原理是:判斷圖片出現(xiàn)在當(dāng)前窗口時,將 data-src
替換為 src
加載圖片,比較常用三個可視區(qū)域判斷方式是
img.getBoundingClientRect().top
<document.documentElement.clientHeight
(元素相對于窗口位置 < 窗體高度)IntersectionObserver
api,當(dāng)其監(jiān)聽到目標(biāo)元素的可見部分到達屏幕高度內(nèi),執(zhí)行指定的回調(diào)函數(shù)loading="lazy"
屬性(目前兼容性不是特別好,參考Lazy loading - Web 性能)
在 Vue 中使用圖片懶加載推薦使用 vue-lazyload 這個插件,直接通過 v-lazy
這個指令就可以實現(xiàn)圖片懶加載的效果
<ul> <li v-for="img in list"> <img v-lazy="img.src" > </li> </ul>
5. 組件銷毀時要清除定時器、EventListener
有時我們會在項目中開啟 setTimeout
來定時觸發(fā)一些事件,比如定時提醒表單保存之類的需求,如果在離開組件時沒有及時清除掉定時器或者是 EventListener
,很多頁面堆積起來很容易造成頁面卡頓和內(nèi)存泄漏
常見的方案是在離開組件之前的 onBeforeUnmount
生命周期鉤子中清除掉定時器和 EventListener
onBeforeUnmount(() => { try { instance?.destroy?.(); } catch (error) { instanceRef.value = null; } })
在清除 EventListener
要注意:移除相同的函數(shù)。以下第一種情況不能清理掉 click 事件,因為它們是不同的函數(shù)對象,需要使用第二種指向相同函數(shù)對象的方式清除
// 這種情況不生效,因為指向的是不同函數(shù)對象 input.addEventListener("click", () => console.log("Hello")) input.removeEventListener("click", () => console.log("Hello")) // 此時指向相同的函數(shù)對象才能清理掉 EventListener 事件 function handler() { console.log("Hello") } input.addEventListener("click", () => handler) input.removeEventListener("click", () => handler)
6. 列表使用唯一 key
這個主要是和 diff 算法的效率有關(guān),所以我也把它作為減少 DOM 渲染壓力的一個方案。在我們使用 v-for
循環(huán)渲染內(nèi)容的時候,需要為每個組件分配一個 id,這樣在組件內(nèi)容有更新的時候,diff 算法通過 id 能夠更高效的找到變化的節(jié)點,讓 dom 渲染更迅速。同時需要注意分配的 id 最好不是數(shù)組的 index,因為一旦增加或減少數(shù)組元素,index 也會發(fā)生變化,這樣就失去 id 的效果了
<template v-for="schema in getSchema" :key="schema.field"> <form-item :schema="schema" :form-props="getProps" :all-default-values="defaultValueRef" /> </template>
減少打包體積
1. 開啟 gzip 壓縮
gzip 一種文件壓縮的格式,比較適合文本文件的壓縮,通常會縮小兩倍以上的體積,所以用在代碼文件的壓縮上非常合適
我們現(xiàn)在使用的打包工具還是 webpack,在 webpack 中開啟 gzip 打包的話可以使用 compression-webpack-plugin
這個插件,具體配置如下
const CompressionPlugin = require("compression-webpack-plugin"); module.exports = { configureWebpack : { plugins: [ new CompressionPlugin({ test: /\.(js|css|json|html)&/, tereshold: 10 * 1024, // 超過 10 k 才壓縮 }) ] } }
開啟 gzip 除了要在代碼中增加配置外,還需要服務(wù)端的支持,在前端中比較常用的是 Nginx,在 Nginx 中開啟 gzip 壓縮的主要配置參數(shù)如下
#開啟和關(guān)閉gzip模式 gzip on; #gizp壓縮起點,文件大于10k才進行壓縮 gzip_min_length 10k; # gzip 壓縮級別,1-9,數(shù)字越大壓縮的越好,也越占用CPU時間,一般為 5,再大效果就不明顯了 gzip_comp_level 5; # 進行壓縮的文件類型。 gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript ; #nginx對于靜態(tài)文件的處理模塊,開啟后會尋找以.gz結(jié)尾的文件,直接返回,不會占用cpu進行壓縮,如果找不到則不進行壓縮 gzip_static on # 是否在http header中添加Vary: Accept-Encoding,建議開啟 gzip_vary on; # 設(shè)置壓縮所需要的緩沖區(qū)大小,以4k為單位,如果文件為7k則申請2*4k的緩沖區(qū) gzip_buffers 2 4k; # 設(shè)置gzip壓縮針對的HTTP協(xié)議版本 gzip_http_version 1.1;
2. 按需引入第三方組件
我們平時使用的 UI 組件一般都是大而全的,我們的項目中很少會全部使用到,所以按需引入第三方組件,能夠有效減少應(yīng)用包體積
以我們現(xiàn)在使用的 Element Plus 組件為例,使用 unplugin-vue-components
和 unplugin-auto-import
這兩個插件來實現(xiàn)(參考官方教程)
首先引入兩個插件
pnpm i -D unplugin-vue-components unplugin-auto-import
然后再 Webpack 配置兩個插件即可
// webpack.config.js const AutoImport = require('unplugin-auto-import/webpack') const Components = require('unplugin-vue-components/webpack') const { ElementPlusResolver } = require('unplugin-vue-components/resolvers') module.exports = { // ... plugins: [ AutoImport({ resolvers: [ElementPlusResolver()], }), Components({ resolvers: [ElementPlusResolver()], }), ], }
到此這篇關(guān)于Vue中的性能優(yōu)化方案的文章就介紹到這了,更多相關(guān)Vue 性能優(yōu)化內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!