如何确保脚本执行完成后再进行页面渲染?

夜静同学_9674

夜静同学_9674

2026-11-15

392人浏览

原创

如何确保脚本执行完成后再进行页面渲染?

浏览器中 HTML 解析、脚本执行与样式布局/绘制并非严格串行,requestAnimationFrame 可能在外部脚本加载前触发,导致渲染早于关键逻辑执行,引发 UI 闪烁或状态不一致。本文详解 render-blocking 机制、blocking="render" 属性及可靠防抖策略。

浏览器中 html 解析、脚本执行与样式布局/绘制并非严格串行,`requestanimationframe` 可能在外部脚本加载前触发,导致渲染早于关键逻辑执行,引发 ui 闪烁或状态不一致。本文详解 render-blocking 机制、`blocking="render"` 属性及可靠防抖策略。

在构建高性能、状态敏感的 Web 应用时,开发者常默认「脚本执行完毕后才会渲染」——但事实并非如此。HTML 规范中,外部经典脚本(<script src="..."></script>)默认是 parser-blocking 的,即 HTML 解析器会暂停并等待该脚本下载、编译与执行完毕,再继续解析后续 HTML;但它不自动成为 render-blocking,除非满足特定条件。

✅ 什么是 render-blocking?

Render-blocking 指:浏览器必须完成该脚本的执行,才能进入样式计算(Style)、布局(Layout)和绘制(Paint)阶段。换言之,即使 DOM 已解析完成,若存在未执行完的 render-blocking 脚本,页面将保持空白或冻结状态,不会呈现任何视觉内容。

根据 HTML 规范,仅当 <script></script> 标签位于 中且为外部脚本(非内联)时,才具备 render-blocking 行为(Chrome/Edge 支持,Firefox 当前暂不支持该特性)。

以下示例可验证此行为(使用 避免 StackBlitz 环境干扰):

<iframe srcdoc="
<html>
  <head>
    <script>
      requestAnimationFrame(() => parent.postMessage(['RAF fired'], '*'));
    </script>
    <script src='data:text/javascript,console.log(`SCRIPT EXECUTED`);'></script>
  </head>
  <body><h1>HELLO</h1></body>
</html>">
</iframe>

在 Chrome 中,你将稳定看到:

SCRIPT EXECUTED
RAF fired

说明 requestAnimationFrame 回调被推迟到了脚本执行之后 —— 因为 中的外部脚本已触发 render-blocking。

⚠️ 注意事项与兼容性

  • Firefox 不支持 render-blocking:上述 中外部脚本的行为在 Firefox 中仍可能被 RAF 抢先触发,需额外防护。
  • 内联脚本不 render-blocking:即使写在 中,<script>...</script> 内联代码仅 parser-blocking,不阻塞渲染。
  • defer / async / type="module" 脚本默认非 render-blocking:它们绕过 parser-blocking,但也失去 render-blocking 能力(除非显式声明)。

✅ 推荐方案:blocking="render"(现代标准)

HTML 新增的 blocking="render" 属性,允许非 parser-blocking 脚本(如 defer、async、模块)主动声明为 render-blocking:

  <!-- 此脚本异步加载,但强制 render-blocking -->
  <script defer blocking="render" src="./init.js"></script><!-- 或配合 RAF 安全初始化 --><script>
    // 确保 DOM 就绪 + 渲染被阻塞后才运行
    document.addEventListener('DOMContentLoaded', () => {
      requestAnimationFrame(() => {
        // ✅ 此处 DOM 已就绪,且无中间帧渲染干扰
        initializeUI();
      });
    });
  </script><blockquote><p>✅ 当前支持:Chrome 125+、Edge 125+(<a href="https://www.php.cn/link/789c5cf0a5d1b4e405320c592b24ee88" rel="nofollow" target="_blank">CanIUse 数据</a>)。Firefox 和 Safari 尚未实现,建议搭配降级逻辑。</p></blockquote><h3>✅ 最佳实践:防御性 RAF + 渲染控制</h3><p>为兼顾兼容性与确定性,推荐组合策略:</p><pre class="brush:php;toolbar:false;">// init.js —— 关键初始化逻辑
function initializeUI() {
  document.body.classList.add('js-ready');
  // ... 其他 DOM 操作、状态初始化
}

// 方案 1:等待 DOM 就绪 + 强制下一帧执行(最通用)
if (document.readyState === 'loading') {
  document.addEventListener('DOMContentLoaded', () => {
    requestAnimationFrame(initializeUI);
  });
} else {
  requestAnimationFrame(initializeUI);
}

// 方案 2:CSS 防抖(零 JS 依赖)
// 在  中添加:
// <style>body { visibility: hidden; } body.js-ready { visibility: visible; }</style>

✅ 总结

