defer表示延迟加载,针对大量节点的渲染加载,结合使用关键帧requestAnimationFrame的形式来分片加载,可以优化白屏时间
知识补充:
requestAnimationFrame
- requestAnimationFrame 是根据帧数来执行回调函数的,就是屏幕一帧,那 requestAnimationFrame就会执行一次。一般屏幕是60帧,也就是一秒执行60次回调函数.
- 性能相对定时器settimeout好,因为定时器执行权限在同步任务 微任务之后,会受到其他任务影响。
与 setTimeout 相比,requestAnimationFrame 最大的优势是由系统来决定回调函数的执行时机。具体一点讲,如果屏幕刷新率是60Hz,那么回调函数就每16.7ms被执行一次,如果刷新率是75Hz,那么这个时间间隔就变成了1000/75=13.3ms,换句话说就是,requestAnimationFrame 的步伐跟着系统的刷新步伐走。它能保证回调函数在屏幕每一次的刷新间隔中只被执行一次,这样就不会引起丢帧现象,也不会导致动画出现卡顿的问题。
解决方案:分帧渲染策略
VUE2.x
<template>
<div>
<div class="container">
<div v-for="n in 100" :key="n+'n'">
<div class="item_container" v-if="defer(n)">
<div class="item_child" v-for="m in 6000" :key="m+'b'"></div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'page_Test',
data() {
return {
nodeCount: 0,
}
},
mounted() {
方法一
let maxNodeCount = 100
const refreshNodeCount = () => {
requestAnimationFrame((timestamp) => {
console.log(timestamp, 'timestamp');//当前帧执行回调时的时间戳(以毫秒为单位,高精度小数)
this.nodeCount++
if (this.nodeCount < maxNodeCount) {
refreshNodeCount()
}
})
}
refreshNodeCount()
方法二
this.update()
},
methods: {
update(){
let maxNodeCount = 100
requestAnimationFrame((timestamp) => {
console.log(timestamp, 'timestamp');//当前帧执行回调时的时间戳(以毫秒为单位,高精度小数)
this.nodeCount++
if (this.nodeCount < maxNodeCount) {
this.update()
}
})
},
defer(n){
// return 当前页面第几帧渲染 >= n;
return this.nodeCount >= n
},
}
</script>
<style lang="less" scoped>
// defer延迟加载
.container {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-gap: 1em;
.item_container {
display: flex;
flex-wrap: wrap;
justify-content: center;
border: 3px solid lightblue;
}
.item_child {
width: 5px;
height: 3px;
background-color: #ccc;
margin: 0.1em;
}
}
</style>
或者封装方法,Mixins混入使用
export default function(maxNodeCount) {
return {
data() {
return {
nodeCount: 0
}
},
mounted() {
const refreshFrameCount = () => {
requestAnimationFrame(() => {
this.nodeCount++
if (this.nodeCount < maxNodeCount) {
refreshFrameCount()
}
})
}
refreshFrameCount()
},
methods: {
defer(n) {
// return 当前页面第几帧渲染 >= n;
return this.nodeCount >= n
}
}
}
}
VUE3.x
封装useDefer.js
import { ref } from 'vue'
const nodeCount = ref(0)
function update() {
nodeCount.value++;
requestAnimationFrame(update)
}
update();
export function useDefer() {
return function (n) {
return nodeCount.value >= n
}
}
组件使用
<template>
<div>
<div class="container">
<div v-for="n in 100" :key="n+'n'">
<div class="item_container" v-if="defer(n)">
<div class="item_child" v-for="m in 6000" :key="m+'b'"></div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { useDefer} from './useDefer.js'
const defer= useDefer();
</script>
<style lang="less" scoped>
// defer延迟加载
.container {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-gap: 1em;
.item_container {
display: flex;
flex-wrap: wrap;
justify-content: center;
border: 3px solid lightblue;
}
.item_child {
width: 5px;
height: 3px;
background-color: #ccc;
margin: 0.1em;
}
}
</style>
这样,就不会影响页面加载前一直显示白屏了~