WHAT - 前端阻塞场景梳理

目录

  • 前言
  • 场景梳理
    • 1. JavaScript 执行阻塞主线程
      • 场景
      • 优化思路
      • 具体代码示例
        • 1. 长时间运行的同步 JavaScript 代码
        • 2. 过多的主线程任务(如频繁的 `setTimeout`/`setInterval`)
        • 3. 未优化的第三方库或框架初始化逻辑
        • 总结
    • 2. 样式计算与布局(Layout)开销大
      • 场景
      • 优化思路
    • 3. 资源加载阻塞渲染
      • 场景
      • 优化思路
    • 4. 强制同步渲染
      • 场景
    • 5. 渲染流水线阻塞
    • 6. 渲染阻塞型 CSS
    • 总结

前言

前端主要的阻塞场景包括:JavaScript执行样式计算资源加载重排重绘主线程繁忙。对应的优化策略有异步加载JS、简化CSS、懒加载资源、减少重排重绘、使用性能分析工具定位瓶颈等。

场景梳理

在前端开发中,浏览器的渲染过程可能会因多种原因被阻塞,导致页面卡顿或性能下降。

以下是常见的阻塞场景及对应的优化思路:

1. JavaScript 执行阻塞主线程

场景

  • 长时间运行的同步 JavaScript 代码(如复杂的计算、频繁的 JS DOM 操作)。
  • 过多的主线程任务(如频繁的 setTimeout/setInterval)。
  • 未优化的第三方库或框架(如庞大的 React/Vue 组件初始化逻辑)。

优化思路

  • 异步化脚本:使用 asyncdefer 属性加载脚本,避免阻塞 HTML 解析。在 WHAT - script 加载(含 async、defer、preload 和 prefetch 区别) 有相关介绍。
  • 减少 DOM 操作:批量修改 DOM(如 DocumentFragmentrequestAnimationFrame)。 在 WHAT - CSS Animationtion 动画系列(三)- 动画卡顿分析 和 HOW - 优化回流频繁导致的性能开销 等文章里我们也详细介绍过。
  • 拆分任务:将长任务分解为微任务(Promise.resolve().then())或 Web Worker。
  • 代码分割:通过 Webpack 等工具按需加载代码(Lazy Loading)。

具体代码示例

1. 长时间运行的同步 JavaScript 代码

问题示例

// 模拟一个耗时的同步计算
function heavyCalculation() {
  let sum = 0;
  for (let i = 0; i < 1e8; i++) {
    sum += Math.sqrt(i);
  }
  console.log(sum); // 阻塞主线程约1秒+
}

// 频繁的DOM操作
const list = document.getElementById('list');
for (let i = 0; i < 10000; i++) {
  const item = document.createElement('li');
  item.textContent = `Item ${i}`;
  list.appendChild(item); // 每次操作都触发重排
}

优化方案

  • 将计算转为异步:使用 Web WorkerPromise 分离主线程:

    // Web Worker 示例
    const worker = new Worker('worker.js');
    worker.postMessage({ data: 1e8 });
    worker.onmessage = (e) => console.log(e.data); // 主线程不受阻塞
    
  • 批量DOM操作:使用 DocumentFragmentrequestAnimationFrame

    const fragment = document.createDocumentFragment();
    for (let i = 0; i < 10000; i++) {
      fragment.appendChild(document.createTextNode(`Item ${i}`));
    }
    document.getElementById('list').appendChild(fragment);
    
