概览
1.函数型组件优化:使用函数型组件。只要在模板上声明functional属性,就可以实现函数式组件。
- 1-1.优化前:
<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>
- 1-2.优化后:有组件生命周期的有很多耗时处理,而作为函数式组件处理提升页面效率。
- ①组件的生命周期处理在框架层面上十分耗时,建议平常尽量使用函数型组件。可以避免不必要的性能损失。只要在模板上声明functional属性,就可以实现函数式组件。
- ②函数式组件特点:
- α:没有管理任何状态。
- β:没有监听任何传递给它的状态。
- γ:没有生命周期方法。
- ζ:只是接收一些prop的函数。
- ③适用简单的展示组件。高阶组件用于接收一个组件作为参数,返回一个被包装过的组件。v-for循环中每项通常都是很好的用做函数式组件的候选项。
<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>
2.子组件拆分:子组件拆分,将重的函数放在子组件中,因为父子组件生命周期更新的问题。
- 2-1.优化前:
<template>
<div :style="{ opacity: number / 300 }">
<div>{{ heavy() }}</div>
</div>
</template>
<script>
export default {
props: ['number'],
methods: {
heavy () { /* HEAVY TASK */ }
}
}
</script>
- 2-2.优化后:将重的函数放在子组件中,因为父子组件生命周期更新的问题。
<template>
<div :style="{ opacity: number / 300 }">
<ChildComp/>
</div>
</template>
<script>
export default {
props: ['number'],
components: {
ChildComp: {
methods: {
heavy () { /* HEAVY TASK */ }
},
render (h) {
return h('div', this.heavy())
}
}
}
}
</script>
3.局部变量:.局部变量,将计算属性赋值给局部变量,防止重复计算。
- 3-1.优化前:
<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>
- 3-2.优化后:使用计算属性的时候,把计算属性赋值给一个局部变量,防止重复计算,提高效率。
<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>
4.活用 v-show 减少v-if:
- 4-1.优化前:
<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>
- 4-2.优化后:v-if,是真正的销毁渲染,比较耗性能,v-show不会操作DOM树,性能高。
<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>
5.Keep-alive:缓存不活跃的组件。
- 5-1.优化前:
<template>
<div id="app">
<router-view/>
</div>
</template>
- 5-2.优化后:缓存不活跃的组件。
<template>
<div id="app">
<keep-alive>
<router-view/>
</keep-alive>
</div>
</template>
6.活用延迟功能:
- 6-1.优化前:
<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>
- 6-2.优化后:提高用户体验,使用defer让一部分逻辑先执行,另一部分逻辑后执行。
<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 mixin:
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
}
}
}
}
7.分批处理:
- 7-1.优化前:
fetchItems ({ commit }, { items }) {
commit('clearItems')
commit('addItems', items)
}
- 7-2.优化后:分批处理,提高画面渲染性能。
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()
}
8.非响应式数据:将不修改的数据冻结。或者将不需要响应的数据不放在data里面。
- 8-1.优化前:
const data = items.map(
item => ({
id: uid++,
data: item,
vote: 0
})
)
- 8-2.优化后:非响应模式(非观察模式),显示声明为非响应式,不想让vue框架去观察data,提高数据响应效率。或者使用Object.freeze()冻结数据。
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
}
9.仅渲染可视化部分:
- 9-1.优化前:
<div class="items no-v">
<FetchItemViewFunctional
v-for="item of items"
:key="item.id"
:item="item"
@vote="voteItem(item)"
/>
</div>
- 9-2.优化后:仅渲染可视化部分,批量渲染
<recycle-scroller
class="items"
:items="items"
:item-size="24"
>
<template v-slot="{ item }">
<FetchItemView
:item="item"
@vote="voteItem(item)"
/>
</template>
</recycle-scroller>