浏览器渲染 详解

一次完整的请求过程


从输入 URL 到页面加载完成发生了什么事
1> DNS解析
2> TCP连接
3> 发送 HTTP请求
4> 服务器处理请求并返回 HTTP报文
5> 浏览器解析渲染页面

浏览器应该有的功能 : 网络,资源管理,网页浏览,多页面管理,插件与管理,账户和同步,安全机制,开发者工具

浏览器的主要功能总结起来就是一句话 : 将用户输入的url转变成可视化的图像

浏览器的内核(渲染引擎) : 在浏览器中有一个最重要的模块,它主要的作用是将页面转变为可视化的图像结果。这个模块就是浏览器内核,通常它也被称为渲染引擎
IE --> Trident
Safari --> WebKit
Chrome;Opera --> Blink
Firefox --> Gecko

一个渲染引擎主要包括 : HTML解析器,CSS解析器,布局layout模块,javascript引擎,绘图模块


渲染过程

1> 网页URL到构建DOM树的整个过程
    <1> 当用户输入URL的时候,Webkit调用资源加载器加载URL对应的网页
    <2> 加载器依赖网络模块建立连接,发送请求并接收答复
    <3> Webkit接收到各种网页或者资源的数据,其中某些资源可能是同步的或异步获取的
注 : 渲染引擎会自上而下解析HTML文档
    <4> 网页被交给HTML解析器转变一系列的词语 (Token)
    <5> 解析器根据词语构建节点 (node),形成DOM树
注 : 渲染引擎会将 DOM树 和 CSS树结合生成渲染树
    <6> 如果节点需要依赖于其他资源,例如 js、css、图片、视频等,会调用资源加载器来加载他们,这些操作都是异步的,不会阻碍dom树的继续创建、顺序执行、并发加载
            如果资源是 CSS的话,调用 CSS解析器解释将CSS解释成内部表示结构(CSSDOM)
注 : 在 <style> 标签中的样式,浏览器还是将其放在 HTML 解析器中进行处理
            如果资源是 JavaScript的话,调用 JavaScript引擎解释并执行
    <7> 阻塞
           CSS 阻塞 : CSS 在 <head>标签 中通过 <link>标签 引入会阻塞页面的渲染,这样做是因为可以避免闪屏现象
注 : 使用 <link>标签 引入样式,浏览器会等待样式文件加载完成后再继续解析 HTML 文档,如果在 <style>标签中则不会
            JS 阻塞 : 直接引入(包括引入JS文件和直接在 <script>标签中写入JS) 的JS会阻塞页面的渲染,这样做是因为 Javascript代码可能会修改DOM树的结构
    <8> 预解析 : WebKit 和 Firefox 都进行了这项优化,在执行 js脚本时,其他线程会解析文档的其余部分,找出并加载需要通过网络加载的其他资源。通过这种方式,资源可以在并行连接上加载,从而提高总体速度
注 : 1> 预解析器不会修改 DOM 树,而是将这项工作交由主解析器处理
        2> 预解析器只会解析外部资源 (例如外部脚本、样式表和图片) 的引用

2> 从DOM树到可视化图像
    <1> CSS文件被CSS解析器解释成内部表示结构(CSSDOM)
    <2> CSS解析器工作完成之后,在 DOM树上附加解释后的样式信息,这就是 RenderObject树
    <3> RenderObject 在创建的同时,Webkit 会根据网页的结构创建 RenderLayer,同时构建一个绘图上下文
    <4> 根据绘图上下文生成最终的图像 (这一过程需要依赖图形库)

3> 上面介绍的是一个完整的渲染过程,但现代网页很多都是动态的,这意味着在渲染完成之后,由于网页的动画或者用户的交互,浏览器其实一直在不停地重复执行渲染过程。(重绘重排),以上的数字表示的是基本顺序,这不是严格一致的,这个过程可能重复也可能交叉。浏览器是一个边解析边渲染的过程


CSS图层
浏览器在渲染一个页面时,会将页面分为很多个图层,图层有大有小,每个图层上有一个或多个节点

在渲染DOM的时候,浏览器所做的工作实际上是 :
1> 获取DOM后分割为多个图层
2> 对每个图层的节点计算样式结果 (Recalculate style -- 样式重计算)
3> 为每个节点生成图形和位置 (Layout -- 重排,回流)
4> 将每个节点绘制填充到图层位图中 (Paint -- 重绘)
5> 图层作为纹理上传至GPU
6> 符合多个图层到页面上生成最终屏幕图像 (Composite Layers--图层重组)

图层创建的条件
Chrome中满足以下任意情况就会创建图层 :
1> 拥有具有3D变换的CSS属性
2> 使用加速视频解码的<video>节点
3> <canvas>节点
4> CSS3动画的节点
5> 拥有CSS加速属性的元素(will-change)
6> 元素有一个z-index较低且包含一个复合层的兄弟元素 (换句话说就是该元素在复合层上面渲染)
注 : 合理的创建图层可以优化页面,但是图层创建太多将会给浏览器带来比较大的负担

