文中例子來自https://www.youtube.com/watch?v=5B66qer8cZo 部分轉載於鏈接:https://www.jianshu.com/p/f372d0e3de80
函數型組件
由於組件的生命週期處理在框架層面上十分耗時,所以,建議平常儘量使用函數型組件。這樣,可以避免不必要的性能損失。只要在模板上聲明functional
屬性,就可以實現函數式組件了:
<template functional>
<div>
<div v-if="value" class="on"></div>
<section v-else class="off"></section>
</div>
</template>
<script>
export default {
props: ['value']
}
</script>
子組件拆分
另一個優化技巧是,將複雜的耗時計算處理放在子組件中進行處理:
<template>
<div :style="{opacity: number / 300 }">
<ChildComp />
</div>
</template>
<script>
export default {
props: ['number'],
components: {
ChilComp: {
methods: {
heavy() {
// heavy task
}
},
render(h) {
return h('div', this.heavy())
}
}
}
}
</script>
局部變量
平常在引用computed
數據進行計算的時候,可以多使用局部變量,這樣可以避免多次重複計算。
<template>
<div :style="{ opacity: start / 300 }">{{ result }}</div>
</template>
<script>
export default {
props: ['start'],
computed: {
base () {
return 42
},
result () {
// 賦值給局部變量,防止重複計算
const base = this.base;
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
},
},
}
</script>
活用v-show,減少v-if
對於需要頻繁切換的視圖來說,使用v-show
比v-if
更加節約性能。因爲v-show
可以避免dom節點的銷燬和重建,所以我們可以將如下的例子
<template functional>
<div class="cell">
<div v-if="props.value" class="on">
<Heavy :n="10000" />
</div>
<section v-else class="off">
<Heavy :n="10000" />
</section>
</div>
</template>
改寫爲
<template functional>
<div class="cell">
<div v-show="props.value" class="on">
<Heavy :n="10000" />
</div>
<section v-show="!props.value" class="off">
<Heavy :n="10000" />
</section>
</div>
</template>
使用keep-alive
另外一種很常用的優化技巧是使用keep-alive
,通常是在路由切換組件中使用:
<template>
<div id="app">
<keep-alive>
<router-view />
</keep-alive>
</div>
</template>
延遲加載(defer)
<template>
<div class="deferred-off">
<VueIcon icon="fitness_center" class="gigantic"/>
<h2>I'm an heavy page</h2>
<Heavy v-for="n in 8" :key="n"/>
<Heavy class="super-heavy" :n="9999999"/>
</div>
</template>
<template>
<div class="deferred-on">
<VueIcon icon="fitness_center" class="gigantic"/>
<h2>I'm an heavy page</h2>
<template v-if="defer(2)">
<Heavy v-for="n in 8" :key="n"/>
</template>
<Heavy v-if="defer(3)" class="super-heavy" :n="9999999"/>
</div>
</template>
<script>
import Defer from '@/mixins/Defer'
export default {
mixins: [
Defer(),
],
}
</script>
分批處理(time slicing)
下面這個性能優化的點是前端通用的,可以用requestAnimationFrame
分批次執行大數據量的計算,防止一次性執行的數據太大從而阻塞頁面渲染。
比如下面這個例子:
fetchItems({ commit }, { items }) {
commit('clearItems');
commit('addItems', items)
}
可以改寫爲:
fetchItems({ commit }, { items, splitCount }) {
commit('clearItems');
//新建一個隊列
const queue = new JobQueue();
splitArray(items, splitCount).forEach(chunk => queue.addJob(done => {
// 分片
requestAnimationFrame(() => {
commit('addItems', chunk);
done()
});
}));
// 等待所有數據處理完畢
awiat queue.start();
}
非響應式模式(non-reactive)
對於複雜的數據結構,我們可以顯式聲明爲非響應式,這樣可以避免很多不必要的計算,從而提高性能
const data = items.map(item => optimizeItem(item));
function optimizeItem (item) {
const itemData = {
id: uid ++,
vote: 0
};
Object.defineProperty(itemData, 'data', {
// mark as non-reactive
configurable: false,
value: item
});
return itemData
}
僅渲染可視化部分
對於無限長列表來說,性能優化主要方法是保持僅渲染可視化部分。
來看一下下面這個例子:
<div class="items no-v">
<FetchItemViewFunctional v-for="item of items" :key="item.id" :item="item" @vote="voteItem(item)">
</FetchItemViewFunctional>
</div>
這是最常見的寫法,不過如果列表的內容很多,你就會發現頁面十分的卡頓。此時大家可以利用vue-virtual-scroller這個組件,進行優化:
<recycle-scroller
class="item"
:items="items"
:item-size="24"
>
<template v-slot="{item}">
<FetchItemView :item="item" @vote="voteItem(item)"/>
</template>
</recycle-scroller>
這樣,可以大大提升組件的流暢度和性能。