沐游虞笔记
  • 前端面试题

    • HTML面试题汇总(无答案)
    • HTML面试题汇总
    • CSS 面试题汇总(无答案)
    • CSS 面试题汇总
    • javascript 面试题汇总(无答案)
    • javascript 面试题汇总
    • promise 面试题(无答案)
    • promise 面试题
    • 浏览器面试题汇总(无答案)
    • 浏览器面试题汇总
    • 网络面试题汇总(无答案)
    • 网络面试题汇总
    • 工程化面试题汇总(无答案)
    • 工程化面试题汇总
    • VUE面试题汇总(无答案)
    • VUE面试题汇总
  • 直播课文件

    • 静态页面学习指导
    • 属性的计算过程
    • 层叠继承规则总结
    • BFC
    • JS基础知识回顾
    • DOM 事件的传播机制
    • DOM 事件的注册和移除
    • 阻止事件默认行为
    • 基础领航考试题
    • 基础领航考试题(答案)
    • 2024前端发展
    • JS核心概念学习指导
    • 第三方库与工程化学习指导
    • Vue入门学习指导
    • vue进阶学习指导
    • 前端性能优化
  • 笔面试环节知识讲解

    • 目录
    • 图像处理
    • 图像处理(面试)
    • Webpack构建优化
    • Webpack构建优化(面试)
    • TTS性能优化
    • TTS性能优化(面试)
    • 实时协作
    • 实时协作(面试)
    • 网页复制图片到剪贴板
    • 网页复制图片到剪贴板(面试)
    • vite插件
    • vite插件(面试)
    • 表单数据同步与保持
    • 表单数据同步与保持(面试)
    • 优化虚拟列表
    • 优化虚拟列表(面试)
    • 微前端解决巨石应用
    • 微前端解决巨石应用(面试)
    • DNS解析与优化
    • DNS解析与优化(面试)
    • 前端监控
    • 前端监控(面试)
    • 12.跨标签页通信
    • 12.跨标签页通信(面试)
    • 13.Vite相关优化
    • 13.Vite相关优化(面试)
    • 14.计时器节流问题
    • 14.计时器节流问题(面试)
    • 15.多文件预览支持
    • 15.多文件预览支持(面试)
    • 16.defer优化白屏时间
    • 16.defer优化白屏时间(面试)
  • Vue3整体变化
  • Vue2响应式回顾
  • Vue3响应式变化
  • nextTick实现原理
  • 两道代码题
  • Vue运行机制
  • 渲染器核心功能
  • 事件绑定与更新
  • computed面试题
  • watch面试题
  • 图解双端diff
  • 图解快速dff
  • 最长递增子序列
  • 模板编译器
  • 模板编译提升
  • 组件name作用
  • 路由传参方式
  • 基础篇

    • 序章React介绍
    • JSX基础语法
    • React基本介绍
    • 表单
    • 生命周期
    • 组件与事件绑定
    • 组件状态与数据传递
    • Hooks
    • React--redux介绍
    • React-router介绍
  • 就业篇

    • 属性默认值和类型验证
    • 高阶组件
    • Ref
    • Context
    • Render Props
    • Portals
    • 错误边界
    • 组件渲染性能优化
    • 前端框架的理解
    • Reacti和Vue描述页面的区别
    • 前端框架的分类
    • 虚拟DoM
    • React整体架构
    • React渲染流程
    • Fiber双缓冲
    • MessageChannel
    • Scheduleri调度普通任务
    • Scheduleri调度延时任务
    • 最小堆
    • React中的位运算
    • beginWork工作流程
    • completeWork工作流程
    • 图解diff算法
    • commit工作流程
    • lane模型
    • React中的事件
    • Hooks原理
    • useStateuseReducer.
    • effect相关hook
    • useCallbackuseMemo
    • useRef
    • Update
    • 性能优化策略之eagerState
    • 性能优化策略之bailout
    • bailoutContextAPl
    • 性能优化对日常开发启示
  • 前端监控概述
  • 错误监控
  • 数据上报
  • 页面性能监控
  • 用户行为收集与埋点
  • CSS3手册
  • HTML5手册
  • JavaScript语言提升

    • es补充
    • 事件循环
    • promise基础
    • Promise的链式调用
    • Promise的静态方法
    • async和await
    • Promise相关面试题
  • 网络

    • 客户端与服务器
    • 关于 Apifox 的使用
  • git文档
  • 工程化

    • CommonJS
    • ES module
    • npm文档(包管理)
    • Lass笔记
    • webpack工具
  • canvas详解
  • uinapp笔记
  • 自动化测试
  • oauth2令牌

    • 认识Oauth2
    • 三方应用实现github授权
    • 微信三方应用登录实现
    • 支付宝沙箱支付功能
  • 前端面试题

    • HTML面试题汇总(无答案)
    • HTML面试题汇总
    • CSS 面试题汇总(无答案)
    • CSS 面试题汇总
    • javascript 面试题汇总(无答案)
    • javascript 面试题汇总
    • promise 面试题(无答案)
    • promise 面试题
    • 浏览器面试题汇总(无答案)
    • 浏览器面试题汇总
    • 网络面试题汇总(无答案)
    • 网络面试题汇总
    • 工程化面试题汇总(无答案)
    • 工程化面试题汇总
    • VUE面试题汇总(无答案)
    • VUE面试题汇总
  • 直播课文件

    • 静态页面学习指导
    • 属性的计算过程
    • 层叠继承规则总结
    • BFC
    • JS基础知识回顾
    • DOM 事件的传播机制
    • DOM 事件的注册和移除
    • 阻止事件默认行为
    • 基础领航考试题
    • 基础领航考试题(答案)
    • 2024前端发展
    • JS核心概念学习指导
    • 第三方库与工程化学习指导
    • Vue入门学习指导
    • vue进阶学习指导
    • 前端性能优化
  • 笔面试环节知识讲解

    • 目录
    • 图像处理
    • 图像处理(面试)
    • Webpack构建优化
    • Webpack构建优化(面试)
    • TTS性能优化
    • TTS性能优化(面试)
    • 实时协作
    • 实时协作(面试)
    • 网页复制图片到剪贴板
    • 网页复制图片到剪贴板(面试)
    • vite插件
    • vite插件(面试)
    • 表单数据同步与保持
    • 表单数据同步与保持(面试)
    • 优化虚拟列表
    • 优化虚拟列表(面试)
    • 微前端解决巨石应用
    • 微前端解决巨石应用(面试)
    • DNS解析与优化
    • DNS解析与优化(面试)
    • 前端监控
    • 前端监控(面试)
    • 12.跨标签页通信
    • 12.跨标签页通信(面试)
    • 13.Vite相关优化
    • 13.Vite相关优化(面试)
    • 14.计时器节流问题
    • 14.计时器节流问题(面试)
    • 15.多文件预览支持
    • 15.多文件预览支持(面试)
    • 16.defer优化白屏时间
    • 16.defer优化白屏时间(面试)
  • Vue3整体变化
  • Vue2响应式回顾
  • Vue3响应式变化
  • nextTick实现原理
  • 两道代码题
  • Vue运行机制
  • 渲染器核心功能
  • 事件绑定与更新
  • computed面试题
  • watch面试题
  • 图解双端diff
  • 图解快速dff
  • 最长递增子序列
  • 模板编译器
  • 模板编译提升
  • 组件name作用
  • 路由传参方式
  • 基础篇

    • 序章React介绍
    • JSX基础语法
    • React基本介绍
    • 表单
    • 生命周期
    • 组件与事件绑定
    • 组件状态与数据传递
    • Hooks
    • React--redux介绍
    • React-router介绍
  • 就业篇

    • 属性默认值和类型验证
    • 高阶组件
    • Ref
    • Context
    • Render Props
    • Portals
    • 错误边界
    • 组件渲染性能优化
    • 前端框架的理解
    • Reacti和Vue描述页面的区别
    • 前端框架的分类
    • 虚拟DoM
    • React整体架构
    • React渲染流程
    • Fiber双缓冲
    • MessageChannel
    • Scheduleri调度普通任务
    • Scheduleri调度延时任务
    • 最小堆
    • React中的位运算
    • beginWork工作流程
    • completeWork工作流程
    • 图解diff算法
    • commit工作流程
    • lane模型
    • React中的事件
    • Hooks原理
    • useStateuseReducer.
    • effect相关hook
    • useCallbackuseMemo
    • useRef
    • Update
    • 性能优化策略之eagerState
    • 性能优化策略之bailout
    • bailoutContextAPl
    • 性能优化对日常开发启示
  • 前端监控概述
  • 错误监控
  • 数据上报
  • 页面性能监控
  • 用户行为收集与埋点
  • CSS3手册
  • HTML5手册
  • JavaScript语言提升

    • es补充
    • 事件循环
    • promise基础
    • Promise的链式调用
    • Promise的静态方法
    • async和await
    • Promise相关面试题
  • 网络

    • 客户端与服务器
    • 关于 Apifox 的使用
  • git文档
  • 工程化

    • CommonJS
    • ES module
    • npm文档(包管理)
    • Lass笔记
    • webpack工具
  • canvas详解
  • uinapp笔记
  • 自动化测试
  • oauth2令牌

    • 认识Oauth2
    • 三方应用实现github授权
    • 微信三方应用登录实现
    • 支付宝沙箱支付功能
  • 必看导言

    • 就业的核心问题

      • 课件
    • 表现力的训练

      • 课件
  • 简历制作

    • 课件
    • 简历准备课堂笔记
    • 个人信息课堂笔记
    • 求职意向课堂笔记
    • 技术栈课堂笔记
    • 教育经历课堂笔记
    • 个人优势与评价课堂笔记
    • 工作经历课堂笔记
    • 项目
    • 项目亮难点问题

    • 项目经历

  • 项目准备

    • 课件
    • 课件
  • 技术重点

    • 划重点

    • 非技术环节
  • 简历投递和面试准备

    • 简历投递
    • 面试准备
  • 笔面试环节知识讲解
  • 项目准备
  • 难点攻关
  • defer优化白屏时间
