第一個優化建議可能你們都知道,下面的一段代碼是簡單的經過props:value
來控制兩個element是否建立javascript
<template>
<div class="cell">
<div v-if="value" class="on"></div>
<section v-else class="off"></section>
</div>
</template>
<script> export default { props: ['value'] } </script>
複製代碼
由於這個組件沒有使用到state
,能夠經過functional component來優化性能,由於functional component沒有state
,在建立的時候就減小了state
追蹤等一系操做帶來的性能消耗,提升了性能html
// 由於functional component沒有this,要使用props.value來訪問props
<template functional>
<div class="cell">
<div v-if="props.value" class="on"></div>
<section v-else class="off"></section>
</div>
</template>
<script> export default { props: ['value'] } </script>
複製代碼
下面是屢次重複銷燬兩個組件性能的對比,黃色的script區域有了必定程度的減小,瀏覽器就能夠作更多的render或者響應。 vue
直接上代碼java
<template>
<div :style="{ opacity: number / 300 }">
<div>{{ heavy() }}</div>
</div>
</template>
<script> export default { props: ['number'], methods: { heavy () { /* 長任務 */ } } } </script>
複製代碼
當組件隨着props:number
的變化,組件patch
從新渲染的時候,heavy
長任務也會從新執行。可是若是能將沒有與父組件相互依賴的元素,拆成一個組件,在父組件須要從新渲染的時候,由於與父組件沒有依賴子組件並不會跟着從新渲染,響應的性能也能獲得提高.git
<template>
<div :style="{ opacity: number / 300 }">
<ChildComp/>
</div>
</template>
<script> export default { props: ['number'], components: { ChildComp: { methods: { heavy () { /* 長任務在子組件裏。 */ } }, render (h) { return h('div', this.heavy()) } } } } </script>
複製代碼
把比較消耗性能且與父組件沒有依賴關係的元素,拆成一個子組件,當父組件patch
且須要從新渲染的時候,子組件不須要從新渲染,長任務也不會重複執行。父組件重複渲染的次數越多,這種方式提升的性能越大。 github
跟前面一個相似,瀏覽器
<template>
<div :style="{ opacity: start / 300 }">{{ result }}</div>
</template>
<script> import { heavy } from '@/utils' export default { props: ['start'], computed: { base () { return 42 }, result () { let result = this.start for (let i = 0; i < 1000; i++) { result += heavy(this.base) } return result } } } </script>
複製代碼
在computed:result
中,循環執行heavy
方法,其中另一個computed屬性this.base
被重複讀取,當你讀取一個響應式的屬性,像computed
的屬性,或者state
,vue會進行一些邏輯的操做,記錄這個響應式的屬性是如何被讀取的,當這個變量改變的時候,就須要通知讀取的方法從新執行。將他先存在另一個本地的變量中(這時,vue已經記錄這個響應式變量被讀取,下次發生改變時仍是會通知這個方法從新執行),就像一個緩存同樣,在重複讀取這個本地變量的值的時候就不會像讀取一個響應式變量同樣每次都須要執行那些邏輯。緩存
<template>
<div :style="{ opacity: start / 300 }">{{ result }}</div>
</template>
<script> import { heavy } from '@/utils' export default { props: ['start'], computed: { base () { return 42 }, result () { const base = this.base let result = this.start for (let i = 0; i < 1000; i++) { result += heavy(base) } return result } } } </script>
複製代碼
在一個方法中大量重複引用一個響應式的變量時,先用一個本地變量存儲,能夠大幅度的提升性能。 app
前面介紹了vue對響應式變量存取的時候須要進行一些邏輯操做,也介紹了functional component
由於沒有state,使得他在建立的時候能夠變的快一些。這些都是由於響應式變量,在提供比較便利的響應更新的同時也消耗了一些的性能。特別是一些比較大的對象,或者對象有深的屬性層級,這個時候vue就要花更多的精力來讓你的對象變的響應式。其實咱們也能夠告訴vue,在對象中某些屬性其實不須要進行響應式操做。異步
const data = items.map(
item => optimizeItem(item)
)
function optimizeItem (item) {
const itemData = {
id: uid++,
vote: 0
}
Object.defineProperty(itemData, 'data', {
// 使他沒有響應式
configurable: false,
value: item
})
return itemData
}
複製代碼
還有一些其餘辦法能夠作這個好比說Object.freeze()
,可是他他同時會使這個屬性變的readonly。 若是有1000個items使部分對象屬性不須要響應式能夠提升17倍的速度
又是比較熟悉的一個,仍是直接上代碼
<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>
複製代碼
當組件props.value
發生改變的時候div.on
和section.off
會被重複的銷燬在建立,這兩個組件裏面都包含一個比較重型的組件,每次銷燬再建立這個重型組件是比較消耗性能的。相比較於v-if
的重複銷燬又建立一個組件,v-show
只是控制組件的顯示和隱藏。
<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>
複製代碼
當一個組件須要被來回切換狀態控制顯示和不顯示,相比較v-if
會重複的建立銷燬組件,利用v-show
控制這個組件的隱藏和展現能夠更好的重複的利用這個組件。
和上一個同樣,v-show
是針對組件的複用,對於須要重複使用的頁面可使用Keep-alive
進行重用.
<template>
<div id="app">
<router-view/>
</div>
</template>
複製代碼
這也又是一個內存空間換速度的例子
<template>
<div id="app">
<keep-alive>
<router-view/>
</keep-alive>
</div>
</template>
複製代碼
當你可能會須要在兩個頁面之間來回的切換,可使用Keep-alive
對頁面進行重用,提高性能.
咱們在以前的以前的博客中有介紹過這種用於處理長任務的方式.比方說咱們有一大堆須要消耗大量性能建立的組件
<template>
<div>
<h2>I'm an heavy page</h2>
<Heavy v-for="n in 10" :key="n"/>
<Heavy class="super-heavy" :n="9999999"/>
</div>
</template>
複製代碼
若是這些組件在同一時間執行,會造成一個長人物,他會影響瀏覽器對用戶的響應,和影響瀏覽器渲染頁面,頁面也會明顯的感受到卡頓,這時候咱們把異步的組件先建立,在稍後的時候在去建立另一部分的組件就能夠把一個長任務拆分開,瀏覽器就能夠先渲染前面一個組件,用戶也會先看到前面建立的一個組件,用戶也不會感受到頁面的卡頓了。
<template>
<div>
<h2>I'm an heavy page</h2>
<template v-if="defer(2)">
<Heavy v-for="n in 10" :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>
複製代碼
Defer.js
:
export default function (count = 10) {
return {
data () {
return {
displayPriority: 0
}
},
mounted () {
this.runDisplayPriority()
},
methods: {
runDisplayPriority () {
const step = () => {
requestAnimationFrame(() => {
this.displayPriority++
if (this.displayPriority < count) {
step()
}
})
}
step()
},
defer (priority) {
return this.displayPriority >= priority
}
}
}
}
複製代碼
當咱們面對一個長任務須要處理的時候,若是他是幾個大組件同時建立形成的,前後執行這些組件是拆分長任務的一個比較好的辦法
同樣是解決長任務的方法。
fetchItems ({ commit }, { items }) {
commit('clearItems')
commit('addItems', items)
}
複製代碼
這個方法一次性提交了items.length
個對象。可能會建立items.length
個組件,跟上面的思路同樣,比起一次性提交items.length
個數據建立一個長任務,咱們能夠將items
拆分開來提交,也就是將一個長任務切分開來執行.
fetchItems ({ commit }, { items, splitCount }) {
commit('clearItems')
const queue = new JobQueue()
splitArray(items, splitCount).forEach(
chunk => queue.addJob(done => {
// Commit array chunks on several frames
requestAnimationFrame(() => {
commit('addItems', chunk)
done()
})
})
)
// Start and wait for all the jobs
// to finish
await queue.start()
}
複製代碼
跟上面相似的作法,當咱們有不少個對象賦值之後可能會引起不少的組件在同一時間建立從而建立一個長任務,咱們能夠分批的將對象提交,從而將長任務,拆分紅好幾個短任務來執行,給瀏覽器主線程留出時間來響應渲染。
在開發時渲染一個列表是很常見的操做
<div class="items no-v">
<FetchItemView v-for="item of items" :key="item.id" :item="item" />
</div>
複製代碼
若是咱們的items特別的多,或者咱們的FetchItemView組件很是的大,一個就佔了一屏的展現空間,其實咱們不必一會兒就渲染那麼多items.length
個組件,咱們只須要渲染屏幕中展現的組件就能夠了。有一個庫能夠幫到咱們:virtual-scroller
<recycle-scroller class="items" :items="items" :item-size="24" >
<template v-slot="{ item }">
<FetchItemView :item="item" />
</template>
</recycle-scroller>
複製代碼
從而及時你有1000個items也只會渲染在屏幕中展現的item。固然咱們不須要庫也能夠辦到這個。這裏就不介紹了。這一點主要介紹的是一個思想,在追求極致的vue性能時你要意識到:循環渲染組件的時候,其實沒必要要的組件可能也被建立了。