HTML渲染的基本过程
一、HTML网页渲染的基本过程
将URL对应的各种资源,通过浏览器渲染引擎的解析,输出可视化的图像
二、浏览器的渲染引擎
一个渲染引擎大致包括HTML解释器、CSS解释器、布局和JavaScript引擎
HTML解析器:解释HTML语言的解释器,本质是将HTML文本解释成DOM树(文档对象模型)。
CSS解释器:解释样式表的解释器,其作用是将DOM中的各个元素对象加上样式信息,从而为计算最后结果的布局提供依据。
布局:将DOM和css样式信息结合起来,计算它们的大小位置等布局信息,形成一个能够表示这所有信息的内部表示模型即渲染树。
JavaScript引擎:JavaScript可以修改网页的内容,也能修改CSS的信息,JavaScript引擎解释JavaScript代码并把代码的逻辑和对DOM和CSS的改动信息应用到布局中去,从而改变渲染的结果。
这些模块依赖很多其他的基础模块,这其中包括网络,存储,2D/3D图形,音频视频和图片解码器等。实际上,渲染引擎中还应该包括如何使用这些依赖模块的部分,这部分的工作其实并不少,因为需要使用它们来高效的渲染网页。例如,利用2D/3D图形库来实现高性能的网页绘制和网页的3D渲染,这个实现非常非常的复杂。最后,当然,在最下面,依然少不了操作系统的支持,例如线程支持,文件支持等等。
三、浏览器的渲染过程
- 用户输入url地址,DNS(域名解析系统)服务器根据输入的域名查找对应 IP
- 浏览器向该IP地址服务器发送http请求,如果服务器段返回以301之类的重定向,浏览器根据相应头中的location再次发送请求
- 浏览器加载 HTML 文件及文件内包含的外部引用文件(CSS、JS)及图片,多媒体等资源。
- 据请求回来的 HTML 文件,渲染引擎开始从上到下解析HTML文档,首先将标签转换成DOM树中的DOM节点(包括js生成的标签),也叫内容树(Content Tree/DOM Tree),此时解析过程中碰见了外部引用的 CSS 文件,去服务器请求回 CSS 文件,构建 CSSOM (CSS Object Model)树,加载解析样式生成 CSSOM 树。CSSOM 的解析过程与 DOM 的解析过程是并行的。
- 此时继续解析 HTML,又碰见了外部引用的 JS 文件,去服务器请求回 JS 文件,加载并执行 JS 代码(包括内联代码或外联 JS 文件)
- 此时在解析 HTML 过程中发现一个标签内引用了一张关联图片,去服务器请求回这张图片,浏览器解析器不会等待图片下载完,而是继续渲染后面的代码。
- 此时 HTML 代码和 CSS 代码已经形成 DOM 树和 CSSOM 树,并生成渲染树(render tree),渲染树按顺序展示在屏幕上的一系列矩形,这些矩形带有字体,颜色和尺寸等视觉属性。
- 布局(layout):从根节点递归调用,计算每一个元素的大小、位置等,给出每个节点所应该在屏幕上出现的精确坐标;
- 绘制(painting):遍历渲染树绘制所有节点,为每一个节点适用对应的样式,这一过程是通过 UI 后端模块完成。
四、Repaint(重绘)和Reflow(重排/重构/回流)
1.Repaint(重绘)
Repaint(重绘)改变不影响元素在网页中的位置的元素样式时,浏览器会根据元素的新属性重新绘制一次,使元素呈现新的外观,譬如background-color(背景色), border-color(边框色), visibility(可见性)。重绘不会带来重新布局,所以并不一定伴随重排。
2.Reflow(重排/重构/回流)
Reflow(重排/重构/回流)当渲染树中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建, 这就称为回流(reflow)。每个页面至少需要一次回流,就是在页面第一次加载的时候。
触发重排的条件
1、页面渲染初始化;(无法避免)
2、添加或删除可见的DOM元素;
3、元素位置的改变,或者使用动画;
4、元素尺寸的改变——大小,外边距,边框;
5、浏览器窗口尺寸的变化(resize事件发生时);
6、填充内容的改变,比如用户在input输入框中输入文字、文本的改变或图片大小改变而引起的计算值宽度和高度的改变;
7、激活CSS伪类(例如::hover)
8、读取某些元素属性
3.重排和重绘的关系
-
- 在重排的时候,浏览器会使渲染树中受到影响的部分失效,并重新构造这部分渲染树,完成重排后,浏览器会重新绘制受影响的部分到屏幕中,该过程称为重绘。
- 重排一定会导致重绘,但是重绘不一定导致重排。
4.重排优化
重绘重排的代价:耗时,导致浏览器卡慢。
1、浏览器优化
1)分离读写操作。
DOM的多个读操作或者写操作,应该放在一起,不要两个读操作之间加入一个写操作
2)class样式集中修改
3)缓存需要修改的dom元素(将DOM离线)
- 使用display:none。一旦我们给元素设置display:none时(只有一次重排重绘),元素便不会再存在在渲染树种,相当于将其从页面上“拿调”,我们之后的操作将不会触发重绘和重排,添加足够多的变更后,通过display属性显示(另一次重排重绘)。通过这种方式即时大量变更也只是触发两次重排,visibility:hidden的元素只对重绘有影响,不影响重排
- 通过documentFragment创建一个dom碎片,在它上面批量操作dom,操作完成之后,再添加到文档中,这样只会触发一次重排。
- 复制节点,在副本上工作,然后替换他
4)尽量使用absolute或fixed脱离文档流,对其他元素影响不大
- 使用绝对定会使的该元素单独成为渲染树中的body的一个子元素,重排开销比较小,不会对其它节点造成太多的影响,当你在这些节点上放置这个元素时,,一些其它在这个区域内的节点可能需要重绘,但是不需要重排。
5)不要通过父元素修改子元素尺寸大小,修改子元素同时不要影响父元素
6)优化动画
- 可以把动画效果应用到position属性为absolute或fixed的元素上,这样对其他元素影响较小
- 启用GPU加速GPU硬件加速是指应用GPU的图形性能对浏览器中的一些图形操作交给GPU来完成,GPU专门是为处理图形而设计的,在速度和能耗上更有效率
2、 css优化
1)注意transform不会触发重绘回流、transform属于合成属性,对合成属性精选transiton/animate动画时,将会创建一个合成层。使得动画元素在独立的层进行渲染,元素的内容没发生变化就没必要发生重绘。
2)使用visibility替换display:none,因为前者只会引起重绘,后者会造成重排。
3)避免设置多层内联样式,css选择符从右往左匹配查找,避免元素层级过多,保证层级扁平
4)将动画效果应用到position属性为absolute或fixed的元素上,避免影响其他元素的布局,这样当动画变化的时候只会造成重绘不会造成重排,并且控制动画速度可以选择requestAnimationFrame(他的作用是:把每一帧中的所有DOM操作集中起来,在一次重绘/重排中就完成,并且重绘/重排的时间间隔跟随浏览器的刷新频率,在隐藏不可见的元素中,requestAnimationFrame将不会进行重绘重排,这就意味着更少的gpu、cpu和内存使用量)
5)避免使用css表达式,可能会引发重排
6)css3硬件加速可以让transform、opacity、filters这些动画不会引起重排重绘,但是对于动画的其他属性,例如background-color这些,还是会引起重绘
7)尽量对复杂的动画元素使用绝对定位,使它脱离文档流,否则会引起父元素及其后续元素频繁重排。
3、js优化
1)避免频繁的操作样式,最好一次性重写style属性,或者将样式列表定义为class并一次性更改class属性
2)避免频繁操作DOM,创建一个documentFragment,在它上面应用所有DOM操作。最后把他添加到文档中
documentFragment表示一个没有父级文件的最小文档对象,相当于是一个轻量版的Document,用于存储已排好版或尚未打理好格式的xml片段,他与document最大的区别在于:documentFragment不是真实DOM树的一部分,他的变化不会引起DOM树的重新渲染操作,且不会导致性能问题。
3)避免频繁读取会引发重排/重绘的属性,如果确实需要多次使用,那就用一个变量存储起来。