CSS和JS在网页中的放置顺序
- 网站加载的整个完整过程:
- 首先浏览器从服务器接收到html代码,然后开始解析html
- 构建DOM树(自html顶向下进行构建),并且在同时构建渲染树
- 遇到JS文件加载执行,将阻塞DOM树的构建;遇到CSS文件,将阻塞渲染树的构建
(script标签中的defer属性:构建DOM树的过程和js文件的加载异步(并行)进行,但是js文件执行需要在DOM树构建完成之后。
script标签中的async属性:构建DOM树、渲染树的过程和js文件的加载和执行异步(并行)进行)
- 为何要注意CSS和JS在网页中的放置顺序
- 若CSS样式放在底部,对于IE浏览器,在点击链接、输入URL、使用书签进入等情况下,会逐步加载无样式的内容,等CSS加载后页面突然会展现样式,影响阅读。
- 若JS放在顶部(
<head>
标签等),会禁用并发,阻止其他内容的下载,影响页面加载的速度;如果js文件比较大,算法也比较复杂的话,影响更大。
白屏和FOUC
- 在写HTML代码时,我们都是将CSS文件的引入位置放在头部(
<head>
标签内部),将js文件的引入位置放在底部(</body>
前面)。 - 不同的浏览器对于CSS和HTML的处理方式不同,有的是等待CSS加载完成之后,对HTML元素进行渲染和展示(白屏问题)。有的是先对HTML元素进行展示,然后等待CSS加载完成之后重新对样式进行修改(FOUC无样式内容闪烁)
- 如果把CSS样式放在底部,对于IE浏览器,在某些场景下(新窗口打开,刷新等)页面会出现白屏,而不是内容逐步展现,如果使用 @import标签,即使 CSS 放入 link, 并且放在头部,也可能出现白屏。
- 如果把CSS样式放在底部,对于IE浏览器,在某些场景下(点击链接,输入URL,使用书签进入等),会出现 FOUC 现象(逐步加载无样式的内容,等CSS加载后页面突然展现样式).对于 Firefox 会一直表现出 FOUC 。
- 如果把js文件放在头部,脚本会阻塞后面内容的呈现,脚本会阻塞其后组件的下载,出现白屏问题。
async与defer的作用和区别
作用
<script src="script.js"></script>
没有defer或async,浏览器会立即加载并执行指定的脚本(“立即”指的是在渲染该script标签之下的文档之前),也就是说不等后续载入的文档元素,读到就加载并执行。
<script async src="script.js"></script>
有async,加载和渲染后续文档元素的过程将和script.js的加载与执行并行进行(异步)。
<script defer src="script.js"></script>
有defer,加载后续文档元素的过程和script.js的加载并行进行(异步),但script.js的执行要在所有元素解析完成之后,DOMContentLoaded事件触发之前完成。区别
蓝色线代表网络读取,红色线代表执行时间,这俩都是针对脚本的;绿色线代表 HTML 解析。
defer 和 async 在网络读取(下载)这块儿是一样的,都是异步的(相较于 HTML 解析),它俩的差别在于脚本下载完之后何时执行。
defer:脚本延迟到文档解析和显示后执行,有顺序。
async:不保证顺序。
网页的渲染机制
- 构建DOM树(DOM tree):从上到下解析HTML文档生成DOM节点树(DOM tree),也叫内容树(content tree);
- 构建CSSOM(CSS Object Model)树:加载解析样式生成CSSOM树;
- 执行JavaScript:加载并执行JavaScript代码(包括内联代码或外联JavaScript文件);
- 构建渲染树(render tree):根据DOM树和CSSOM树,生成渲染树(render tree);
渲染树:按顺序展示在屏幕上的一系列矩形,这些矩形带有字体,颜色和尺寸等视觉属性。 - 布局(layout):根据渲染树将节点树的每一个节点布局在屏幕上的正确位置;
- 绘制(painting):遍历渲染树绘制所有节点,为每一个节点适用对应的样式,这一过程是通过UI后端模块完成;
为了更友好的用户体验,浏览器会尽可能快的展现内容,而不会等到文档所有内容到达才开始解析和构建/布局渲染树,而是每次处理一部分,并展现在屏幕上,这也是为什么我们经常可以看到页面加载的时候内容是从上到下一点一点展现的。
Webkit渲染引擎流程如下图: