页面加载原理

加载资源的形式

  • 输入 url (或跳转页面)加载 HTML
https://example.com
1
  • 加载 HTML 中的静态资源
<script type="text/javascript" src="./target.js"></script>
<img src="a.png"/>
1
2

加载(下载)资源的过程

  1. 浏览器根据 DNS 服务器得到域名的 IP 地址

  2. 向该 IP 的服务器发送请求(httphttps

  3. 服务端接受请求,处理之后并返回请求

  4. 客户端得到服务端返回的内容

浏览器渲染页面的过程

  1. 根据 HTML 结构生成 DOM 树结构(DOM 的数据结构是 树(Tree) 类型)

  2. 根据 CSS 生成 CSSOMW3C

  3. DOMCSSOM 整合形成 Render

  4. 根据 Render 树开始渲染并展示结果

  5. 特别地,在下载并执行script 标签时,因为不确定是否包含修改 DOM 的 JS 代码,浏览器默认将 阻塞 当前渲染。

注:对于第 5 点,可在 script 标签上使用 deferasync 来解除浏览器在下载 JS 代码的阻塞渲染行为,或者在 body 的结尾处(即等到 DOM 树渲染完成)引入外部 JS 文件。

defer 属性指定浏览器不阻塞渲染并继续下载 JS 代码,但等到 DOM 树建立后才执行下载完成的代码。async 属性指定浏览器不阻塞渲染并继续下载 JS 代码,并在下载完成后立即执行下载完成的 JS 代码。

拓展:外部 CSS 在 head 标签中引入的原因

(下文渲染 DOM 树指的是 DOM 树与 CSSOM(可能为浏览器默认样式) 结合生成 Render 树)

浏览器是自上而下解析 HTML 文档的,那么在渲染 DOM 树之前,浏览器就已经建立了 CSSOM 树,那么在渲染 DOM 树(即目的是生成 Render 树)时,浏览器将直接根据 CSSOM 树并结合 DOM 树生成 render 树,此举避免DOM 树先应用浏览器默认样式,之后再应用外部样式表的样式这样的多次应用样式的情况发生。

可以对比一下,若在 body 标签的最后引入外部 CSS 文件的话,那么在渲染完 DOM 树(此时是浏览器的默认样式)之后,浏览器又要重新将 DOM 树应用样式。这样可能导致性能问题,并且用户体验太差,可能因样式变化导致的页面跳动等问题。

结论:在 head 标签中引入外部 CSS 文件,是为了保证浏览器在渲染 DOM 树时,可以一次性渲染完成,并生成 Render 树。

拓展:外部 JS 在 body 标签结尾处引入的原因

因为不会阻塞页面的渲染。在下载并执行外部 JS 文件时,此时页面的渲染已经完成。此举让页面更快的完成渲染,减少用户等待时间(渲染未完成时,是白屏)。