前端性能优化系列——优化白屏时间(一)⚡

一、前言

前端优化是一个永恒的话题,每个前端开发者都希望自己的页面能够快速加载,给用户良好的体验,但是在实际开发中,我们经常会遇到一个棘手的问题,那就是首页白屏问题。

首页白屏问题是指用户打开页面后,需要等待很长时间才能看到页面内容,这会让用户感到不耐烦,甚至流失,造成白屏的原因有很多,比如网络延迟、JS 加载过多、渲染性能不佳等等,针对不同的原因,我们需要采取不同的优化策略。

今天和大家讨论一种常见的场景,以及其中的一种优化手段,这种优化手段可能很多人都不知道,但是它非常有效,可以大幅度提升用户的感知效率。

二、场景回顾

js 复制代码
<template>
  <div class="container">
    <div v-for="n in 100">
      <heavy-comp></heavy-comp>
    </div>
  </div>
</template>

<script setup>
  import HeavyComp from './components/HeavyComp.vue';
</script>

这里我们导入了一个叫做 HeavyComp 的组件,这个组件里有很多很多的元素,特别是在大屏项目里元素可能特别密集,然后我们将这个组件循环了 100 次,每一次循环渲染一次,我们来看一下它的运行效果。

我们会发现有长时间的等待,最后一起把这些组件渲染出来,然后我们使用浏览器的调试工具 Performance 做一下性能分析:

可以看出 2686 毫秒在加载 JS,2795 毫秒在渲染,这样的页面对于用户而言是完全无法接受的,那么怎么来优化呢?

三、如何优化

其实主要的思路就是让这些组件一部分一部分的渲染,一起渲染压力太大,要分摊一下,先渲染其中一部分,让用户能够看到,然后再持续的渲染剩下的部分。虽然总的时间没有变化,甚至还有可能会增加,但是对于用户的感知而言,从用户打开页面到看到东西这段时间被大幅度的缩减了。这里我们先直接使用写好的优化方法:

js 复制代码
<template>
  <div class="container">
    <div v-for="n in 100">
      <!-- 在渲染组件的时候,我们可以利用 defer 函数来进行 if 判断 -->
      <heavy-comp v-if="defer(n)"></heavy-comp>
    </div>
  </div>
</template>

<script setup>
import HeavyComp from './components/HeavyComp.vue';
// 导入一个我已经写好的函数
import { useDefer } from './useDefer';
// 这个函数返回一个方法
const defer = useDefer();
</script>

那么这个 defer 函数有什么作用呢?关于渲染帧大家应该都知道,就是在一个渲染时间线内,它是分成很多小段,每一段都是一个渲染帧,每一个渲染帧的间隔是 16.6 毫秒。

假如说第一个渲染帧叫第一次渲染,第二个渲染帧叫第二次渲染,依次类推,那么这个 defer(n) 表达的意思就是,目前渲染帧有没有超过这个 n

举个例子:比如说 defer(10),传入 10,表达的意思是当渲染的帧数是第一帧的时候,返回 false,第二帧的时候还是返回 false,只有当渲染帧大于等于第十帧的时候才返回 true,就这么简单。

当它返回 false 的时候,由于我们使用了 v-if,那么这个组件它就不会渲染,换句话说,这里给它传一个 n,就表示它循环了 100 次,第一个组件的时候 n 是 1,就表示第一帧的时候渲染第一个组件,第二个组件的时候 n 是 2,就表示第二帧的时候渲染第二个组件,依此类推。

我们来看一下优化后页面的渲染:

我们发现很快就得到了响应,尽管总时间没有变化,因为它还在持续渲染,但是用户感知到它的效率已经大大提升了,我们再次用性能分析工具分析一下。

可以看到,渲染过程被分成了一帧一帧进行渲染,避免了页面长时间的卡顿,每一次渲染用户都能看到它的渲染结果。这有点像 React 的 fiber 架构,在 Vue3 中也有类似的概念,其实都是利用浏览器空闲时间来进行任务调度和拆分。

接下来我们来看看这个函数到底是怎么实现的。

四、useDefer

js 复制代码
import { ref } from "vue";
// 函数接收一个参数,表示监测的最大帧数,这里默认值是 1000
// 如果说你渲染的东西特别多可以传入一个值
export function useDefer(maxFrameCount = 1000) {
  // 然后开始计数
  const frameCount = ref(0);
  const refreshFrameCount = () => {
    requestAnimationFrame(() => {
      // 每一次 requestAnimationFrame 就计数加一
      // 表示当前渲染的帧数变多了一帧
      frameCount.value++;
      // 只要当前帧数小于最大帧数就递归执行
      if (frameCount.value < maxFrameCount) {
        refreshFrameCount();
      }
    });
  };
  refreshFrameCount();
  // 返回一个函数,接收传递进来的 n
  return function (showInFrameCount) {
    // 判断当前渲染的帧数有没有大于 n
    return frameCount.value >= showInFrameCount;
  };
}

五、总结

前端优化之白屏问题是一个非常重要而又常见的问题,我们需要根据不同场景采取不同策略。本文介绍了其中一种场景和优化方法,即利用 defer 函数来控制组件何时进行渲染,让页面内容逐步展现出来,而提高用户感知效率和体验。当然还有其他优化方法和技巧,比如使用骨架屏、预加载、懒加载、代码分割。

本文参考该篇文章,以及渡一教育袁老师的短视频讲解分享。

相关推荐
程序员凡尘26 分钟前
完美解决 Array 方法 (map/filter/reduce) 不按预期工作 的正确解决方法,亲测有效!!!
前端·javascript·vue.js
编程零零七4 小时前
Python数据分析工具(三):pymssql的用法
开发语言·前端·数据库·python·oracle·数据分析·pymssql
(⊙o⊙)~哦6 小时前
JavaScript substring() 方法
前端
无心使然云中漫步6 小时前
GIS OGC之WMTS地图服务,通过Capabilities XML描述文档,获取matrixIds,origin,计算resolutions
前端·javascript
Bug缔造者6 小时前
Element-ui el-table 全局表格排序
前端·javascript·vue.js
xnian_7 小时前
解决ruoyi-vue-pro-master框架引入报错,启动报错问题
前端·javascript·vue.js
罗政7 小时前
[附源码]超简洁个人博客网站搭建+SpringBoot+Vue前后端分离
vue.js·spring boot·后端
麒麟而非淇淋8 小时前
AJAX 入门 day1
前端·javascript·ajax
2401_858120538 小时前
深入理解MATLAB中的事件处理机制
前端·javascript·matlab
阿树梢8 小时前
【Vue】VueRouter路由
前端·javascript·vue.js