第一个优化建议可能你们都知道,下面的一段代码是简单的经过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性能时你要意识到:循环渲染组件的时候,其实没必要要的组件可能也被建立了。