重绘(Repaint)
重绘是一个元素外观的改变所触发的浏览器行为,例如改变outline、背景色等属性。浏览器会根据元素的新属性重新绘制,使元素呈现新的外观。重绘不会带来重新布局,所以并不一定伴随重排
注 : 如果图层中某个元素需要重绘,那么整个图层都需要重绘,比如一个图层包含很多节点,其中有个gif图,gif图的每一帧,都会重回整个图层的其他节点,然后生成最终的图层位图。所以这需要通过特殊的方式来强制gif图属于自己一个图层 translateZ(0) 或者 translate3d(0,0,0) ,CSS3的动画也是一样 (好在绝大部分情况浏览器自己会为CSS3动画的节点创建图层)

重排(Reflow 回流)
渲染对象在创建完成并添加到渲染树时,并不包含位置和大小信息,计算这些值的过程称为布局或重排。"重绘" 不一定需要 "重排",比如改变某个网页元素的颜色,就只会触发"重绘",不会触发"重排",因为布局没有改变。但是,"重排" 必然导致 "重绘",比如改变一个网页元素的位置,就会同时触发"重排"和"重绘",因为布局改变了
注 : 重排的代价高于重绘

触发重绘的属性
colorbackgroundoutline-color
border-stylebackground-imageoutline
border-radiusbackground-positionoutline-style
visibilitybackground-repeatoutline-width
text-decorationbackground-sizebox-shadow

触发重排(回流)的属性
盒子模型相关属性会触发重布局
定位属性及浮动也会触发重布局
改变节点内部文字结构也会触发重布局
widthtoptext-align
heightbottomoverflow-y
paddingleftfont-weight
marginrightoverflow
displaypositionfont-family
border-widthfloatline-height
borderclearvertival-align
min-height white-space
常见的触发重排的操作
Reflow 的成本比 Repaint 的成本高得多的多。DOM Tree 里的每个结点都会有 reflow 方法,一个结点的 reflow 很有可能导致子结点,甚至父点以及同级结点的 reflow。在一些高性能的电脑上也许还没什么,但是如果 reflow 发生在手机上,那么这个过程是非常痛苦和耗电的。所以,下面这些动作有很大可能会是成本比较高的 :
1> 当增加、删除、修改 DOM 结点时,会导致 Reflow , Repaint
2> 当移动 DOM 的位置
3> 当修改 CSS 样式的时候
4> 当 Resize 窗口的时候 (移动端没有这个问题)
5> 当修改网页的默认字体时
6> 获取某些属性时(width,height...)
注 : display:none 会触发 reflow,而 visibility:hidden 只会触发 repaint,因为没有发生位置变化

优化
如果需要使得动画或其他节点渲染的性能提高,需要做的就是减少浏览器在运行时所需要做的工作 (减少1234中的步骤)
1> 计算需要被加载到节点上的样式结果 (Recalculate style -- 样式重计算)
2> 为每个节点生成图形和位置 (Layout -- 回流和重布局)
3> 将每个节点填充到图层中 (Paint Setup和Paint -- 重绘)
4> 组合图层到页面上 (Composite Layers -- 图层重组)

1> 元素位置移动变换时尽量使用CSS3的transform来代替对top left等的操作 : 变换(transform) 和 透明度(opacity) 的改变仅仅影响图层的组合
2> 使用 opacity 来代替 visibility : 透明度的改变时,GPU在绘画时只是简单的降低之前已经画好的纹理的alpha值来达到效果,并不需要整体的重绘。不过这个前提是这个被修改opacity本身必须是一个图层,如果图层下还有其他节点,GPU也会将他们透明化
3> 不要使用table布局

4> 将多次改变样式属性的操作合并成一次操作 : 不要一条一条地修改DOM的样式,预先定义好class,然后修改DOM的className
5> 将 DOM离线后再修改 : 由于 display属性为none的元素不在渲染树中,对隐藏的元素操作不会引发其他元素的重排。如果要对一个元素进行复杂的操作时,可以先隐藏它,操作完成后再显示。这样只在隐藏和显示时触发2次重排
6> 利用文档碎片
7> 不要把某些DOM节点的属性值放在一个循环里当成循环的变量,当你请求向浏览器请求一些 style信息的时候,就会让浏览器flush队列,比如 :
    <1> offsetTop, offsetLeft, offsetWidth, offsetHeight
    <2> scrollTop/Left/Width/Height
    <3> clientTop/Left/Width/Height
    <4> width,height
当你请求上面的一些属性的时候,浏览器为了给你最精确的值,需要flush队列,因为队列中可能会有影响到这些值的操作。即使你获取元素的布局和样式信息跟最近发生或改变的布局信息无关,浏览器都会强行刷新渲染队列

8> 动画实现过程中,启用GPU硬件加速
9> 为动画元素新建图层,提高动画元素的z-index


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值