核心原则:不要依赖“脚本执行顺序 = 渲染顺序”;始终用 DOMContentLoaded + requestAnimationFrame 组合确保 DOM 就绪且无中间渲染;对关键首屏逻辑,优先采用 中 render-blocking 外部脚本,或等待 blocking="render" 全面落地后升级。

场景 是否 render-blocking 说明
<script src="x.js"></script> ✅(Chrome/Edge) 最简兼容方案,Firefox 不适用
<script blocking="render" defer src="x.js"></script> ✅(Chrome 125+) 未来标准,支持异步加载+渲染阻塞
<script src="x.js"></script>❌ 仅 parser-blocking,不阻塞渲染,易 flicker
requestAnimationFrame(...) 在 script 标签前 ⚠️ 危险 RAF 可能早于脚本执行,务必包裹在 DOMContentLoaded 或 requestIdleCallback 后
PHP速学视频免费教程(入门到精通)
PHP速学视频免费教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
js获取数组长度的方法
js获取数组长度的方法

在js中,可以利用array对象的length属性来获取数组长度,该属性可设置或返回数组中元素的数目,只需要使用“array.length”语句即可返回表示数组对象的元素个数的数值,也就是长度值。php中文网还提供JavaScript数组的相关下载、相关课程等内容,供大家免费下载使用。

2023.06.20

5946

5

js刷新当前页面
js刷新当前页面

js刷新当前页面的方法:1、reload方法,该方法强迫浏览器刷新当前页面,语法为“location.reload([bForceGet]) ”;2、replace方法,该方法通过指定URL替换当前缓存在历史里(客户端)的项目,因此当使用replace方法之后,不能通过“前进”和“后退”来访问已经被替换的URL,语法为“location.replace(URL) ”。php中文网为大家带来了js刷新当前页面的相关知识、以及相关文章等内容

2023.07.04

1429

3

js四舍五入
js四舍五入

js四舍五入的方法:1、tofixed方法,可把 Number 四舍五入为指定小数位数的数字;2、round() 方法,可把一个数字舍入为最接近的整数。php中文网为大家带来了js四舍五入的相关知识、以及相关文章等内容

2023.07.04

5864

6

js删除节点的方法
js删除节点的方法

js删除节点的方法有:1、removeChild()方法,用于从父节点中移除指定的子节点,它需要两个参数,第一个参数是要删除的子节点,第二个参数是父节点;2、parentNode.removeChild()方法,可以直接通过父节点调用来删除子节点;3、remove()方法,可以直接删除节点,而无需指定父节点;4、innerHTML属性,用于删除节点的内容。

2023.09.01

1060

4

JavaScript转义字符
JavaScript转义字符

JavaScript中的转义字符是反斜杠和引号,可以在字符串中表示特殊字符或改变字符的含义。本专题为大家提供转义字符相关的文章、下载、课程内容,供大家免费下载体验。

2023.09.04

2036

5

js生成随机数的方法
js生成随机数的方法

js生成随机数的方法有:1、使用random函数生成0-1之间的随机数;2、使用random函数和特定范围来生成随机整数;3、使用random函数和round函数生成0-99之间的随机整数;4、使用random函数和其他函数生成更复杂的随机数;5、使用random函数和其他函数生成范围内的随机小数;6、使用random函数和其他函数生成范围内的随机整数或小数。

2023.09.04

4025

4

如何启用JavaScript
如何启用JavaScript

JavaScript启用方法有内联脚本、内部脚本、外部脚本和异步加载。详细介绍:1、内联脚本是将JavaScript代码直接嵌入到HTML标签中;2、内部脚本是将JavaScript代码放置在HTML文件的`<script>`标签中;3、外部脚本是将JavaScript代码放置在一个独立的文件;4、外部脚本是将JavaScript代码放置在一个独立的文件。

2023.09.12

5333

6

Js中Symbol类详解
Js中Symbol类详解

javascript中的Symbol数据类型是一种基本数据类型,用于表示独一无二的值。Symbol的特点:1、独一无二,每个Symbol值都是唯一的,不会与其他任何值相等;2、不可变性,Symbol值一旦创建,就不能修改或者重新赋值;3、隐藏性,Symbol值不会被隐式转换为其他类型;4、无法枚举,Symbol值作为对象的属性名时,默认是不可枚举的。

2023.09.20

3500

5

java访问控制修饰符介绍
java访问控制修饰符介绍

java访问控制修饰符有四种,分别是public、protected、private、默认访问修饰符。详细介绍:1、public,public是最宽松的访问控制修饰符,被修饰的类、方法和变量可以被任何其他类访问,当一个类、方法或变量被声明为public时,它们可以在任何地方被访问,无论是同一个包中的类还是不同包中的类;2、protected修饰符等等。

2023.09.20

1128

7

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
热门推荐
/
最新课程
phpStudy极速入门视频教程
phpStudy极速入门视频教程

共6课时 | 54.7万人学习

独孤九贱(4)_PHP视频教程
独孤九贱(4)_PHP视频教程

共89课时 | 134.3万人学习