你有没有遇到过这种情况:点开一个网页,页面空白好几秒,等得人心焦。尤其是用手机访问时,明明网速不慢,但就是迟迟不出内容。问题很可能出在DOM阻塞渲染上。
什么是DOM阻塞渲染?
浏览器加载HTML页面时,会从上到下解析代码,构建DOM树。如果在HTML中直接引入了外部JavaScript文件,或者内联了大量JS代码,浏览器就会暂停DOM构建,先去下载并执行这些脚本。这个过程就是“阻塞”,用户看到的就是白屏。
比如你在
里写了这样一段:<script src="big-script.js"></script>
只要这个 big-script.js 没下载完、没执行完,页面后面的元素就都别想显示。哪怕它是个和页面展示无关的统计脚本,也照卡不误。
怎么解决?三个实用方法
1. 给脚本加上 async 或 defer
对于非关键的JS文件,比如广告、埋点、第三方插件,建议加上 async 或 defer 属性。
<script src="analytics.js" async></script>
<script src="utils.js" defer></script>
async 是异步加载,加载完就立即执行,不保证顺序;defer 是延迟执行,等到DOM解析完再按顺序执行。一般推荐用 defer,更可控。
2. 把脚本移到 body 底部
如果你还在用老办法,可以把所有非必要的 script 标签挪到 前面。这样浏览器先解析完DOM,再处理JS,自然就不会卡住页面渲染。
<!DOCTYPE html>
<html>
<head>
<title>我的页面</title>
</head>
<body>
<h1>欢迎光临</h1>
<p>这里是一些内容</p>
<!-- 脚本放在最后 -->
<script src="app.js"></script>
</body>
</html>
3. 内联关键CSS,异步加载其余样式
CSS虽然不直接执行,但浏览器会等关键CSS资源加载完才渲染页面,防止样式闪动。但如果CSS文件太大,也会拖慢首屏。
解决方案是:把首屏用到的关键CSS直接写在 <style> 标签里,其余的用异步方式加载。
<style>
/* 内联首屏需要的样式 */
.header { color: #333; }
.hero { margin-top: 20px; }
</style>
<!-- 异步加载完整样式表 -->
<link rel="preload" href="styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="styles.css"></noscript>
实际效果对比
有个客户之前首页加载要4秒多,用户流失严重。我们帮他把统计脚本加了 async,主JS改成 defer,关键CSS内联后,首屏时间降到1.2秒左右。最明显的变化是,用户进来几乎立刻能看到内容,不再干等。
这种优化不需要改功能,也不影响交互,但体验提升非常明显。特别是对网络不太好的用户,少等一秒都是留住他们的机会。
小改动,大收益
避免DOM阻塞渲染不是什么高深技术,更多是细节上的讲究。就像修路,没必要把整条路重铺,只要把堵车的几个关键路口理顺,通行效率就能翻倍。下次写HTML时,多问一句:这个脚本真的要现在执行吗?也许答案是否定的。