翻到一個star,VUE 效能優化給大家分享一下。
業精於勤,行成於思.
Vue 9 Perf Secrets
栗子
- Functional components
- Child component splitting
- Local variables
- Reuse DOM with v-show
- Keep alive(DOM-Reusing Router View)
- Deferred features
- Vuex demo
原始碼中有個 Static
被註釋,沒有達到作者的優化效果。
吃栗子
栗子中都分別寫在On,Off的對應元件檔案中。對比兩份程式碼。
Functional components
FunctionalOff
<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>
複製程式碼
FunctionalOn
<template functional>
<div class="cell">
<div v-if="props.value" class="on"></div>
<section v-else class="off"></section>
</div>
</template>
複製程式碼
Child component splitting
ChildOff
<template>
<div :style="{ opacity: number / 300 }">
<div>{{ heavy() }}</div>
</div>
</template>
<script>
export default {
props: ['number'],
methods: {
heavy () {
const n = 100000
let result = 0
for (let i = 0; i < n; i++) {
result += Math.sqrt(Math.cos(Math.sin(42)))
}
return result
},
},
}
</script>
複製程式碼
ChildOn
<template>
<div :style="{ opacity: number / 300 }">
<ChildComp/>
</div>
</template>
<script>
export default {
components: {
ChildComp: {
methods: {
heavy () {
const n = 100000
let result = 0
for (let i = 0; i < n; i++) {
result += Math.sqrt(Math.cos(Math.sin(42)))
}
return result
},
},
render (h) {
return h('div', this.heavy())
},
},
},
props: ['number'],
}
</script>
複製程式碼
Local variables
LocalOff
<template>
<div :style="{ opacity: start / 300 }">{{ result }}</div>
</template>
<script>
export default {
props: ['start'],
computed: {
base () {
return 42
},
result () {
let result = this.start
for (let i = 0; i < 1000; i++) {
result += Math.sqrt(Math.cos(Math.sin(this.base))) + this.base * this.base + this.base + this.base * 2 + this.base * 3
}
return result
},
},
}
</script>
複製程式碼
LocalOn
<template>
<div :style="{ opacity: start / 300 }">{{ result }}</div>
</template>
<script>
export default {
props: ['start'],
computed: {
base () {
return 42
},
result ({ base, start }) {
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>
複製程式碼
Reuse DOM with v-show
HideOff
<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>
複製程式碼
HideOn
<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 (DOM-Reusing Router View)
<template>
<Benchmark title="DOM-Reusing Router View" class="keep-alive">
<template #toolbar>
<VueGroup v-model="page">
<VueGroupButton :value="false">Simple page</VueGroupButton>
<VueGroupButton :value="true">Heavy page</VueGroupButton>
</VueGroup>
<PlayToggle v-model="play"/>
</template>
<template #on>
<keep-alive>
<router-view/>
</keep-alive>
</template>
<template #off>
<router-view/>
</template>
</Benchmark>
</template>
<script>
export default {
data () {
return {
play: false,
}
},
computed: {
page: {
get () {
return this.$route.name === 'bench-keep-alive-heavy'
},
set (value) {
if (value) {
this.$router.push({ name: 'bench-keep-alive-heavy' })
} else {
this.$router.push({ name: 'bench-keep-alive' })
}
},
},
},
watch: {
play (value) {
if (value) {
this.togglePage()
} else {
clearTimeout(this.$_timer)
}
},
},
created () {
this.count = 300
},
methods: {
togglePage () {
this.page = !this.page
if (this.play) {
this.$_timer = setTimeout(this.togglePage, 2000)
}
},
},
}
</script>
<style lang="stylus" scoped>
.keep-alive
.router-multi-view
height 100%
>>>
.simple-page,
.deferred-off
height 100%
display flex
flex-direction column
align-items center
padding 40px
box-sizing border-box
h2:not(:last-child)
margin-bottom 24px
</style>
複製程式碼
Deferred features
DeferredOff
<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>
複製程式碼
DeferredOn
<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>
複製程式碼
mixin Defer
export default function (count = 10) {
// @vue/component
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
},
},
}
}
複製程式碼
Vuex demo
最後一個給大家好奇去看程式碼。渲染10000條資料,一個是creash,優化後的沒壓力。