web 页面加载、解析、渲染过程
浏览器输入url到页面渲染
- 用户输入URL地址
- 浏览器查询缓存,若有缓存,直接展示
- 浏览器解析URL解析出主机名
浏览器将主机名转换为服务器ip地址
先查找本地DNS缓存,若没有,向浏览器默认DNS服务器发送查询请求,同时缓存
- 浏览器将端口号从URL解析出
- 浏览器向服务器发送TCP连接,三次握手
- 浏览器向服务器发送HTTP请求,请求数据包
- 服务器向浏览器返回HTTP响应,响应报文
- 关闭连接,浏览器解析文档,解析成DOM树,解析CSS样式,渲染页面
DOM和JavaScript的关系
文档对象模型(DOM)
是一个独立于语言,用于操作 XML 和 HTML 文档的 API.
DOM是一个与语言无关的API,别的语言也可以实现操作DOM的具体api,但是它在浏览器中是用JavaScript来实现的,因此,DOM是现在JavaScript编码中很重要的一部分,因为JavaScript很多时候都在操作底层文档。
操作DOM会很慢
DOM 和 JavaScript 是独立实现的,通过 js 操作 dom, 需要先连接,然后进行操作。
次数多,则操作会很慢
网页生成过程
- 将HTML构建成一个
DOM树
(Document Object Model 文档对象模型) - 将CSS构造
CSSOM树
(CSS Object Model CSS对象模型) 根据DOM树和CSSOM来构造
Rendering Tree
(渲染树)注意:Rendering Tree 渲染树并不等同于 DOM 树,因为一些像 Header 或 display:none 的东西就没必要放在渲染树中了。
- 生成
布局(layout)
,计算出每个节点在屏幕中的位置 - 将布局
绘制(paint)
在屏幕上
“生成布局”(flow)和”绘制”(paint)这两步,合称为”渲染”(render),耗时较多
重排和重绘
Reflow(回流/重排)
:当它发现了某个部分发生了变化影响了布局,渲染树需要重新计算。
Repaint(重绘)
: 根据元素的新属性重新绘制,使元素呈现新的外观。重绘不会带来重新布局,并不一定伴随重排
重排一定会引起重绘,而重绘不一定会引起重排
reflow原因
- 页面初始渲染
- 浏览器窗口大小发生改变
- 添加/删除可见DOM元素
- 改变元素位置
- 改变元素尺寸(宽、高、内外边距、边框等)
- 改变元素内容(文本或图片等)
- 元素字体大小变化
- 添加或者删除可见的DOM元素
- 激活CSS伪类(例如::hover)
- 设置style属性(例如:width/height 等)
- 查询某些属性或调用某些方法(例如:scrollIntoViewIfNeeded())
避免reflow
- DOM 的多个读操作(或多个写操作),应该放在一起。不要两个读操作之间,加入一个写操作
- 如果某个样式是通过重排得到的,那么最好缓存结果。避免下一次用到的时候,浏览器又要重排
- 不要一条条地改变样式,而要通过改变class,或者csstext属性,一次性地改变样式
- 尽量使用离线DOM,而不是真实的网面DOM,来改变元素样式。
比如,操作Document Fragment对象,完成后再把这个对象加入DOM。再比如,使用 cloneNode() 方法,在克隆的节点上进行操作,然后再用克隆的节点替换原始节点。 - 先将元素设为display: none(需要1次重排和重绘),然后对这个节点进行100次操作,最后再恢复显示(需要1次重排和重绘)。这样一来,你就用两次重新渲染,取代了可能高达100次的重新渲染
- position属性为absolute或fixed的元素,重排的开销会比较小,因为不用考虑它对其他元素的影响。
- 只在必要的时候,才将元素的display属性为可见,因为不可见的元素不影响重排和重绘。另外,visibility:hidden的元素只对重绘有影响,不影响重排
- 使用虚拟DOM的脚本库,比如React等
- 使用 window.requestAnimationFrame()、window.requestIdleCallback() 这两个方法调节重新渲染
- 少用table, table元素的重排和重绘成本,要高于div元素