2. 过多的主线程任务(如频繁的 setTimeout/setInterval

问题示例

// 每秒执行一次的定时器,导致界面卡顿
setInterval(() => {
  // 模拟耗时操作
  Array.from(document.querySelectorAll('*')).forEach(el => el.style.transform = 'translateX(1px)');
}, 100);

在这段代码里,问题 1: setInterval 每 100ms 执行一次,相当于每秒触发 ​10 次​定时任务。如果任务本身耗时(即使很短),主线程会被持续占用,导致其他操作(如渲染、用户交互)被延迟。影响: 浏览器主线程需要处理定时任务 → 渲染线程无法及时刷新界面 → 动画卡顿或掉帧。问题 2querySelectorAll('*') 会遍历整个 DOM 树,获取页面上 所有元素​(假设页面有 1000+ 个节点,这个操作就会很慢)。影响: 每次循环都进行全量查询 → O(n) 时间复杂度随节点数量指数级增长 → 主线程阻塞。

优化方案

  • 使用 requestAnimationFrame:将动画交给浏览器调度:

    let pos = 0;
    function animate() {
      requestAnimationFrame(animate);  // 主动交还主线程控制权
      pos += 1;
      document.getElementById('box').style.transform = `translateX(${pos}px)`; // 仅操作必要元素
    }
    animate();
    
  • 合并定时任务:减少调用频率或使用 debounce/throttle

    // 合并多次快速触发的事件(如滚动事件)
    function handleScroll() {
      requestAnimationFrame(() => {
        // 实际逻辑
      });
    }
    window.addEventListener('scroll', handleScroll);
    
3. 未优化的第三方库或框架初始化逻辑

问题示例

// 未分割的 React 组件,首次加载时渲染百万级虚拟列表
import { Component } from 'react';

class SlowComponent extends Component {
  constructor(props) {
    super(props);
    this.state = { items: Array(100000).fill('Item') }; // 初始化大量数据
  }

  render() {
    return (
      <ul>
        {this.state.items.map((item, index) => (
          <li key={index}>{item}</li>
        ))}
      </ul>
    );
  }
}

优化方案

  • 代码分割:按需加载组件(React.lazy + Suspense):

    const LazyComponent = React.lazy(() => import('./HeavyComponent'));
    
    function App() {
      return (
        <Suspense fallback={<div>Loading...</div>}>
          <LazyComponent />
        </Suspense>
      );
    }
    
  • 虚拟列表:仅渲染可见区域的数据(如 react-windowvue-virtual-scroller):

    // React 虚拟列表示例
    import { FixedSizeList as List } from 'react-window';
    
    const Row = ({ index, style }) => <div style={style}>Item {index}</div>;
    
    function App() {
      return (
        <List
          height={400}
          itemCount={100000}
          itemSize={35}
          width={300}
        >
          {Row}
        </List>
      );
    }
    
总结
  • 同步阻塞异步化(Web Worker、requestAnimationFrame)。
  • 频繁任务合并或降频debouncethrottlerequestAnimationFrame)。
  • 大型库/组件代码分割(动态导入) + 按需渲染(虚拟列表)。

通过这些优化,可以显著减少主线程阻塞时间,提升页面流畅度。

2. 样式计算与布局(Layout)开销大

场景

  • 复杂的 CSS 选择器(如多层嵌套的 div > ul > li)。
  • 强制同步布局(如频繁调用 offsetTop/scrollTop)。
  • 触发大量重排(Reflow)的操作(如 width/height 变化、DOM结构调整)。

优化思路

  • 简化选择器:优先使用类选择器(.class)而非层级过深的元素选择器。
  • 避免强制同步布局:将多次读操作合并为一次(例如缓存布局信息)。 在 WHAT - CSS Animationtion 动画系列(三)- 动画卡顿分析 中我们有相关介绍。
  • 减少重排触发: 在 HOW - 优化回流频繁导致的性能开销 我们有相关介绍
    • 使用 transformopacity 替代 left/top/visibility
    • 批量修改样式(通过 classList.add 或 CSS Variables)。
  • 启用 CSS Containment:限制子元素的布局影响范围(contain: layout;)。具体可以阅读 WHAT - CSS Containment 隔离子元素的布局 (contain=layout)

3. 资源加载阻塞渲染

场景

  • 关键资源(如首屏图片、字体)加载延迟。
  • 未压缩的媒体文件(如高清图片、视频)。
  • 大体积第三方脚本(广告、分析工具)阻塞主线程。

优化思路

  • 懒加载非关键资源:对图片、视频使用 loading="lazy" 属性。
  • 预加载关键资源:通过 <link rel="preload"> 提前加载字体、首屏图片。 具体可以阅读 WHAT - script 加载(含 async、defer、preload 和 prefetch 区别)
  • 压缩资源:启用 Gzip/LZ4 压缩文本资源,使用 WebP 格式替代 JPEG/PNG。
  • 异步加载第三方脚本:动态插入 <script> 标签或配置 async/defer

4. 强制同步渲染

注意,这个不同于前面提到的强制同步布局。

场景

  • 在主线程执行期间强制触发重绘(Painting)或合成(Compositing)。
  • 频繁修改样式导致浏览器不断重排重绘。

优化思路

  • 利用硬件加速:通过 will-change 或 CSS 属性(如 transform: translateZ(0))启用 GPU 加速。
  • 减少重绘触发:合并多次样式更新(例如批量修改元素的 class)。
  • 避免动画阻塞主线程:使用 CSS 动画(@keyframes)而非 JavaScript 控制动画。

5. 渲染流水线阻塞

场景

  • 主线程忙于其他任务(如 JS 执行、事件处理),无法及时处理渲染指令。
  • 合成层(Composite Layers)过多导致合成阶段耗时。