luzhichang
2024-09-18
目录

16-技术讲解

# defer优化白屏时间

# 技术讲解

# 什么问题

当我们出现白屏时间太久的情况,那当然就需要进行相关优化,关于白屏出现的情况非常的多,这个当然我们需要通过指标,进行相关的分析,得出结论。

其中一种最常见的导致页面白屏时间过长的原因,就是界面需要渲染的内容过多,渲染时间过长导致的白屏问题

# 解决思路

由于页面复杂,所以首先考虑的优化手段是方向明确、效果明确的,不会对页面业务造成过多影响的优化手段。考虑的以下的手段:

  • 骨架屏
  • SSR DOM直出
  • 分屏加载

骨架屏虽然有利于FCP的数据展现,但是指标不治本,用户的需求是能在首屏马上看到内容,而不是用一个骨架屏的假象替换,只能说给用户心理安慰,当前页面在做事情,请稍等。但是有一些用户甚至会十分厌恶骨架屏的出现,因为出现就意味着速度慢。

SSR DOM直出可以直观的减少页面二次请求和渲染,但是如果本身页面就有很多内容需要渲染的话,也并不是最合适的手段,而且SSR并不是说上就上的,我们还需要考虑成本的问题。计算的成本是抛给前端,还是全部压在后端,这是需要平衡的。后端需要的算力不一样,所需要的服务器也就不一样,成本当然也就不一样。

