文章目錄
1.代碼優(yōu)化
2.項(xiàng)目優(yōu)化
3.其它優(yōu)化
4.總結(jié)
本文主要針對的是 vue 2.x 版本的性能優(yōu)化,并且從代碼優(yōu)化 和 其他的優(yōu)化去講下在項(xiàng)目開發(fā)時(shí)應(yīng)該注意的優(yōu)化事項(xiàng)。
首先從項(xiàng)目代碼層面方面
1.代碼優(yōu)化
v-if / v-show
這兩個(gè)指令在不同場景的使用下,會(huì)有不同的性能損耗情況撞叨,首先簡單了解下兩者的區(qū)別。
- v-if 指令在編譯階段就會(huì)編譯成一個(gè)三元運(yùn)算符革屠,通過條件進(jìn)行渲染镊讼。當(dāng)條件的值變化時(shí),會(huì)觸發(fā)對應(yīng)的組件更新揩徊,即會(huì)經(jīng)過 diff 算法腰鬼, 組件初始化、渲染 vnode塑荒、patch等過程熄赡。
- v-show 指令相比于 v-if 的優(yōu)勢就是它在更新階段僅僅更新了 DOM 的顯隱,少去了很多性能開銷的操作齿税。但是初始化的時(shí)候會(huì)把所有條件節(jié)點(diǎn)都渲染出來
<!-- v-if -->
<template>
<section>
<p v-if="props.value">Hello World!</p>
<p v-else>Vue yyds</p>
</section>
</template>
<!-- v-show -->
<template>
<section>
<p v-show="props.value">Hello World!</p>
<p v-show="!props.value">Vue yyds</p>
</section>
</template>
- 使用場景:
初始化階段: v-if 性能優(yōu)于 v-show
頻繁更新階段: v-show 性能優(yōu)于 v-if
v-for 和 v-if
避免把這兩個(gè)指令放在同一個(gè)節(jié)點(diǎn)
因?yàn)?v-for 指令的優(yōu)先級比 v-if 高彼硫,所以兩個(gè)指令混用的話會(huì)導(dǎo)致每次節(jié)點(diǎn)render的結(jié)果都帶上了條件渲染。當(dāng)數(shù)據(jù)量和節(jié)點(diǎn)復(fù)雜的時(shí)候凌箕,就會(huì)有明顯的性能差拧篮。
當(dāng)我們要循環(huán)節(jié)點(diǎn)和條件判斷的時(shí)候,我們可以先對數(shù)據(jù)進(jìn)行處理后再進(jìn)行 v-for 渲染牵舱,如下例子:
<!-- bad -->
<div v-for="item in list" v-if="item.count > 10" :key="item.id">{{item.name}}</div>
<!-- good -->
<div v-for="item in filters" :key="item.id">{{item.name}}</div>
computed: {
filters () {
return this.list.filter(item => item.count > 10)
}
}
從上面例子不難看出串绩,每個(gè) v-for 節(jié)點(diǎn)處就加了 key 唯一標(biāo)識,這也是提升性能的一個(gè)小點(diǎn)仆葡。使用 key 時(shí)赏参,vue會(huì)基于 key 的變化重新排列元素順序志笼,并且會(huì)移除 key 不存在的元素。
keep-alive
部分場景可以使用 keep-alive 進(jìn)行組件緩存
keep-alive 包裹的組件在經(jīng)過渲染后的 vnode 以及 DOM 都會(huì)被緩存起來把篓,然后下次再次渲染該組件的時(shí)纫溃,直接從緩存中拿到對應(yīng)的 vnode 和 DOM 并且進(jìn)行渲染,并不需要再走一次組件初始化韧掩,render 和 patch 等一系列流程紊浩,減少了 script 的執(zhí)行時(shí)間,性能更好疗锐。
v-slot:slotName / slot="slotName"
vue 2.6
版本坊谁,可以使用 v-slot:slotName 的語法代替 slot="slotName"
- 舊的寫法在更新時(shí)多了一個(gè)父組件更新的過程,而新的寫法由于直接更新子組件滑臊,就會(huì)更加高效口芍,性能更好,所以推薦始終使用語法 v-slot:slotName
函數(shù)式組件
vue 2.x 的版本雇卷,需要DOM層復(fù)用的情況且場景相對簡單時(shí)鬓椭,可以用 函數(shù)式組件 代替普通組件
函數(shù)式組件生成的是普通的 vnode,不會(huì)存在遞歸子組件的過程关划,所以會(huì)減少一定程度的性能開銷
場景:我在多個(gè)頁面都復(fù)用了一個(gè)頁面渲染的組件小染,不需要一些復(fù)雜的操作,只需要根據(jù)傳參進(jìn)行條件渲染或者循環(huán)渲染等贮折、就可以用如下的例子去進(jìn)行組件的編寫
<!-- 函數(shù)式組件 -->
<template functional>
<section>
<div v-if="props.value">Hello World</div>
<div v-else>Hi~</div>
<ul>
<li v-for="item in props.list" :key="item.id">{{item.name}}</li>
</ul>
</section>
</template>
子組件拆分
除開代碼維護(hù)的層面裤翩,從性能方面,組件的拆分也是有好處的调榄。
- 因?yàn)関ue 的更新是組件力度踊赠,如果組件中有數(shù)據(jù)發(fā)生變化,就會(huì)執(zhí)行 render 函數(shù)生成新的 vnode 和舊的 vnode 進(jìn)行 diff振峻。哪怕這個(gè)數(shù)據(jù)只影響到一個(gè)元素渲染臼疫,其他元素也需要在 diff 過程中進(jìn)行比較。極端情況是假設(shè)這個(gè)大組件里面有個(gè)倒計(jì)時(shí)扣孟。
- 如果組件拆分得當(dāng)?shù)脑捥痰蹋蟛糠值臄?shù)據(jù)改動(dòng)只會(huì)影響到子組件本身進(jìn)行 diff ==> 渲染。所以合理得對這種大組件進(jìn)行拆分凤价,應(yīng)用的更新效率會(huì)更高鸽斟。
例如:
<!-- 拆分前 -->
<template>
<div :style="{ opacity: number / 300 }">
<div>{{ heavy() }}</div>
</div>
</template>
<script>
export default {
props: ['number'],
methods: {
heavy () {
const n = 100000
let result = 0
for (let i = 0; i < n; i++) {
result += Math.sqrt(Math.cos(Math.sin(42)))
}
return result
},
},
}
</script>
<!-- 拆分后 -->
<template>
<div :style="{ opacity: number / 300 }">
<ChildComp/>
</div>
</template>
<script>
export default {
components: {
ChildComp: {
methods: {
heavy () {
const n = 100000
let result = 0
for (let i = 0; i < n; i++) {
result += Math.sqrt(Math.cos(Math.sin(42)))
}
return result
},
},
render (h) {
return h('div', this.heavy())
},
},
},
props: ['number'],
}
</script>
響應(yīng)式數(shù)據(jù)優(yōu)化
data的優(yōu)化
vue 在組件實(shí)例初始化的時(shí)候會(huì)對data進(jìn)行響應(yīng)式處理,能夠減少一個(gè)數(shù)據(jù)就是減少一點(diǎn)點(diǎn)的性能開銷利诺。而且一些常量數(shù)據(jù)不應(yīng)該在data里面進(jìn)行定義富蓄,簡單總結(jié)為以下三點(diǎn):
減少無用的data數(shù)據(jù)
減少數(shù)據(jù)被observer
數(shù)據(jù)盡量扁平化
局部變量緩存響應(yīng)式數(shù)據(jù)
先直接看對比代碼
// 優(yōu)化前
// ··········
computed: {
base () {
return 42
},
result () {
let result = this.start
for (let i = 0; i < 1000; i++) {
result += Math.sqrt(Math.cos(Math.sin(this.base))) + this.base * this.base + this.base + this.base * 2 + this.base * 3
}
return result
},
}
// 優(yōu)化后
// ·········
computed: {
base () {
return 42
},
result ({ base, start }) {
let result = start
for (let i = 0; i < 1000; i++) {
result += Math.sqrt(Math.cos(Math.sin(base))) + base * base + base + base * 2 + base * 3
}
return result
},
}
優(yōu)化前每次調(diào)用 this.base
的時(shí)候,由于它是個(gè)響應(yīng)式數(shù)據(jù)慢逾,每次都會(huì)調(diào)用都會(huì)觸發(fā)它的 getter
立倍,進(jìn)而執(zhí)行依賴收集等邏輯灭红。
優(yōu)化后先把 this.base
緩存到局部變量,后面重復(fù)調(diào)用的時(shí)候就不會(huì)頻繁的觸發(fā)到 getter
的邏輯處理
computed
計(jì)算屬性是基于它們的響應(yīng)式依賴進(jìn)行緩存的口注。只在相關(guān)響應(yīng)式依賴發(fā)生改變時(shí)它們才會(huì)重新求值变擒。這就意味著只要 數(shù)據(jù) 還沒有發(fā)生改變,多次訪問 計(jì)算屬性會(huì)立即返回之前的計(jì)算結(jié)果寝志,可以極大提升性能娇斑。
例如:
<template>
<div>{{count}}</div>
</template>
<script>
export default {
computed: {
count () {
let result
// 復(fù)雜的計(jì)算操作
return result
}
}
}
</script>
2.項(xiàng)目優(yōu)化
路由懶加載
在vue-router的配置文件里,定義一個(gè)能夠被 Webpack 自動(dòng)代碼分割的異步組件
const Hello = () => import('//hello.vue')
const router = new VueRouter({
routes: [
{ path: '/hello', component: Hello },
{ path: '/world', component: import('//world.vue') }
]
})
組件按需加載
以 element-ui
為例子材部,有些項(xiàng)目用ui組件庫的時(shí)候使用的并不多毫缆,可以按需加載適當(dāng)優(yōu)化項(xiàng)目的體積
import {Button} from 'element-ui'
Vue.use(Button)
3.其它優(yōu)化
1.圖片懶加載
2.節(jié)流 / 防抖
3.長列表的虛擬滾動(dòng)
4.總結(jié)
1.減少?zèng)]必要的渲染機(jī)制
2.減少全量加載,適當(dāng)?shù)膽屑虞d
3.正確的使用vue的每個(gè)api乐导,本身vue這個(gè)框架就對性能方面做了很多處理苦丁,正常使用就不會(huì)出現(xiàn)性能瓶頸