Vue中的性能優(yōu)化方案
最近使用 Vue 開發(fā)的過程中使用到一些對于性能有所提升的編碼方式,所以特別梳理出來,可以作為后續(xù) Vue 開發(fā)的編碼規(guī)范使用
性能優(yōu)化方案主要分為三類,下面就詳細講講這三類優(yōu)化方案的應用
- 減少響應式使用
- 減少 DOM 渲染
- 減少打包體積
減少響應式使用
Vue 中使用最方便的就是響應式的變量,在讀?。╣et)對象屬性的時候收集副作用函數(shù)(effect)依賴,在寫入(set)屬性時取出副作用函數(shù)依賴執(zhí)行,但是收集依賴、觸發(fā)依賴執(zhí)行畢竟都會影響到性能,所以在明確知道不需要使用響應式變量的場景下,就應該減少響應式變量的使用
1. 使用 computed 緩存計算結果
computed 和普通方法的區(qū)別在于:computed 會緩存計算結果,只有當計算的內(nèi)容改變的時候才會重新計算,而普通方法每次都會重新計算。所以對于有計算邏輯的取值,建議盡量都通過 computed 來封裝一層
比如下面這個示例就是簡單的將 props 通過 computed 封裝一層后共 template 使用
const getTooltipStyle = computed((): CSSProperties => {
return {
color: props.color,
fontSize: props.fontSize,
};
});
2. 本地化響應式變量
根據(jù) Vue 響應式變量的原理,每次訪問響應式數(shù)據(jù)時,都會收集依賴,所以在需要頻繁使用響應式變量的時候,可以先將響應式變量用一個本地變量存儲,轉換為一個非響應式的變量
在 Vue3 中可以使用 unref 這個 api 來獲取到響應式變量參數(shù)本身(Vue2 中直接通過 this 賦值就好)
const tableData = ref([])
const unrefTableData = unref(tableData) // 本地化變量后再做大量操作
unrefTableData.forEach(item => {
// 具體操作
})
3. 函數(shù)式組件(Vue2)
函數(shù)式組件是指:只接受一些 prop 參數(shù),無響應式數(shù)據(jù),無實例的組件,主要應用在創(chuàng)建簡單的展示組件,比如標題 header、純展示的表單等等。因為沒有響應式數(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> 標簽內(nèi)嵌套一層 <transition> 標簽增加組件切換時的過渡動畫效果,再嵌套一層 <keep-alive> 標簽緩存組件狀態(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 是單頁面頁面應用,如果在首屏加載的時候就把所有需要使用的路由都加載出來的話,那就太浪費性能了,所以使用懶加載的方式加載路由,減少首屏加載的壓力,才是更合理的方案
在 vue-router 中使用路由懶加載需要通過箭頭函數(shù)返回一個 import 組件的路徑,這樣在運行到這個組件的時候,才會運行 import 編譯加載組件
const form: AppRouteRecordRaw = {
path: "/basicForm",
name: "BasicForm",
component: () => import("/@/views/form/index.vue"),
meta: {
title: "基礎表單",
},
};
export default form;
4. 圖片懶加載
圖片使用懶加載的原因和路由懶加載類似,都是為了減少不必要的渲染。比如我們有一張很長的頁面有很多數(shù)據(jù)或者圖片需要展示,而顯示屏幕的可視高度卻是固定的,所以在屏幕高度外的內(nèi)容完全可以等到頁面需要的時候再加載,從而減少了可是屏幕區(qū)域內(nèi)的渲染壓力
圖片懶加載的原理是:判斷圖片出現(xiàn)在當前窗口時,將 data-src 替換為 src 加載圖片,比較常用三個可視區(qū)域判斷方式是
img.getBoundingClientRect().top<document.documentElement.clientHeight(元素相對于窗口位置 < 窗體高度)IntersectionObserverapi,當其監(jiān)聽到目標元素的可見部分到達屏幕高度內(nèi),執(zhí)行指定的回調函數(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 算法的效率有關,所以我也把它作為減少 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 除了要在代碼中增加配置外,還需要服務端的支持,在前端中比較常用的是 Nginx,在 Nginx 中開啟 gzip 壓縮的主要配置參數(shù)如下
#開啟和關閉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結尾的文件,直接返回,不會占用cpu進行壓縮,如果找不到則不進行壓縮 gzip_static on # 是否在http header中添加Vary: Accept-Encoding,建議開啟 gzip_vary on; # 設置壓縮所需要的緩沖區(qū)大小,以4k為單位,如果文件為7k則申請2*4k的緩沖區(qū) gzip_buffers 2 4k; # 設置gzip壓縮針對的HTTP協(xié)議版本 gzip_http_version 1.1;
2. 按需引入第三方組件
我們平時使用的 UI 組件一般都是大而全的,我們的項目中很少會全部使用到,所以按需引入第三方組件,能夠有效減少應用包體積
以我們現(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()],
}),
],
}到此這篇關于Vue中的性能優(yōu)化方案的文章就介紹到這了,更多相關Vue 性能優(yōu)化內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