优化思路

  • 分析性能瓶颈:使用 Chrome DevTools 的 Performance 面板定位主线程阻塞点。
  • 合理使用合成层:避免不必要的 position: fixedz-index 导致层爆炸。
  • 优先级调度:通过 requestAnimationFrame 将动画任务交给主线程空闲时处理。

6. 渲染阻塞型 CSS

场景

  • 在 HTML 解析阶段遇到未完成的 <style> 标签(内联或外链),导致后续内容无法渲染。
  • 使用 @import 导入关键 CSS,会阻塞主线程。

优化思路

  • 内联关键 CSS:将首屏必需的样式直接嵌入 HTML <head> 中。
  • 异步加载非关键 CSS:通过 JavaScript 动态插入 <link> 标签加载次要样式。
  • 避免 @import:改用 <link rel="stylesheet"> 并设置 media="print" 触发异步加载。

总结

浏览器渲染阻塞的核心原因是 主线程忙于执行 JS 或计算样式,导致无法及时完成布局、绘制和合成。优化时需结合性能分析工具(如 Chrome DevTools 的 Lighthouse、Performance 面板),针对性地减少主线程负担、优化资源加载策略,并利用 CSS 和 Web API 的硬件加速能力。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/984242.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

DeepSeek+知识库+鸿蒙,助力鸿蒙高效开发

不知道你们发现没有&#xff0c;就是鸿蒙开发官网&#xff0c;文档也太多太多了&#xff0c;对于新手来说确实头疼&#xff0c;开发者大多是极客&#xff0c;程序的目的是让世界更高效&#xff01;看文档&#xff0c;挺头疼的&#xff0c;毕竟都是理科生。 遇到问题不要慌&…

第十五届蓝桥杯省赛电子类单片机学习过程记录(客观题)

客观试题: 01.典型的BUCK电源电路包含哪些关键器件(ABCD) A. 电容 B. 二极管 C. 电感 D. MOSFET 解析: 典型的 BUCK 电源电路是一种降压型的直流-直流转换电路,它包含以下关键器件: A.电容:电容在电路中起到滤波的作用。输入电容用于平滑输入电压的波动,减少电源噪声对…

uniapp uniCloud引发的血案(switchTab: Missing required args: “url“)!!!!!!!!!!

此文章懒得排版了&#xff0c;为了找出这个bug, 星期六的晚上我从9点查到0点多&#xff0c;此时我心中一万个草泥马在崩腾&#xff0c;超级想骂人&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; uniCloud 不想…

源码:用Python进行电影数据分析实战指南

源码&#xff1a;用Python进行电影数据分析实战指南 原创 IT小本本 IT小本本 2025年03月03日 22:28 北京 接上一篇文章&#xff1a;用Python进行电影数据分析实战指南 1、首先复制csv内容到csv文件中 2、接着创建.py文件复制源码内容 3、运行代码&#xff0c;就可以看到数据…

GHCTF2025--Web

upload?SSTI! import os import refrom flask import Flask, request, jsonify,render_template_string,send_from_directory, abort,redirect from werkzeug.utils import secure_filename import os from werkzeug.utils import secure_filenameapp Flask(__name__)# 配置…

Unity Shader编程】之基础纹理

一&#xff0c;单张纹理 好的&#xff0c;用户想学习Unity Shader中的单张纹理章节。我需要根据提供的搜索结果来整理相关内容。首先&#xff0c;查看搜索结果中的相关部分&#xff0c;特别是‌、‌、‌、‌、‌这几条&#xff0c;因为它们提到了基础纹理、单张纹理的实现方法…

SpringBoot使用注解扫描注册Java Web三大组件

使用注解扫描和注册Java Web三大组件&#xff08;Servlet、Filter、Listener&#xff09;非常方便。 1. Servlet 注册 Servlet 是 Java Web 开发的基础组件&#xff0c;用于处理客户端&#xff08;通常是浏览器&#xff09;发送的 HTTP 请求并生成响应。 Controller是基于 Ser…

STM32F4 UDP组播通信:填一填ST官方HAL库的坑

先说写作本文的原因&#xff0c;由于开项目开发中需要用到UDP组播接收的功能&#xff0c;但是ST官方没有提供合适的参考&#xff0c;使用STM32CubeMX生成的代码也是不能直接使用的&#xff0c;而我在网上找了一大圈&#xff0c;也没有一个能够直接解决的方案&#xff0c;deepse…

JVM - 3.垃圾回收

