Vue项目使用defer优化页面白屏,性能优化提升,秒加载!!!

发布于:2025-06-25 ⋅ 阅读:(16) ⋅ 点赞:(0)

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>

这样,就不会影响页面加载前一直显示白屏了~


网站公告

今日签到

点亮在社区的每一天
去签到