一文详解如何避免JavaScript 代码阻塞页面渲染

 更新时间:2026年02月01日 11:39:47   作者:Smilezyl  
阻塞渲染是指浏览器在解析 HTML 构建 DOM 树的过程中,遇到 <script> 标签时会暂停 DOM 解析,等待脚本下载并执行完毕后才继续解析,本文给大家介绍了如何避免JavaScript 代码阻塞页面渲染,需要的朋友可以参考下

核心答案

阻塞渲染是指浏览器在解析 HTML 构建 DOM 树的过程中,遇到 <script> 标签时会暂停 DOM 解析,等待脚本下载并执行完毕后才继续解析。这是因为 JavaScript 可能会修改 DOM 结构(如 document.write),浏览器必须确保 DOM 的正确性。

避免阻塞的核心方法:

  1. 使用 async 属性:脚本异步下载,下载完立即执行
  2. 使用 defer 属性:脚本异步下载,DOM 解析完成后按顺序执行
  3. 将脚本放在 </body>
  4. 动态创建 script 标签

深入解析

浏览器渲染流程

HTML → DOM Tree
                  → Render Tree → Layout → Paint
CSS  → CSSOM

阻塞机制详解

1. JavaScript 阻塞 DOM 解析

HTML解析 → 遇到<script> → 暂停解析 → 下载JS → 执行JS → 继续解析

2. CSS 也会间接阻塞

  • CSS 本身不阻塞 DOM 解析,但阻塞渲染
  • 如果 JS 在 CSS 之后,JS 会等待 CSSOM 构建完成(因为 JS 可能访问样式)

async vs defer 的区别

特性asyncdefer
下载异步,不阻塞解析异步,不阻塞解析
执行时机下载完立即执行DOM 解析完成后执行
执行顺序不保证顺序保证顺序
适用场景独立脚本(统计、广告)有依赖关系的脚本

常见误区

  • 误区:async 和 defer 可以同时使用

实际:同时存在时,现代浏览器优先使用 async

  • 误区:内联脚本可以使用 async/defer

实际:async/defer 只对外部脚本有效

  • 误区:放在 body 底部就不会阻塞

实际:仍会阻塞,只是此时 DOM 已基本解析完成,影响较小

代码示例

<!-- 1. 阻塞渲染(默认行为) -->
<script src="app.js"></script>

<!-- 2. async:异步下载,下载完立即执行 -->
<script async src="analytics.js"></script>

<!-- 3. defer:异步下载,DOM 解析后按顺序执行 -->
<script defer src="vendor.js"></script>
<script defer src="app.js"></script>  <!-- 保证在 vendor.js 之后执行 -->

<!-- 4. 动态加载脚本 -->
<script>
  const script = document.createElement('script');
  script.src = 'lazy-module.js';
  script.async = false; // 保证顺序执行
  document.body.appendChild(script);
</script>

<!-- 5. 模块脚本(默认 defer 行为) -->
<script type="module" src="app.mjs"></script>

<!-- 6. 预加载关键资源 -->
<link rel="preload" href="critical.js" rel="external nofollow"  as="script">

现代优化方案

// 使用 Intersection Observer 懒加载脚本
const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const script = document.createElement('script');
      script.src = entry.target.dataset.src;
      document.body.appendChild(script);
      observer.unobserve(entry.target);
    }
  });
});

// 使用 requestIdleCallback 在空闲时加载
requestIdleCallback(() => {
  const script = document.createElement('script');
  script.src = 'non-critical.js';
  document.body.appendChild(script);
});

面试技巧

可能的追问方向

"async 和 defer 的执行时机具体是什么?"

  • async:下载完成后立即执行,可能在 DOMContentLoaded 之前或之后
  • defer:在 DOMContentLoaded 事件之前执行

"CSS 会阻塞 JS 执行吗?"

  • 会。如果 <script><link> 之后,JS 会等待 CSSOM 构建完成

"如何检测和量化阻塞时间?"

  • Performance API、Lighthouse、Chrome DevTools Performance 面板

"type="module" 的脚本有什么特点?"

  • 默认 defer 行为、严格模式、独立作用域、支持 import/export

展示深度的回答技巧

  • 提及浏览器的预解析器(Preload Scanner)会提前扫描并下载资源
  • 讨论 Critical Rendering Path 优化策略
  • 结合实际项目经验,如 Webpack 的代码分割、动态 import

一句话总结

JS 阻塞 DOM 解析是因为可能修改 DOM;用 defer 保顺序、async 求速度、动态加载最灵活。

以上就是一文详解如何避免JavaScript 代码阻塞页面渲染的详细内容,更多关于JavaScript代码阻塞页面渲染避免方法的资料请关注脚本之家其它相关文章!

相关文章

最新评论