【CRR学习笔记】浏览器渲染html过程

356 阅读4分钟

###前言 浏览器输入url到页面加载、渲染,这一过程经历了什么,这是我在面试中遇到过的问题。 但是我只回答了:

  1. DNS域名解析
  2. TCP连接
  3. HTTP请求
  4. 服务器响应
  5. 客户端渲染

面试官就问我客户端是怎么渲染……嗯? 这个似乎是看过,但是忘记了,所以我就随便说了几句话,然后面试完后台就灰啦。这是很基础的问题,作为前端开发者,怎么能连浏览器渲染html的过程都不知道呢。

那么我们开始吧。

学习自文章:浏览器的渲染

客户端渲染HTML过程

  1. 处理 HTML 标记并构建 DOM 树。
  2. 处理 CSS 标记并构建 CSSOM 树。
  3. 将 DOM 与 CSSOM 合并成一个 Render 渲染树。
  4. 根据渲染树来布局,以计算每个节点的几何信息。
  5. 将各个节点绘制到屏幕上

CSS和Javascript阻塞资源

首先,我们要明确一个知识点,CSS和JavaScript会阻塞渲染。

  1. CSS被视为阻塞渲染的资源,意味着浏览器将不会渲染任何已处理的内容,直到CSSOM构建完毕;
  2. JavaScript不仅可以读取和修改DOM属性,还可以读取和修改CSSOM属性;
  3. CSSOM构建时,JS执行将暂停,直到CSSOM就绪;

渲染树(Render-Tree)的关键渲染路径中,要求同时具有 DOM 和 CSSOM,之后才会构建渲染树。 即,HTML 和 CSS都是阻塞渲染的资源。HTML显然是必需的,因为包括我们希望显示的文本在内的内容,都在 DOM 中存放,那么可以从 CSS 上想办法。

用媒体类型和媒体查询来解除对渲染的阻塞。

<link href="index.css" rel="stylesheet">
<link href="print.css" rel="stylesheet" media="print">
<link href="other.css" rel="stylesheet" media="(min-width: 30em) and (orientation: landscape)">

第一个资源会加载并阻塞。 第二个资源设置了媒体类型,会加载但不会阻塞,print声明只在打印网页时使用。 第三个资源提供了媒体查询,会在符合条件时阻塞渲染。

而JS比CSS更复杂点,例如:

<p>Do not go gentle into that good night,</p>
<script>console.log("inline")</script>
<p>Old age should burn and rave at close of day;</p>
<script src="app.js"></script>
<p>Rage, rage against the dying of the light.</p>

<p>Do not go gentle into that good night,</p>
<script src="app.js"></script>
<p>Old age should burn and rave at close of day;</p>
<script>console.log("inline")</script>
<p>Rage, rage against the dying of the light.</p>

这样的 script 标签会阻塞 HTML 解析,无论是不是 inline-script。上面的 P 标签会从上到下解析,这个过程会被两段 JavaScript 分别打断一次(加载并且执行的时间段内)。 实际工程时,我们常将资源放到文档底部。

改变资源阻塞

defer/async

首先,注意 async 与 defer 属性对于inline-script 都是无效的,所以下面这个示例中三个 script 标签的代码会从上到下依次执行。

<!-- 按照从上到下的顺序输出 1 2 3 -->
<script async>
  console.log("1");
</script>
<script defer>
  console.log("2");
</script>
<script>
  console.log("3");
</script>

defer\async针对设置了 src 属性的 script 标签。

使用方法:

<script src="app1.js" defer></script>
<script src="app.js" async></script>

- 区别
defer 延迟执行引入的js文件,并行加载,等待document解析完毕,defer加载完毕,再按照顺序执行,接着触发DOMContentLoaded事件;
async 异步执行引入的js文件,加载好就开始执行,无论html解析阶段还是DOMContentLoaded触发之后。这种方式加载的 JavaScript 依然会阻塞 load 事件。换句话说,async-script 可能在 DOMContentLoaded 触发之前或之后执行,但一定在 load 触发之前执行。

document.createElement('script').async

document.createElement('script').async创建script默认是异步的。 动态添加script标签引入JavaScript文件默认不会阻塞页面,如果想同步执行,需要把async的属性人为设置为false。 使用document.createElement创建link元素,引入资源,在Chrome中不会阻塞。

const style = document.createElement("link");
style.rel = "stylesheet";
style.href = "index.css";
document.head.appendChild(style); // 阻塞?

document.write,innerHTML

不推荐,不太可行。