Object.freeze冻结属性和v-if结合requestAnimationFrame分帧渲染解决白屏

计算100W条数据的长度造成2s延迟

<template>
  <div>
    <h1>数据总长度{{ arrList.length }}</h1>
  </div>
</template>
<script>
  export default {
    data(){
      return {
        arrList: []
      }
    },
    created(){
      console.time('赋值代码耗时')
      // 我们发现这里赋值耗时1.8s
      this.arrList= this.getNewListApi()
      console.timeEnd('赋值代码耗时')
    },
    methods: {
      getNewListApi(){
        let arr = []
        // 100w次循环需要78ms
        console.time('100w次循环耗时')
        for(let i=0;i<1000000;i++){
          arr.push({
            name: '张三' + i,
            age: i+10,
            add:'xxx',
            info:'喜欢上班'
          })
        }
        console.timeEnd('100w次循环耗时')
        return arr
      }
    }
  }
</script>

使用 Object.freeze 提升效率

created(){
  console.time('赋值代码耗时')
  this.arrList= Object.freeze(this.getNewListApi())
  console.timeEnd('赋值代码耗时')
},

由原来的1.7s变为现在93ms

什么使用 Object.freeze会提升效率呢?

我们都知道vue2中

会把放在data中的数据变成响应式数据。

this.arrList= this.getNewListApi()

这一行代码会把arrList中的100w条数据变成响应式数据。

这样就造成消耗。

实际上我们只需要进行展示,不会做修改操作。

因此我们可以使用 Object.freeze 来冻结数据。

不让它变成响应式数据。从而提升效率。

在我们实际开发中像纯列表(不会做修改,删除,添加,按条件搜索)

我们都可以使用 Object.freeze 来冻结数据。

学习 Object.freeze

Object.freeze() 可以让一个对象被冻结

这个被冻结的对象:

1.不能添加属性,不能删除属性,不能修改属性的值

2.不能更改它们的可枚举性

3.对象的原型也不能被重新指定

freeze()返回与传入的对象相同的对象。

let obj={
  name:'张三',
  age: 70
}
let newObj = Object.freeze(obj)
newObj.newAddRess = '王者峡谷' // 不能新增属性
delete newObj.name // 不能删除属性
newObj.age = 50 // 不能修改属性
console.log(newObj)
//输出 {"name": "张三","age": 70}

5w条数据渲染出现白屏

<template>
  <div>
    <div v-for="(item,index) in 50000" :key="index" :data-aIndex="index">
        <h4>姓名</h4>
        <h4>年龄</h4>
        <h4>地址</h4>
        <h4>爱好</h4>
        <h4>序号{{ index }}</h4>
    </div>
  </div>
</template>

使用v-if结合requestAnimationFrame让它延迟渲染

// 自定义hook
import { onMounted, onUnmounted, ref } from "vue"
import { onUnmounted, ref } from "vue"
export function useDeferRender(allEleNumber=100){
  const frameCount = ref(0);
  let requestId;
  // 刷新帧的函数
  function updateFrameCount(){
    requestId = requestAnimationFrame(()=>{
      frameCount.value++;
      // 当前渲染的帧数大于最大帧数的时候就停止渲染 (说明元素已经渲染完了)
      if(frameCount.value >= allEleNumber){
        return
      }
      updateFrameCount()
    })
    console.log(' frameCount.value',  frameCount.value)
  };
  // 一开始进入就执行这个函数
  updateFrameCount();
  // 卸载的时候取消掉
  onUnmounted(()=>{
    cancelAnimationFrame(requestId)
  })
  // 会返回一个true或者false.来控制是否渲染,让它逐帧渲染
  return function deferBool(n){
    // 目前渲染了多少帧 >= 这个元素是在第几帧渲染
    return frameCount.value >=n
  };
}

<template>
  <div>
    <div v-for="(item,index) in 50000" :key="index" :data-aIndex="index">
      <template v-if="deferBool(index)">
        <h4>姓名</h4>
        <h4>年龄</h4>
        <h4>地址</h4>
        <h4>爱好</h4>
        <h4>序号{{ index }}</h4>
      </template>
    </div>
  </div>
</template>
<script setup>
import {useDeferRender} from '../hooks/useDeferRender.js'
let deferBool = useDeferRender(50000)
</script>

requestAnimationFrame的简单介绍

window.requestAnimationFrame()方法告诉浏览器你希望执行一个动画(一段代码)。

它要求浏览器在下一次重绘之前调用指定的回调函数。

对回调函数的调用频率通常与显示器的刷新率相匹配。

有60hz、75hz、120hz、144hz 。

但是最常见的刷新率还是 60hz(每秒 60 帧)。

它的返回值是一个long 类型整数值。

是在回调列表里的唯一标识符。

你可以将此值传递给 window.cancelAnimationFrame() 函数以取消该刷新回调请求