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 = 100const refreshNodeCount = () => {requestAnimationFrame((timestamp) => {console.log(timestamp, 'timestamp');//當前幀執行回調時的時間戳(以毫秒為單位,高精度小數)this.nodeCount++if (this.nodeCount < maxNodeCount) {refreshNodeCount()}})}refreshNodeCount()方法二this.update()},methods: {update(){let maxNodeCount = 100requestAnimationFrame((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>
這樣,就不會影響頁面加載前一直顯示白屏了~