分屏加载,主要是利用IntersectionObserver API (opens new window)观察 DOM 元素是否在视口内的方式实现懒加载,当然这种一般都用在页面比较有明显分界线,比如分页情况,比如图片的懒加载等等。

但是我们的要求是一开始就需要渲染出所有的内容,比如本身当前视口的DOM内容就非常复杂,只不过相当多的内容是出于隐藏状态的,只有触发某个事件的时候,才会显示相应内容,这种情况分屏显示并不能很好的处理。

  • requestIdleCallback (opens new window)空闲时优化渲染

requestIdleCallback 可以在浏览器的空闲时间执行一些低优先级的任务,从而避免阻塞主线程,提高页面的响应速度。

  • requestAnimationFrame (opens new window)分帧渲染

requestAnimationFrame 是一个用于在下一个浏览器重绘之前执行回调函数的方法。它可以用于创建流畅的动画效果

# 解决细节

# 1、web-vitals (opens new window)

# 2、requestAnimationFrame (opens new window)

可能我们所知道的requestAnimationFrame 主要作用是用于动画,但它也非常适合用于优化页面的 DOM 渲染,它可以将一系列 DOM 操作分散到多个帧中进行,从而避免阻塞主线程,提升页面的响应速度和流畅度。

浏览器的渲染机制:

  • 浏览器每秒会尝试刷新屏幕 60 次(60fps),每次刷新(忽略解析Parse阶段)都会经历以下几个步骤:
    • 计算样式(Style Calculations)
    • 布局(Layout)
    • 绘制(Paint)
    • 合成(Composite)
  • requestAnimationFrame 会在浏览器进行这些步骤之前调用你的回调函数,这样可以确保 DOM 操作和样式计算在同一个刷新周期内完成,避免多次重排和重绘。

