Vue中的性能優(yōu)化方案
最近使用 Vue 開發(fā)的過程中使用到一些對于性能有所提升的編碼方式,所以特別梳理出來,可以作為后續(xù) Vue 開發(fā)的編碼規(guī)范使用
性能優(yōu)化方案主要分為三類,下面就詳細(xì)講講這三類優(yōu)化方案的應(yīng)用
- 減少響應(yīng)式使用
- 減少 DOM 渲染
- 減少打包體積
減少響應(yīng)式使用
Vue 中使用最方便的就是響應(yīng)式的變量,在讀?。╣et)對象屬性的時(shí)候收集副作用函數(shù)(effect)依賴,在寫入(set)屬性時(shí)取出副作用函數(shù)依賴執(zhí)行,但是收集依賴、觸發(fā)依賴執(zhí)行畢竟都會(huì)影響到性能,所以在明確知道不需要使用響應(yīng)式變量的場景下,就應(yīng)該減少響應(yīng)式變量的使用
1. 使用 computed 緩存計(jì)算結(jié)果
computed 和普通方法的區(qū)別在于:computed 會(huì)緩存計(jì)算結(jié)果,只有當(dāng)計(jì)算的內(nèi)容改變的時(shí)候才會(huì)重新計(jì)算,而普通方法每次都會(huì)重新計(jì)算。所以對于有計(jì)算邏輯的取值,建議盡量都通過 computed 來封裝一層
比如下面這個(gè)示例就是簡單的將 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ù)時(shí),都會(huì)收集依賴,所以在需要頻繁使用響應(yīng)式變量的時(shí)候,可以先將響應(yīng)式變量用一個(gè)本地變量存儲(chǔ),轉(zhuǎn)換為一個(gè)非響應(yīng)式的變量
在 Vue3 中可以使用 unref
這個(gè) 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ù),無實(shí)例的組件,主要應(yīng)用在創(chuàng)建簡單的展示組件,比如標(biāo)題 header、純展示的表單等等。因?yàn)闆]有響應(yīng)式數(shù)據(jù)和實(shí)例,所以初始化速度比普通有狀態(tài)的組件快很多,并且還支持返回多個(gè)節(jié)點(diǎn)
在 Vue2 中聲明函數(shù)式組件的方式如下
<!-- template 中的聲明方式 --> <template functional> </template> <!-- jsx 中的聲明方式 --> Vue.component("list", { functional: true, })
但是在 Vue3 中,有狀態(tài)的組件性能已經(jīng)大大提升,和無狀態(tài)組件(函數(shù)式組件)幾乎沒有差異,并且有狀態(tài)組件也支持了返回多個(gè)節(jié)點(diǎn),所以官方也移除了 functional
定義函數(shù)式組件的方式,注意 Vue3 中是不兼容 Vue2 的函數(shù)式組件定義,所以如果未來打算升級 Vue3 的小伙伴就不建議使用函數(shù)式組件了
減少 DOM 渲染壓力
1. DOM 頻繁切換展示的情況使用 v-show
這是一個(gè)老身長談的優(yōu)化方案了,原理在于 v-if
和 v-show
實(shí)現(xiàn)方式的區(qū)別,對于 v-if
在不符合條件的情況下不會(huì)渲染 DOM 節(jié)點(diǎn),對于 v-show
則是將各個(gè)條件情況都渲染出來,在通過 display: block / none
進(jìn)行切換,所以在頻繁切換 DOM 展示情況的場景下,使用 v-show
的性能會(huì)相對更好,比如一個(gè)可編輯單元格需要頻繁切換編輯和保存后的狀態(tài)的時(shí)候
但 v-show
也不是沒有缺點(diǎn),因?yàn)闀?huì)把各個(gè)分支情況都提前渲染出來,如果節(jié)點(diǎn)很多并且不需要頻繁切換狀態(tài),用 v-if
會(huì)是更好的選擇
2. keep-alive 緩存組件狀態(tài)
在 Vue 中切換組件時(shí),組件內(nèi)部的狀態(tài)也會(huì)丟失,比如我們在填寫一個(gè)表單的時(shí)候,切換到另外一個(gè)組件填寫其他信息,在切換回之前的表單組件后,原來填寫的信息會(huì)被刷新掉,這種情況下就會(huì)使用到 keep-alive 組件緩存組件狀態(tài)
比較常用的做法是在 <router-view>
標(biāo)簽內(nèi)嵌套一層 <transition>
標(biāo)簽增加組件切換時(shí)的過渡動(dòng)畫效果,再嵌套一層 <keep-alive>
標(biāo)簽緩存組件狀態(tài),最后使用 <component>
渲染動(dòng)態(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)用,如果在首屏加載的時(shí)候就把所有需要使用的路由都加載出來的話,那就太浪費(fèi)性能了,所以使用懶加載的方式加載路由,減少首屏加載的壓力,才是更合理的方案
在 vue-router 中使用路由懶加載需要通過箭頭函數(shù)返回一個(gè) import
組件的路徑,這樣在運(yùn)行到這個(gè)組件的時(shí)候,才會(huì)運(yùn)行 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)容完全可以等到頁面需要的時(shí)候再加載,從而減少了可是屏幕區(qū)域內(nèi)的渲染壓力
圖片懶加載的原理是:判斷圖片出現(xiàn)在當(dāng)前窗口時(shí),將 data-src
替換為 src
加載圖片,比較常用三個(gè)可視區(qū)域判斷方式是
img.getBoundingClientRect().top
<document.documentElement.clientHeight
(元素相對于窗口位置 < 窗體高度)IntersectionObserver
api,當(dāng)其監(jiān)聽到目標(biāo)元素的可見部分到達(dá)屏幕高度內(nèi),執(zhí)行指定的回調(diào)函數(shù)loading="lazy"
屬性(目前兼容性不是特別好,參考Lazy loading - Web 性能)
在 Vue 中使用圖片懶加載推薦使用 vue-lazyload 這個(gè)插件,直接通過 v-lazy
這個(gè)指令就可以實(shí)現(xiàn)圖片懶加載的效果
<ul> <li v-for="img in list"> <img v-lazy="img.src" > </li> </ul>
5. 組件銷毀時(shí)要清除定時(shí)器、EventListener
有時(shí)我們會(huì)在項(xiàng)目中開啟 setTimeout
來定時(shí)觸發(fā)一些事件,比如定時(shí)提醒表單保存之類的需求,如果在離開組件時(shí)沒有及時(shí)清除掉定時(shí)器或者是 EventListener
,很多頁面堆積起來很容易造成頁面卡頓和內(nèi)存泄漏
常見的方案是在離開組件之前的 onBeforeUnmount
生命周期鉤子中清除掉定時(shí)器和 EventListener
onBeforeUnmount(() => { try { instance?.destroy?.(); } catch (error) { instanceRef.value = null; } })
在清除 EventListener
要注意:移除相同的函數(shù)。以下第一種情況不能清理掉 click 事件,因?yàn)樗鼈兪遣煌暮瘮?shù)對象,需要使用第二種指向相同函數(shù)對象的方式清除
// 這種情況不生效,因?yàn)橹赶虻氖遣煌瘮?shù)對象 input.addEventListener("click", () => console.log("Hello")) input.removeEventListener("click", () => console.log("Hello")) // 此時(shí)指向相同的函數(shù)對象才能清理掉 EventListener 事件 function handler() { console.log("Hello") } input.addEventListener("click", () => handler) input.removeEventListener("click", () => handler)
6. 列表使用唯一 key
這個(gè)主要是和 diff 算法的效率有關(guān),所以我也把它作為減少 DOM 渲染壓力的一個(gè)方案。在我們使用 v-for
循環(huán)渲染內(nèi)容的時(shí)候,需要為每個(gè)組件分配一個(gè) id,這樣在組件內(nèi)容有更新的時(shí)候,diff 算法通過 id 能夠更高效的找到變化的節(jié)點(diǎn),讓 dom 渲染更迅速。同時(shí)需要注意分配的 id 最好不是數(shù)組的 index,因?yàn)橐坏┰黾踊驕p少數(shù)組元素,index 也會(huì)發(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 一種文件壓縮的格式,比較適合文本文件的壓縮,通常會(huì)縮小兩倍以上的體積,所以用在代碼文件的壓縮上非常合適
我們現(xiàn)在使用的打包工具還是 webpack,在 webpack 中開啟 gzip 打包的話可以使用 compression-webpack-plugin
這個(gè)插件,具體配置如下
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壓縮起點(diǎn),文件大于10k才進(jìn)行壓縮 gzip_min_length 10k; # gzip 壓縮級別,1-9,數(shù)字越大壓縮的越好,也越占用CPU時(shí)間,一般為 5,再大效果就不明顯了 gzip_comp_level 5; # 進(jìn)行壓縮的文件類型。 gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript ; #nginx對于靜態(tài)文件的處理模塊,開啟后會(huì)尋找以.gz結(jié)尾的文件,直接返回,不會(huì)占用cpu進(jìn)行壓縮,如果找不到則不進(jìn)行壓縮 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. 按需引入第三方組件
我們平時(shí)使用的 UI 組件一般都是大而全的,我們的項(xiàng)目中很少會(huì)全部使用到,所以按需引入第三方組件,能夠有效減少應(yīng)用包體積
以我們現(xiàn)在使用的 Element Plus 組件為例,使用 unplugin-vue-components
和 unplugin-auto-import
這兩個(gè)插件來實(shí)現(xiàn)(參考官方教程)
首先引入兩個(gè)插件
pnpm i -D unplugin-vue-components unplugin-auto-import
然后再 Webpack 配置兩個(gè)插件即可
// 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)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue-cli實(shí)現(xiàn)多頁面多路由的示例代碼
本篇文章主要介紹了vue-cli實(shí)現(xiàn)多頁面多路由的示例代碼,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-01-01Vue3 style CSS 變量注入的實(shí)現(xiàn)
本文主要介紹了Vue3 style CSS 變量注入的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-07-07vue中多個(gè)倒計(jì)時(shí)實(shí)現(xiàn)代碼實(shí)例
這篇文章主要介紹了vue中多個(gè)倒計(jì)時(shí)實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-03-03vue利用指令實(shí)現(xiàn)快速設(shè)置元素的高度
在項(xiàng)目中經(jīng)常有需要將列表的高度設(shè)置成剩余可視區(qū)域的高度,本文主要來和大家介紹一下如何通過指令和css變量的方式快速設(shè)置列表高度,希望對大家有所幫助2024-03-03element step組件在另一側(cè)加時(shí)間軸顯示
本文主要介紹了element step組件在另一側(cè)加時(shí)間軸顯示,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06