当前位置: 首页 > news >正文

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 的硬件加速能力。

相关文章:

  • Hive-优化(语法优化篇)
  • 【Unity】 HTFramework框架(六十一)Project窗口文件夹锁定器
  • Vue 系列之:Vuex 和 Pinia
  • 直播流程管理 AI 应用的开发思路和功能实现
  • 从零开始玩转 Docker:用 Node.js 打印“Hello World”
  • IOC 篇
  • 机器学习数学基础:38.统计学模型变量
  • Android中的AsyncTask。
  • Redis--Hash类型
  • SQL 注入 (C++向)
  • 【Linux】初识make
  • 78.StringBuilder简单示例 C#例子 WPF例子
  • GPT 4.5 可能是戳破 AI 泡沫的模型
  • C++二叉搜索树代码
  • 西安交大DeepSeek—电力人工智能多模态大模型创新技术应用
  • Leetcode 刷题记录 04 —— 子串
  • 【Linux】多线程(1)
  • python语言总结(持续更新)
  • Vue2 的生命周期有哪些
  • 物联网智慧农业一体化解决方案-可继续扩展更多使用场景
  • 网站备案怎么弄/信阳seo公司
  • node怎么做网站/百度客服人工服务电话
  • 洛阳做网站哪家便宜/搜索引擎营销是什么意思
  • 做百度还是阿里网站好/网店代运营可靠吗
  • 崇州网站制作/seo优化一般包括
  • 大学城网站开发公司/泉州seo技术