CSS 与 JS 是这样阻塞 DOM 解析和渲染的

CSS 与 JS 是这样阻塞 DOM 解析和渲染的

估计大家都听过,尽量将 CSS 放头部,JS 放底部,这样可以提高页面的性能。然而,为什么呢?大家有考虑过么?很长一段时间,我都是知其然而不知其所以然,强行背下来应付考核当然可以,但实际应用中必然一塌糊涂。因此洗(wang)心(yang)革(bu)面(lao),小结一下最近玩出来的成果。

友情提示,本文也是小白向为主,如果直接想看结论可以拉到最下面看的~

node 端唯一需要解释一下的是这个函数:

function sleep(time) { return new Promise(function(res) { setTimeout(() => { res() }, time); })}

嗯!其实就延时啦。如果 CSS 或者 JS 文件名有 sleep3000 之类的前缀时,意思就是延迟 3000 毫秒才会返回这文件。

下文使用的 HTML 文件是长这样的:

     <title>Title/<title>    

我会在其中插入不同的 JS 和 CSS。

而使用的 common.css,不论有没有前缀,内容都是这样的:

div { background: red;}

十五年编程经验,今年1月整理了一批2019年最新WEB前端教学视频,不论是零基础想要学习前端还是学完在工作想要提升自己,这些资料都会给你带来帮助,从HTML到各种框架,帮助所有想要学好前端的同学,学习规划、学习路线、学习资料、问题解答。只要关注我的头条号,后台私信我【前端】两个字,即可免费获取。

好了,话不多数,开始正文!

CSS

关于 CSS,大家肯定都知道的是标签放在头部性能会高一点,少一点人知道如果

CSS 不会阻塞 DOM 的解析

注意哦!这里说的是 DOM 解析,证明的例子如下,首先在头部插入,JS 文件的内容是:

const div = document.querySelecot('div');console.log(div);

defer 属性相信大家也很熟悉了,MDN 对此的描述是用来通知浏览器该脚本将在文档完成解析后,触发 DOMContentLoaded 事件前执行。设置这个属性,能保证 DOM 解析后马上打印出 div。

之后将<link>插入 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> /<header>

但思考一下这会产生什么结果呢?

答案是浏览器会转圈圈三秒,但此过程中不会打印任何东西,之后呈现出一个浅蓝色的 div,再打印出 null。结果好像是 CSS 不单阻塞了页面渲染,还阻塞了 DOM 的解析啊!稍等,在你打算掀桌子疯狂吐槽我之前,请先思考一下是什么阻塞了 DOM 的解析,刚才已经证明了 CSS 是不会阻塞的,那么阻塞了页面解析其实是 JS!但明明 JS 的代码如此简单,肯定不会阻塞这么久,那就是 JS 在等待 CSS 的下载,这是为什么呢?

仔细思考一下,其实这样做是有道理的,如果脚本的内容是获取元素的样式,宽高等 CSS 控制的属性,浏览器是需要计算的,也就是依赖于 CSS。浏览器也无法感知脚本内容到底是什么,为避免样式获取,因而只好等前面所有的样式下载完后,再执行 JS。因而造成了之前例子的情况。

所以,看官大人明白为何 <link>


分享到:


相關文章: