一次完整的HTTP请求过程:
- 地址解析:浏览器会将输入的地址解析成一个可访问的 URL(统一资源定位符)。
- DNS 解析:浏览器会向 DNS(域名系统)服务器发送一个请求,以获取输入域名对应的 IP 地址。
- 建立 TCP 连接:浏览器会使用 HTTP 或 HTTPS 协议与服务器建立一个 TCP 连接。通过三次握手来建立。
- 发起 HTTP 请求:一旦与服务器建立了 TCP 连接,浏览器会发送一个 HTTP 请求,其中包含了要获取的资源的详细信息,例如请求的方法(GET、POST 等)、资源路径、请求头(headers)等。
- 服务器响应:服务器收到请求后,会根据请求的内容进行处理,并发送一个相应的 HTTP 响应给浏览器。响应通常包含一个状态码(如200表示成功、404表示未找到等)、响应头和响应体等信息。
- 页面渲染:一旦浏览器接收到服务器的响应,它会开始对响应进行处理。首先,浏览器会检查响应的内容类型,以确定如何处理。对于 HTML 页面,浏览器会解析 HTML,并构建一个 DOM(文档对象模型)树。然后,它会根据 CSS 样式和 JavaScript 脚本对页面进行渲染和交互。
- 下载资源:在渲染页面的过程中,浏览器会下载 HTML 文件中引用的其他资源,如 CSS 文件、JavaScript 文件、图像、字体等。这些资源可以通过 HTTP 请求获取,并在下载完成后进行处理和展示。
- 页面展示:当浏览器完成页面渲染和资源下载后,它会将最终呈现的页面显示给用户。用户可以与页面进行交互,点击链接或执行其他操作。
页面渲染的7个阶段:
1. 构建DOM树,浏览器无法识别html文件,需要将html解析成DOM树,解析过程是一个深度遍历的过程,只有当某个节点下所有子节点都遍历完成了才会去解析下一个兄弟节点。DOM 是文档对象模型 (Document Object Model) 的缩写。它是 HTML 文档的对象表示,同时也是外部内容(例如 JavaScript)与 HTML 元素之间的接口。 根节点是Document对象。
2. 构建CSSOM树(层叠样式表模型:CSS Object Model)和HTML的解析过程同步执行,浏览器将识别所有的CSS样式信息,生成CSSOM树。例如:
注意:HTML、CSS的解析过程可能会被JS引擎线程所阻塞;样式为不可见的一些树节点(display:none)也会被构建进树结构中,只有在后面的render树会做过滤。
3. 构建渲染树(Render Tree)CSSOM树和DOM树合并,形成Render树。形成Render树的过程中,浏览器会筛选出所有可见的节点(visibility: hidden的元素在Render Tree中),针对可见节点匹配其CSSOM树种的CSS规则。
4. 布局阶段(Layout/reflow)布局阶段主要是将渲染树遍历,将元素嵌套关系以盒子模型的形式写入文档流。这个阶段会计算出每个树节点应该占据的空间以及在视图中的位置,一些布局的样式如float、absolute、fixed造成的偏移就会在这个阶段生效。
5. 绘制阶段(paint)将渲染树转化为像素,并对所有的媒体文件进行解码。
6. 栅格化(Rasterization):将绘制好的元素转换为位图,即将每个元素转换为屏幕上的一系列像素点。
7.合成(Compositing):通过合成所有位图,创建最终的页面视图。浏览器使用合成线程将位图绘制到用户界面上。
回流和重绘:
在页面初次加载完毕之后,我们可能由于交互或者一些JS操作,导致页面的布局和样式发生变化,从而重新触发浏览器的渲染过程,这期间就会涉及到回流和重绘两种情况。
回流/重排(reflow):当我们的操作引发了 DOM 几何属性的变化(比如修改元素的宽、高或隐藏元素等)时,浏览器需要重新计算元素的几何属性(其他元素的几何属性和位置也会因此受到影响),然后再将计算的结果绘制出来。相当于 刷新页面。
触发回流的情况:
- 改变DOM树的结构,如移动、删除、增加节点、修改节点尺寸等操作。
- 获取一些特定属性的值,如offsetTop、offsetLeft、 offsetWidth、offsetHeight、scrollTop、scrollLeft、scrollWidth、scrollHeight、clientTop、clientLeft、clientWidth、clientHeight等,这些属性都是需要通过实时计算得到的,浏览器为了获取他们的值,也会触发回流。
- 手动调用getComputedStyle方法等,也会触发浏览器的实时计算,同样也触发回流。
重绘(layout):当我们对 DOM 的修改导致了样式的变化、却并未影响其几何属性(比如修改了颜色或背景色)时,浏览器不需重新计算元素的几何属性、直接为该元素绘制新的样式(跳过了上图所示的回流环节)。相当于 不刷新页面,动态更新内容。
重绘不一定引起回流,回流必将引起重绘。
如何减少回流和重绘:
- 可以将要多次修改的DOM元素设置display:none,因为display:none的元素不会被包含在renderTree中,当修改完之后,在设置为display:block这样就只会触发一次回流操作。
- 使用transform做形变和位移可以减少reflow 采用绝对定位将一些复杂的元素脱离文档流,形成新的RenderLayer,降低回流成本。
- 减少上述CSS属性的使用。
- 使用 transform 替代 top。
- 使用 visibility 替换 display: none ,因为前者只会引起重绘,后者会引发回流(改变了布局)。
- 不要使用 table 布局,可能很小的一个小改动会造成整个 table 的重新布局。
- 动画实现的速度的选择,动画速度越快,回流次数越多,也可以选择使用requestAnimationFrame。
- CSS 选择符从右往左匹配查找,避免节点层级过多。
- 将频繁重绘或者回流的节点设置为图层,图层能够阻止该节点的渲染行为影响别的节点。比如对于 video 标签来说,浏览器会自动将该节点变为图层。