1.垃圾收集的经典问题 1.哪些内存需要回收2.什么时候回收3.如何回收1.你知道哪几种垃圾回收器&#xff0c;各自的优缺点&#xff0c;重点讲一下cms和g12.JVM GC算法有哪些&#xff0c;目前的JDK版本采用什么回收算法3.G1回收器的回收过程 1.Java中垃圾的定义&#xff08;Garbag…

重构谷粒商城09:人人开源框架的快速入门

谷粒商城09——人人开源框架的快速入门 前言&#xff1a;这个系列将使用最前沿的cursor作为辅助编程工具&#xff0c;来快速开发一些基础的编程项目。目的是为了在真实项目中&#xff0c;帮助初级程序员快速进阶&#xff0c;以最快的速度&#xff0c;效率&#xff0c;快速进阶…

css实现元素垂直居中显示的7种方式

文章目录 * [【一】知道居中元素的宽高](https://blog.csdn.net/weixin_41305441/article/details/89886846#_1) [absolute 负margin](https://blog.csdn.net/weixin_41305441/article/details/89886846#absolute__margin_2) [absolute margin auto](https://blog.csdn.net…

用Python写一个算24点的小程序

一、运行界面 二、显示答案——递归介绍 工作流程&#xff1a; 1. 基本情况&#xff1a;函数首先检查输入的数字列表 nums 的长度。如果列表中只剩下一个数字&#xff0c;它会判断这个数字是否接近 24&#xff08;使用 abs(nums[0] - 24) < 1e-10 来处理浮点数精度问题&…

【长安大学】苹果手机/平板自动连接认证CHD-WIFI脚本(快捷指令)

背景&#xff1a; 已经用这个脚本的记得设置Wifi时候&#xff0c;关闭“自动登录” 前几天实在忍受不了CHD-WIFI动不动就断开&#xff0c;一天要重新连接&#xff0c;点登陆好几次。试了下在网上搜有没有CHD-WIFI的自动连接WIFI自动认证脚本&#xff0c;那样我就可以解放双手&…

双击PPT文件界面灰色不可用,需要再次打开该PPT文件才能正常打开

双击PPT文件界面灰色不可用&#xff0c;需要再次打开该PPT文件才能正常打开 1. 软件环境⚙️2. 问题描述&#x1f50d;3. 解决方法&#x1f421;解决步骤 4. 结果预览&#x1f914; 1. 软件环境⚙️ Windows10 或 Windows11 专业版64位&#xff0c;安装MotionGo软件&#xff08…

蓝桥杯[每日两题] 真题:好数 神奇闹钟 (java版)

题目一&#xff1a;好数 题目描述 一个整数如果按从低位到高位的顺序&#xff0c;奇数位&#xff08;个位、百位、万位 &#xff09;上的数字是奇数&#xff0c;偶数位&#xff08;十位、千位、十万位 &#xff09;上的数字是偶数&#xff0c;我们就称之为“好数”。给定…

蓝桥杯刷题周计划(第二周)

目录 前言题目一题目代码题解分析 题目二题目代码题解分析 题目三题目代码题解分析 题目四题目代码题解分析 题目五题目代码题解分析 题目六题目代码题解分析 题目七题目代码题解分析 题目八题目题解分析 题目九题目代码题解分析 题目十题目代码题解分析 题目十一题目代码题解分…

ThinkPHP框架

在电脑C磁盘中安装composer 命令 在电脑的D盘中创建cd文件夹 切换磁盘 创建tp框架 创建一个aa的网站&#xff0c;更换路径到上一步下载的tp框架路径 在管理中修改路径 下载压缩包public和view 将前面代码中的public和view文件替换 在PHPStom 中打开文件 运行指定路径 修改demo…

Spring学习笔记:工厂模式与反射机制实现解耦

1.什么是Spring? spring是一个开源轻量级的java开发应用框架&#xff0c;可以简化企业级应用开发 轻量级 1.轻量级(对于运行环境没有额外要求) 2.代码移植性高(不需要实现额外接口) JavaEE的解决方案 Spring更像是一种解决方案&#xff0c;对于控制层&#xff0c;它有Spring…

爬虫案例八js逆向爬取网易音乐

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、js逆向的前期准备二、网站分析三、代码 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 爬取网易音乐 提示&#xff1a;以下是本篇…

【02】Web网页基础

一、网页的组成 1、HTML 2、CSS 3、javascript 二、网页的结构 三、节点树及节点间的关系 四、选择器 一、网页的组成 网页可以分为三大部分 —— HTML、CSS 和 JavaScript。如果把网页比作一个人的话&#xff0c;HTML 相当于骨架&#xff0c;JavaScript 相当于肌肉&#…