hello~各位亲爱的看官老爷们你们好。估计你们都听过,尽可能将CSS
放头部,JS
放底部,这样能够提升页面的性能。然而,为何呢?你们有考虑过么?很长一段时间,我都是知其然而不知其因此然,强行背下来应付考核固然能够,但实际应用中必然一塌糊涂。所以洗(wang)心(yang)革(bu)面(lao),小结一下最近玩出来的成果。css
友情提示,本文也是小白向为主,若是直接想看结论能够拉到最下面看的~html
因为关系到文件的读取,那是确定须要服务器的,我会把所有的文件放在github上,给我点个 star 我会开心!掘金上再给我点个 赞 我就更开心了~node
node
端惟一须要解释一下的是这个函数:git
function sleep(time) {
return new Promise(function(res) {
setTimeout(() => {
res()
}, time);
})
}
复制代码
嗯!其实就延时啦。若是CSS
或者JS
文件名有sleep3000
之类的前缀时,意思就是延迟3000毫秒才会返回这文件。github
下文使用的HTML
文件是长这样的:数组
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
div {
width: 100px;
height: 100px;
background: lightgreen;
}
</style>
</head>
<body>
<div></div>
</body>
</html>
复制代码
我会在其中插入不一样的JS
和CSS
。浏览器
而使用的common.css
,不论有没有前缀,内容都是这样的:bash
div {
background: lightblue;
}
复制代码
好了,话很少数,开始正文!服务器
关于CSS
,你们确定都知道的是<link>
标签放在头部性能会高一点,少一点人知道若是<script>
与<link>
同时在头部的话,<script>
在上可能会更好。这是为何呢?下面咱们一块儿来看一下CSS
对DOM
的影响是什么。dom
CSS
不会阻塞 DOM
的解析注意哦!这里说的是DOM
解析,证实的例子以下,首先在头部插入<script defer src="/js/logDiv.js"></script>
,JS
文件的内容是:
const div = document.querySelector('div');
console.log(div);
复制代码
defer
属性相信你们也很熟悉了,MDN对此的描述是用来通知浏览器该脚本将在文档完成解析后,触发 DOMContentLoaded 事件前执行。设置这个属性,能保证DOM
解析后立刻打印出div
。
以后将<link rel="stylesheet" href="/css/sleep3000-common.css">
插入HTML
文件的任一位置,打开浏览器,能够看到是首先打印出div
这个DOM
节点,过3s左右以后才渲染出一个浅蓝色的div
。这就证实了CSS
是不会阻塞 DOM
的解析的,尽管CSS
下载须要3s,但这个过程当中,浏览器不会傻等着CSS
下载完,而是会解析DOM
的。
这里简单说一下,浏览器是解析DOM
生成DOM Tree
,结合CSS
生成的CSS Tree
,最终组成render tree
,再渲染页面。因而可知,在此过程当中CSS
彻底没法影响DOM Tree
,于是无需阻塞DOM
解析。然而,DOM Tree
和CSS Tree
会组合成render tree
,那CSS
会不会页面阻塞渲染呢?
CSS
阻塞页面渲染其实这一点,刚才的例子已经说明了,若是CSS
不会阻塞页面阻塞渲染,那么CSS
文件下载以前,浏览器就会渲染出一个浅绿色的div
,以后再变成浅蓝色。浏览器的这个策略其实很明智的,想象一下,若是没有这个策略,页面首先会呈现出一个原始的模样,待CSS
下载完以后又忽然变了一个模样。用户体验可谓极差,并且渲染是有成本的。
所以,基于性能与用户体验的考虑,浏览器会尽可能减小渲染的次数,CSS
瓜熟蒂落地阻塞页面渲染。
然而,事情总有奇怪的,请看这例子,HTML
头部结构以下:
<header>
<link rel="stylesheet" href="/css/sleep3000-common.css">
<script src="/js/logDiv.js"></script>
</header>
复制代码
但思考一下这会产生什么结果呢?
答案是浏览器会转圈圈三秒,但此过程当中不会打印任何东西,以后呈现出一个浅蓝色的div
,再打印出null
。结果好像是CSS
不单阻塞了页面渲染,还阻塞了DOM
的解析啊!稍等,在你打算掀桌子疯狂吐槽我以前,请先思考一下是什么阻塞了DOM
的解析,刚才已经证实了CSS
是不会阻塞的,那么阻塞了页面解析实际上是JS
!但明明JS
的代码如此简单,确定不会阻塞这么久,那就是JS
在等待CSS
的下载,这是为何呢?
仔细思考一下,其实这样作是有道理的,若是脚本的内容是获取元素的样式,宽高等CSS
控制的属性,浏览器是须要计算的,也就是依赖于CSS
。浏览器也没法感知脚本内容究竟是什么,为避免样式获取,于是只好等前面全部的样式下载完后,再执行JS
。于是形成了以前例子的状况。
因此,看官大人明白为什么<script>
与<link>
同时在头部的话,<script>
在上可能会更好了么?之因此是可能,是由于若是<link>
的内容下载更快的话,是没影响的,但反过来的话,JS
就要等待了,然而这些等待的时间是彻底没必要要的。
JS
,也就是<script>
标签,估计你们都很熟悉了,不就是阻塞DOM
解析和渲染么。然而,其中其实仍是有一点细节能够考究一下的,咱们一块儿来好好看看。
JS
阻塞 DOM
解析首先咱们须要一个新的JS
文件名为blok.js
,内容以下:
const arr = [];
for (let i = 0; i < 10000000; i++) {
arr.push(i);
arr.splice(i % 3, i % 7, i % 5);
}
const div = document.querySelector('div');
console.log(div);
复制代码
其实那个数组操做时没意义的,只是为了让这个JS
文件多花执行时间而已。以后把这个文件插入头部,浏览器跑一下。
结果估计你们也能想象获得,浏览器转圈圈一会,这过程当中不会有任何东西出现。以后打印出null
,再出现一个浅绿色的div
。现象就足以说明JS
阻塞 DOM
解析了。其实缘由也很好理解,浏览器并不知道脚本的内容是什么,若是先行解析下面的DOM
,万一脚本内全删了后面的DOM
,浏览器就白干活了。更别谈丧心病狂的document.write
。浏览器没法预估里面的内容,那就干脆所有停住,等脚本执行完再干活就行了。
对此的优化其实也很显而易见,具体分为两类。若是JS
文件体积太大,同时你肯定不必阻塞DOM
解析的话,不妨按须要加上defer
或者async
属性,此时脚本下载的过程当中是不会阻塞DOM
解析的。
而若是是文件执行时间太长,不妨分拆一下代码,不用当即执行的代码,可使用一下之前的黑科技:setTimeout()
。固然,现代的浏览器很聪明,它会“偷看”以后的DOM
内容,碰到如<link>
、<script>
和<img>
等标签时,它会帮助咱们先行下载里面的资源,不会傻等到解析到那里时才下载。
<script>
标签时,会触发页面渲染这个细节可能很多看官大人并不清楚,其实这才是解释上面为什么JS
执行会等待CSS
下载的缘由。先上例子,HTML
内body
的结构以下:
<body>
<div></div>
<script src="/js/sleep3000-logDiv.js"></script>
<style>
div {
background: lightgrey;
}
</style>
<script src="/js/sleep5000-logDiv.js"></script>
<link rel="stylesheet" href="/css/common.css">
</body>
复制代码
这个例子也是很极端的例子,但不妨碍它透露给咱们不少重要的信息。想象一下,页面会怎样呢?
答案是先浅绿色,再浅灰色,最后浅蓝色。因而可知,每次碰到<script>
标签时,浏览器都会渲染一次页面。这是基于一样的理由,浏览器不知道脚本的内容,于是碰到脚本时,只好先渲染页面,确保脚本能获取到最新的DOM
元素信息,尽管脚本可能不须要这些信息。
综上所述,咱们得出这样的结论:
CSS
不会阻塞 DOM
的解析,但会阻塞 DOM
渲染。JS
阻塞 DOM
解析,但浏览器会"偷看"DOM
,预先下载相关资源。<script>
且没有defer
或async
属性的 标签时,会触发页面渲染,于是若是前面CSS
资源还没有加载完毕时,浏览器会等待它加载完毕在执行脚本。因此,你如今明白为什么<script>
最好放底部,<link>
最好放头部,若是头部同时有<script>
与<link>
的状况下,最好将<script>
放在<link>
上面了吗?
感谢各位看官大人看到这里,但愿本文对你有所帮助,有不一样或更好意见的大佬,还望不吝赐教!谢谢~