前言
估计大家都听过,尽量将 CSS 放头部,JS 放底部,这样可以提高页面的性能。然而,为什么呢?大家有考虑过么?很长一段时间,我问过不少团队里前端的开发人员,但实际得到的解释一塌糊涂,这里总结一下。
总结
- CSS 不会阻塞 DOM 的解析,但会阻塞 DOM 渲染。
- JS 阻塞 DOM 解析,但浏览器会"偷看"DOM,预先下载相关资源。
- 浏览器遇到
<script>
且没有 defer 或 async 属性的 标签时,会触发页面渲染,因而如果前面 CSS 资源尚未加载完毕时,浏览器会等待它加载完毕在执行脚本。
所以,你现在明白为何<script>
最好放底部,<link>
最好放头部,如果头部同时有<script>
与<link>
的情况下,最好将
测试
针对上述总结,下面将做一段测试,不想看直接看总结。
环境
准备的原文代码如下,我们给它加了一个样式,背景颜色为绿色。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
div {
width: 200px;
height: 200px;
background: lightgreen; /*绿色背景*/
}
</style>
</head>
<body>
<div></div>
</body>
</html>
准备的css代码如下,命名=test.css,将div的背景色改为红色。
div {
background: red; /*红色背景*/
}
准备的js代码如下,命名=test.js,将输出div内容到浏览器。
const div = document.querySelecot('div');
console.log(div);
为了方便测试,还准备了一个函数,进行延迟加载上述的 js、css等内容,下面我们给只要添加sleep的元素都添加3秒的延迟操作,便于查看对应的效果。
function sleep(time) {
return new Promise(function(res) {
setTimeout(() => {
res()
}, time);
})
}
效果1
1、在<head>
中插入test.js文件;
2、将添加了sleep方法延迟3秒的css文件test.css加入到html中的任意位置;
3、打开浏览器,可以看到是首先打印出 div 这个 DOM 节点,过 3s 左右之后才渲染出一个浅蓝色的 div。这就证明了 CSS 是不会阻塞 DOM 的解析的,尽管 CSS 下载需要 3s,但这个过程中,浏览器不会傻等着 CSS 下载完,而是会解析 DOM 的。
效果2
1、在<head>
中按顺序分别添加 css、js文件,css文件延迟3秒,效果如下
<header>
<link rel="stylesheet" href="/css/sleep3000-test.css">
<script src="/js/test.js"></script>
</header>
2、答案是浏览器会转圈圈三秒,但此过程中不会打印任何东西,之后呈现出一个浅蓝色的 div,再打印出 null;
答案是浏览器会转圈圈三秒,但此过程中不会打印任何东西,之后呈现出一个浅蓝色的 div,再打印出 null。结果好像是 CSS 不单阻塞了页面渲染,还阻塞了 DOM 的解析啊!稍等,在你打算掀桌子疯狂吐槽我之前,请先思考一下是什么阻塞了 DOM 的解析,刚才已经证明了 CSS 是不会阻塞的,那么阻塞了页面解析其实是 JS!但明明 JS 的代码如此简单,肯定不会阻塞这么久,那就是 JS 在等待 CSS 的下载,这是为什么呢?
仔细思考一下,其实这样做是有道理的,如果脚本的内容是获取元素的样式,宽高等 CSS 控制的属性,浏览器是需要计算的,也就是依赖于 CSS。浏览器也无法感知脚本内容到底是什么,为避免样式获取,因而只好等前面所有的样式下载完后,再执行 JS。因而造成了之前例子的情况。