Skip to content

浏览器渲染

浏览器会尽可能多地渲染已经解析的内容,即使遇到阻塞的任务(如 script 和 link 下载 CSS),也会尽可能多地渲染已经解析的内容,以提高用户的感知速度和体验。这也是为什么在页面加载过程中,即使还没有完全加载完成,用户也能够看到部分内容的原因。

过程

主要分为解析和渲染两个步骤,其中解析是将文本文件转换为数据结构的过程,渲染是指根据解析得到的文档对象模型(DOM)、样式对象模型(CSSOM)和计算机图形学基础的一些原理,通过布局和绘制等过程将页面展示出来的过程。

  1. 解析 HTML 文档得到 DOM 树;
  2. 解析 CSS 文档得到 CSSOM 树;
  3. 合并 DOM 树 和 CSSOM 得到渲染树;
  4. 布局,重新计算元素大小,位置等信息,可能导致页面重拍;
  5. 绘制,重新计算颜色,透明度等信息,可能会导致页面重绘;

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 的执行被延迟,从而影响页面的加载速度和用户体验。