在构建页面时,我们会在html中载入一个或多个css和js文件。或许大家都已经习惯了“最佳实践”中,css文件应该放在<head>标签中引入,而js文件则是放在</body>关闭标签前引入的原则,但其中的原因,很多人可能像我之前一样,不是了解得很清楚。在查阅了书籍和资料后,稍微了解的其中的原由。
让我们先看一看浏览器中的渲染流程:
主流程:
详细流程:
当浏览器获得一个html文件时,会“自上而下”
加载,并在加载过程中进行解析渲染。
步骤:
- 浏览器将HTML解析成一个DOM Tree,DOM Tree的构建过程是一个深度遍历过程:当前节点的所有子节点都构建好后才会去构建当前节点的下一个兄弟节点。
- 将CSS解析成 CSS Rule Tree 。
- 根据DOM树和CSSOM合并构造 Render Tree(渲染树)。
- (注意:Render Tree 并不等同于 DOM 树,一些像 <head> 或 display:none 的元素不会放进渲染树)
- Layout,即布局阶段,顾名思义就是计算出每个节点在屏幕中的位置。因为有了Render Tree,浏览器已经能知道网页中有哪些节点、各个节点的CSS定义以及他们的从属关系。
- 最后是绘制,浏览器遍历Render Tree,使用UI后端层绘制Render Tree节点到屏幕。最终,我们就可以在屏幕上看到文档的内容。
整个浏览器渲染页面的流程就是这样,可以说正是因为这个渲染的过程,决定了我们html文档中css文件和js文件的引入位置。
在html中,css文件一般是由href加载,而js用src加载,这两者有什么不同呢?
href:是hypertext reference的缩写,表示超文本引用,用来建立当前元素和文档间的链接。常用的有link,a。
src:是source的缩写,表示资源,src指向的内容会嵌入到文档中当前标签的位置。常用的有img, script, iframe。
当css使用href引用时,当浏览器解析到该元素时,css文件会并行下载,不会阻塞DOM树的解析
当js使用src引用时,当浏览器解析到该元素时,会阻塞对文档和解析和渲染以及资源的下载,等待js文件的下载、解析和执行。直到脚本全部执行完毕,浏览器才会继续解析和渲染文档
为什么脚本会阻塞文档的解析和渲染?
因为脚本有直接操作DOM节点和样式的能力,例如 document.write,这意味着,在JS执行完成前,所有的资源加载解析可能是没有必要的
OK,了解了以上原因,现在应该可以理解为什么要将css放在头部而js放在底部引入了:
css在头部引入:在生成DOM Tree的同时,就可以同时对DOM Tree进行渲染,使页面更快的呈现在屏幕上。如果css放在底部引入,那么在DOM Tree已经生成完毕之后,还需要等待css文件的下载和解析,之后才能进行渲染并绘制,这样会导致页面长时间白屏。
js在底部引入:不会阻塞文件的加载和解析,在页面渲染完毕时才加载执行,不影响用户的浏览