Appearance
浏览器渲染
浏览器会尽可能多地渲染已经解析的内容,即使遇到阻塞的任务(如 script 和 link 下载 CSS),也会尽可能多地渲染已经解析的内容,以提高用户的感知速度和体验。这也是为什么在页面加载过程中,即使还没有完全加载完成,用户也能够看到部分内容的原因。
过程
主要分为解析和渲染两个步骤,其中解析是将文本文件转换为数据结构的过程,渲染是指根据解析得到的文档对象模型(DOM)、样式对象模型(CSSOM)和计算机图形学基础的一些原理,通过布局和绘制等过程将页面展示出来的过程。
- 解析 HTML 文档得到 DOM 树;
- 解析 CSS 文档得到 CSSOM 树;
- 合并 DOM 树 和 CSSOM 得到渲染树;
- 布局,重新计算元素大小,位置等信息,可能导致页面重拍;
- 绘制,重新计算颜色,透明度等信息,可能会导致页面重绘;
js 加载
将 script 标签放在 body 底部,主要原因是为了避免 JavaScript 对 DOM 解析和页面渲染的阻塞。当浏览器解析 HTML 文档时,如果遇到 script 标签,它会停止解析 HTML 文件并开始下载并执行 JavaScript 文件,这是因为浏览器需要先加载和执行 JavaScript 代码,才能得到相关的 DOM 和 CSSOM 数据,然后再进行解析。
普通 script
标签加载会阻塞 dom
解析,导致浏览器必须加载并且执行脚本,之后才能继续解析,一些耗时操作会让页面出现白屏,而 defer
属性和 async
属性能够让 JavaScript 脚本在加载时不阻塞文档解析。
- 存在
async
属性的脚本会并行请求,并尽快解析和执行,不保证顺序; - 存在
defer
属性的脚本会阻塞DOMContentLoaded
事件,并行请求,在DOMContentLoaded
事件之前调用,保证顺序; - 对于内嵌脚本(即无
src
属性),二者都失效。
css 加载
将 CSS 文件放在 HTML 文件的 <head>
元素中的主要原因是为了优化页面的渲染性能。当浏览器解析 HTML
文档时,遇到 link
标签或 style
标签时,会开始下载 CSS 文件并解析 CSS 规则。另外 link
标签下载样式表时是阻塞的,因为样式会影响到最终的渲染效果。
如果将 CSS 文件放在 HTML 文件的 <body>
元素中或后面,则会导致页面先渲染出原始样式,然后再重新渲染使用 CSS 样式后的样式,这会导致页面出现“闪烁”的效果,影响用户的使用体验。
- css 加载不会阻塞 DOM 树的解析。当浏览器在解析 HTML 文档时遇到
<link>
标签或者<style>
标签时,会异步下载 CSS 文件,并在下载过程中继续构建 DOM 树。因此,即使 CSS 文件没有下载完成,也不会阻塞 DOM 树的解析。 - css 加载会阻塞 DOM 树的渲染。浏览器在构建 DOM 树的同时,需要计算每个元素在页面中的布局和位置信息。如果 CSS 文件没有下载完成,就无法确定元素的最终显示效果,从而无法进行渲染。因此,CSS 加载会阻塞 DOM 树的渲染。
- css 加载会阻塞后面 js 语句的执行。当浏览器遇到
<script>
标签时,会暂停 HTML 解析,并等待 JS 文件下载和执行完成后才继续解析 HTML。如果 CSS 文件的下载和解析时间较长,就可能导致 JS 的执行被延迟,从而影响页面的加载速度和用户体验。