1.使用计算属性
这一点已经被提及很多次了,计算属性最大的一个特点就是它是可以被缓存的,这个缓存指的是只要它的依赖的不发生改变,它就不会被重新求值,再次访问时会直接拿到缓存的值,在做一些复杂的计算时,可以极大提升性能。可以看以下代码
<template>
<div>{{superCount}}</div>
</template>
<script>
export default {
data() {
return {
count: 1
}
},
computed: {
superCount() {
let superCount = this.count
// 假设这里有个复杂的计算
for (let i = 0; i < 10000; i++) {
superCount++
}
return superCount
}
}
}
</script>
这个例子中,在 created、mounted 以及模板中都访问了 superCount 属性,这三次访问中,实际上只有第一次即
created
时才会对 superCount 求值,由于 count 属性并未改变,其余两次都是直接返回缓存的 value,对于计算属性更加详细的介绍可以看我之前写的文章:Vue computed 是如何实现的?。
2.使用函数式组件
对于某些组件,如果我们只是用来显示一些数据,不需要管理状态,监听数据等,那么就可以用函数式组件。函数式组件是无状态的,无实例的,在初始化时不需要初始化状态,不需要创建实例,也不需要去处理生命周期等,相比有状态组件,会更加轻量,同时性能也更好。具体的函数式组件使用方式可参考官方文档:函数式组件
3.使用 keep-alive
在动态组件的场景下:
<template>
<div>
<component :is="currentComponent" />
</div>
</template>
这个时候有多个组件来回切换,
currentComponent
每变一次,相关的组件就会销毁/创建一次,如果这些组件比较复杂的话,就会造成一定的性能压力,其实我们可以使用 keep-alive 将这些组件缓存起来:
keep-alive
的作用就是将它包裹的组件在第一次渲染后就缓存起来,下次需要时就直接从缓存里面取,避免了不必要的性能浪费
<template>
<div>
<keep-alive>
<component :is="currentComponent" />
</keep-alive>
</div>
</template>
4.避免 v-for 和 v-if 同时使用
这一点是 Vue 官方的风格指南中明确指出的一点:Vue 风格指南
<ul>
<li v-for="user in users" v-if="user.isActive" :key="user.id">
{{ user.name }}
</li>
</ul>
可以看到,这里是先遍历(v-for),再判断(v-if),这里有个问题就是:如果你有一万条数据,其中只有 100 条是
isActive
状态的,你只希望显示这 100 条,但是实际在渲染时,每一次渲染,这一万条数据都会被遍历一遍。比如你在这个组件内的其他地方改变了某个响应式数据时,会触发重新渲染,调用渲染函数,调用渲染函数时,就会执行到上面的代码,从而将这一万条数据遍历一遍,即使你的users
没有发生任何改变。为了避免这个问题,在此场景下你可以用计算属性代替
<template>
<div>
<ul>
<li v-for="user in activeUsers" :key="user.id">{{ user.name }}</li>
</ul>
</div>
</template>
<script>
export default {
// ...
computed: {
activeUsers() {
return this.users.filter((user) => user.isActive)
}
}
}
</script>
这样只会在users
发生改变时才会执行这段遍历的逻辑,和之前相比,避免了不必要的性能浪费。
5.始终为 v-for 添加 key,并且不要将 index 作为的 key
这一点是Vue 风格指南中明确指出的一点,同时也是面试时常问的一点,很多人都习惯的将 index 作为 key,这样其实是不太好的,index 作为 key 时,将会让 diff 算法产生错误的判断,从而带来一些性能问题,你可以看下ssh大佬的文章,深入分析下,为什么 Vue 中不要用 index 作为 key。在这里我也通过一个例子来简单说明下当 index 作为 key 时是如何影响性能的。
6.延迟渲染
延迟渲染就是分批渲染,假设我们某个页面里有一些组件在初始化时需要执行复杂的逻辑:
<template>
<div>
<!-- Heavy组件初始化时需要执行很复杂的逻辑,执行大量计算 -->
<Heavy1 />
<Heavy2 />
<Heavy3 />
<Heavy4 />
</div>
</template>
这将会占用很长时间,导致帧数下降、卡顿,其实可以使用分批渲染的方式来进行优化,就是先渲染一部分,再渲染另一部分:
下面是分批运行组件的算法
<template>
<div>
<Heavy v-if="defer(1)" />
<Heavy v-if="defer(2)" />
<Heavy v-if="defer(3)" />
<Heavy v-if="defer(4)" />
</div>
</template>
<script>
export default {
data() {
return {
displayPriority: 0
}
},
mounted() {
this.runDisplayPriority()
},
methods: {
runDisplayPriority() {
const step = () => {
requestAnimationFrame(() => {
this.displayPriority++
if (this.displayPriority < 10) {
step()
}
})
}
step()
},
defer(priority) {
return this.displayPriority >= priority
}
}
}
</script>