一个页面从输入URL到加载完成,这个过程发生了什么?
- 浏览器把请求的 URL 交给 DNS 解析,找到真实 IP;根据真实 IP 地址,向服务器发起 http 请求
- 服务器处理 http 请求,返回数据
- 浏览器接收数据(HTML、CSS、JS、媒体文件如图片视频等)
- 浏览器根据接收到的资源文件,进行页面加载渲染
页面的渲染过程
![ef630298a811a97cab1a891affe86eae.png](https://img-blog.csdnimg.cn/img_convert/ef630298a811a97cab1a891affe86eae.png)
- 根据 HTML 构建 DOM Tree
- 根据 CSS 构建 CSS Tree
- 将两颗树合并为 Render Tree(由这里可知若 CSS 没有加载完成,页面无法生成 Render Tree,所以 CSS 加载会阻塞页面的渲染)
- 浏览器根据 Render Tree 渲染页面
- 如遇到 <script> 则暂停渲染,优先加载并执行 JS 代码,执行完成再继续渲染(如果<script>元素引用了外部脚本,就下载该脚本再执行;否则就直接执行 JS 代码)
由于渲染引擎和 JS 引擎的互斥性,执行 JavaScript 会阻塞 HTML 的解析和渲染,因此我们应当把<script>标签都放在页面底部,这样即使遇到脚本失去响应,网页主体部分也已被渲染。
渲染的三个步骤:
- layout 计算布局:(文档流, 盒模型, 计算各个元素的大小、位置)
- paint 绘制:(将边框颜色、文字颜色、阴影等都画出来)
- composite 合成:根据层叠关系显示画面
如何优化页面的渲染?
1.如何防止 CSS 阻塞渲染?
- 对 CSS 进行压缩
- 合理使用缓存
- 减少 HTTP 请求,将多个 CSS 合并成一个文件,或者直接写成内联样式(缺点就是不能缓存)
2.在页面渲染时,如果遇到的 <script> 标签引用了外部文件,页面渲染过程中包含了请求文件以及执行文件的时间,但页面的首次渲染可能并不依赖这些文件,这些请求和执行文件的动作反而延长了页面加载的时间。为了避免这种现象,<script> 标签有三个属性:
- async 属性。立即请求文件,但不阻塞渲染引擎,文件加载完毕后再阻塞渲染引擎并立即执行文件内容。
- defer 属性。立即请求文件,但不阻塞渲染引擎,页面渲染完成之后再执行文件内容。
- type 属性,对应值为“module”(type = "module")。让浏览器按照 ECMA Script 6 标准将文件当作模块进行解析,默认阻塞效果同 defer,也可以配合 async 在请求完成后立即执行。
除此之外还应当注意:
当渲染引擎解析 HTML 遇到 <script> 标签引入文件时,会立即进行一次渲染(三个步骤)。所以构建工具会把编译好的引用 JavaScript 代码的 script 标签放入到 body 标签底部,因为当渲染引擎执行到 body 底部时会先将已解析的内容渲染出来,然后再去请求相应的 JavaScript 文件。如果是内联脚本(即不通过 src 属性引用外部脚本文件直接在 HTML 编写 JavaScript 代码的形式),渲染引擎则不会渲染。
辨析 window.onload 和 DOMContentLoaded:
- 前者:页面的全部资源加载完成,包括图片、视频等
- 后者:DOM 渲染完成,此时的图片、视频还可能没有加载完
- 一般使用后者判定页面是否加载完成
回流和重绘
- 引起 DOM 树结构变化,页面布局变化的行为叫回流。
- 只是样式的变化,不会引起 DOM 树变化,页面布局变化的行为叫重绘。
- 重绘不一定会引起回流,回流一定伴随重绘。
回流比重绘的代价要更高。我们应该怎么避免回流???
CSS层面:
- 避免使用 table 布局。
- 尽可能在 DOM 树的最末端改变 class。
- 将动画效果应用到 position 属性为 absolute 或 fixed 的元素上。
JS层面:
- 避免频繁操作样式,最好一次性重写 style 属性,或者将样式列表定义为 class 并一次性更改 class 属性。
- 也可以先为元素设置 display: none,操作结束后再把它显示出来。因为在 display 属性为 none的元素上进行的DOM操作不会引发回流和重绘。绘。
- 也可以先为元素设置 display: none,操作结束后再把它显示出来。因为在 display 属性为 none 的元素上进行的 DOM 操作不会引发回流和重绘。