页面渲染的过程
整个渲染的过程其实就是将URL中所对应的各种资源,通过浏览器自身的渲染引擎的解析,输出可视化的内容,图像等。
浏览器的主要功能是将用户选择的web资源呈现出来,它从服务器请求资源,并将得到的资源(HTML,PDF,image等等)显示在浏览器窗口。
通常,我们只需要编写HTML,CSS,JavaScript,浏览器上就能呈现出漂亮的网页了
浏览器会解析三个东西:
(1) HTML/SVG/XHTML,解析这三种文件会产生一个 DOM Tree。
(2) CSS,解析 CSS 会产生 CSS 规则树。
(3) JavaScript脚本,主要是通过 DOM API 和 CSSOM API 来操作 DOM Tree 和 CSS Rule Tree.
当浏览器获得一个html文件时,会`“自上而下”`加载,并在加载过程中进行解析渲染。
网页 构成:
基本元素和树状结构:
现在的语言是动态的语言, 需要CSS 样式语言 与 javascript语言;
CSS 是一种样式表语言, 用来描述元素的现实信息。
Javascript: 是一种解释型 的脚本语言, 主要目的是控制用户端的逻辑, 同用户交互等。 他可以修改HTML元素 及其内容。
UML: Unified Resource Locator, 网络上的每个资源都是有URL标记的, 他是URI(Unified Resource Identifier) 的一种实现。
- 浏览器会将HTML解析成一个DOM树,DOM 树的构建过程是一个深度遍历过程:当前节点的所有子节点都构建好后才会去构建当前节点的下一个兄弟节点。 \
- 将CSS解析成 CSS Rule Tree 。 \
- 根据DOM树和CSSOM来构造 Rendering Tree。注意:Rendering Tree 渲染树并不等同于 DOM 树,因为一些像 Header 或 display:none 的东西就没必要放在渲染树中了。 4.有了Render Tree,浏览器已经能知道网页中有哪些节点、各个节点的CSS定义以及他们的从属关系。下一步操作称之为Layout,顾名思义就是计算出每个节点在屏幕中的位置。
页面的优化
网页生成的时候,会渲染一次。用户访问的过程中,还会不断的渲染。
一下三种情况,会导致网页重新渲染。
- 修改DOM
- 修改样式表
- 用户事件(如:鼠标悬停、页面滚动、输入框输入文字、改变窗口大小)
重新渲染,就需要重新生成布局和重新绘制。前者叫做"重排"(reflow),后者叫做"重绘"(repaint)。
(1)Reflow(重排):浏览器要花时间去渲染,当它发现了某个部分发生了变化影响了布局,那就需要倒回去重新渲染。 \
(2)Repaint(重绘):如果只是改变了某个元素的背景颜色,文字颜色等,不影响元素周围或内部布局的属性,将只会引起浏览器的repaint,重画某一部分。 \
Reflow要比Repaint更花费时间,也就更影响性能。所以在写代码的时候,要尽量避免过多的Reflow。
需要注意的是,"重绘"不一定需要"重排",比如改变某个网页元素的颜色,就只会触发"重绘",不会触发"重排",因为布局没有改变。但是,“重排"必然导致"重绘”,比如改变一个网页元素的位置,就会同时触发"重排"和"重绘",因为布局改变了。
重排和重绘会不断触发,这是不可避免的。但是,它们非常耗费资源,是导致网页性能低下的根本原因。
提高网页性能,就是要降低"重排"和"重绘"的频率和成本,尽量少触发重新渲染。
前面提到,DOM变动和样式变动,都会触发重新渲染。但是,浏览器已经相当智能,会尽量把所有的变动集中在一起,排成一个队列,然后一次性执行,尽量避免多次重新渲染。
div.style.color = 'blue';
div.style.height = '30px';
此时为两次变动,浏览器只会触发一次重排和重绘。
div.style.color = 'red';
let box = parseInt(div.style.height);
div.style.height = '30px';
// div元素设置背景色以后,第二行要求浏览器给出该元素的位置,所以浏览器不得不立即重排。
从性能角度考虑,尽量不要把读操作和写操作,放在一个语句里面。
一般来说:
1 样式表越简单,重排和重绘就越快。
2.重排和重绘的DOM元素层级越高,成本就越高。
3.table元素的重排和重绘成本,要高于div元素
如果想提高性能,可以试试以下方法:
1. DOM 的多个读操作(或多个写操作),应该放在一起。不要两个读操作之间,加入一个写操作
2. 如果某个样式是通过重排得到的,那么最好缓存结果。避免下一次用到的时候,浏览器又要重排。
3. 不要一条条地改变样式,而要通过改变class,或者csstext属性,一次性地改变样式。
4. 尽量使用离线DOM,而不是真实的网面DOM,来改变元素样式。比如,操作Document Fragment对象,完成后再把这个对象加入DOM。再比如,使用 cloneNode() 方法,在克隆的节点上进行操作,然后再用克隆的节点替换原始节点。
5. 先将元素设为display: none(需要1次重排和重绘),然后对这个节点进行100次操作,最后再恢复显示(需要1次重排和重绘)。这样一来,你就用两次重新渲染,取代了可能高达100次的重新渲染。`
6. position属性为absolute或fixed的元素,重排的开销会比较小,因为不用考虑它对其他元素的影响。
7. 只在必要的时候,才将元素的display属性为可见,因为不可见的元素不影响重排和重绘。另外,visibility : hidden的元素只对重绘有影响,不影响重排。
8. 使用虚拟DOM的脚本库,比如React等。
9. 使用 window.requestAnimationFrame()、window.requestIdleCallback()
文章参考自:网页渲染与网页性能