结合setInterval对比理解:

setInterval动画的情况

image-20240709105643061

使用RAF的情况:

image-20240709110002666

避免阻塞主线程:

  • 当你一次性进行大量 DOM 操作时,会阻塞主线程,导致页面卡顿。通过将这些操作分散到多个帧中,requestAnimationFrame 可以将大任务分解成小任务,逐步完成,减少主线程的阻塞时间。
  • requestAnimationFrame 会根据屏幕刷新率进行调用,确保每次 DOM 操作都尽可能在下一次重绘之前完成,从而提供平滑的用户体验。

# 逐帧渲染 DOM 的步骤

  1. 分解大任务:
    • 将一个大的 DOM 操作任务分解为多个小任务,每个任务操作一部分 DOM。
  2. 递归调用 requestAnimationFrame:
    • 使用递归的方式逐步执行这些小任务,每次执行完一个小任务后,调用 requestAnimationFrame 进行下一次渲染。
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Progressive List Rendering</title>
    <style>
      * {
        margin: 0;
        padding: 0;
      }
      li {
        margin: 8px;
        padding: 10px;
        list-style: none;
      }
    </style>
  </head>
  <body>
    <ul id="list"></ul>
    <script>
      const list = document.getElementById("list");
      const items = Array.from({ length: 10000 }, (_, i) => `Item ${i + 1}`);

      // 直接循环渲染
      /* function renderItems(startIndex, batchSize) {
        for (
          let i = startIndex;
          i < startIndex + batchSize && i < items.length;
          i++
        ) {
          const li = document.createElement("li");
          li.textContent = items[i];
          // 随机背景颜色
          li.style.backgroundColor = `#${Math.floor(
            Math.random() * 16777215
          ).toString(16)}`;
          list.appendChild(li);
        }
      }

      // 直接循环调用renderItems,每次20
      for (let i = 0; i <= 10000; i += 20) {
        renderItems(i, 20);
      } */

			// 分帧渲染
      function renderItems(startIndex, batchSize) {
        for (
          let i = startIndex;
          i < startIndex + batchSize && i < items.length;
          i++
        ) {
          const li = document.createElement("li");
          li.textContent = items[i];
          // 随机背景颜色
          li.style.backgroundColor = `#${Math.floor(
            Math.random() * 16777215
          ).toString(16)}`;
          list.appendChild(li);
        }

        if (startIndex + batchSize < items.length) {
          requestAnimationFrame(() =>
            renderItems(startIndex + batchSize, batchSize)
          );
        }
      }

      requestAnimationFrame(() => renderItems(0, 20)); // 每次渲染20个项
    </script>
  </body>
</html>

直接渲染:

image-20240707171739910

分帧渲染

image-20240707171850492

把这个用到vue的框架中,无非也就是组件v-if是否渲染,通过requestAnimationFrame来进行控制,那我们就可以封装成一个Composables(Hooks)函数,比如useDefer

import { onUnmounted, ref } from 'vue';

export function useDefer(maxCount = 100) {
  const count = ref(0);
  let raqId = null;
  function updateFrame() {
    count.value++;
    if (count.value >= maxCount) {
      return;
    }
    raqId = requestAnimationFrame(updateFrame);
  }
  updateFrame();
  onUnmounted(() => {
    cancelAnimationFrame(raqId);
  });
  return function (n) {
    return count.value >= n;
  };
}

直接渲染:

image-20240707172836736

分帧渲染

image-20240707172936045

Theme by Vdoing | Copyright © 2021-2024 蜀ICP备2024068710号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式