You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何body标签下方的脚本会阻塞浏览器渲染?

为什么body下方的脚本会阻塞页面渲染?

原本认为body标签下方的脚本不会阻塞渲染阶段(如DOM树、Render Tree构建及绘制),预期会先快速显示黄色背景,约5秒后JavaScript修改DOM(CSSOM)将背景改为红色。但实际是阻塞5秒后直接显示红色背景,无法先看到黄色背景,请问该脚本为何会阻塞渲染阶段?

代码示例

<style>
.section1 {
    background-color:yellow;
    width:100wh;
    height:100vh;
}
</style>
<body>
    <div class="container">
        <section class="section1">
        
        </section>
    </div>
    
    <script>
        const n = 5000000000 // 在我的设备上大约耗时5秒
        for(let i= 0 ; i<n; i++){
            
        }
        document.querySelector('.section1').style.backgroundColor = "red"
    </script>
</body>

原因解析

浏览器的主线程负责DOM解析、CSSOM构建、JS执行、渲染(布局+绘制)这些核心任务,同一时间只能处理其中一件事,这是导致阻塞的核心原因:

  • 虽然脚本放在body末尾,此时DOM树已经构建完成,但浏览器不会立刻触发渲染——它会等待当前同步JS任务执行完毕后,才会进行一次完整的渲染流程。
  • 你的脚本里有一个长达5秒的同步循环,这段时间内主线程完全被JS占据,根本没有机会去执行渲染操作。
  • 当循环结束后,脚本立刻修改了.section1的背景色,此时浏览器终于能执行渲染,但渲染树已经包含了修改后的红色背景样式,所以直接显示红色,不会先展示黄色。

简单说:同步JS阻塞了主线程,让浏览器没机会在脚本执行前完成第一次渲染,等到能渲染时,样式已经被改成红色了。

实现预期效果的方法

如果想先显示黄色,再5秒后变红,需要让浏览器在脚本执行前完成一次渲染,比如把长耗时任务放到异步队列里:

<script>
// 用setTimeout把任务推到下一个事件循环,让浏览器先完成渲染
setTimeout(() => {
    const n = 5000000000;
    for(let i= 0 ; i<n; i++){}
    document.querySelector('.section1').style.backgroundColor = "red";
}, 0);
</script>

更优的方案是用Web Worker处理长耗时计算,完全不阻塞主线程:

// 主线程脚本
<script>
const worker = new Worker('worker.js');
worker.postMessage('start');
worker.onmessage = () => {
    document.querySelector('.section1').style.backgroundColor = "red";
};
</script>

// worker.js文件
self.onmessage = () => {
    const n = 5000000000;
    for(let i= 0 ; i<n; i++){}
    self.postMessage('done');
};

内容的提问来源于stack exchange,提问作者minwoo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 08:05:27