游览器渲染浅析

前提知识

游览器构成

游览器内核

  • 游览器内核也称渲染引擎,主要有3种:
  • Trident内核: IE
  • Webkit内核:Chrome,Safari
  • Gecko内核:FireFox

解析前的工作

  1. DNS域名解析
  2. 建立TCP连接
  3. 发送HTTP请求
  4. 服务器处理请求
  5. 返回响应结果
  6. 关闭TCP连接
  7. 浏览器解析

关键渲染路径

浏览器从最初接收请求来的HTML、CSS、javascript等资源,然后解析、构建树、渲染布局、绘制,最后呈现给客户能看到的界面这整个过程——简单来说,就是对游览器渲染过程的描述。

渲染步骤

  1. 解析html,生成dom树
  2. 解析css,生成cssom树
  3. 将dom树和cssom树合并,生成渲染树
  4. 遍历渲染树,开始布局和计算
  5. 绘制渲染树,显示到屏幕

渲染图示

以webkit渲染引擎为例

渲染过程

解析html,生成dom树

  • 当浏览器接收到服务器响应来的HTML文档后,会自上而下扫描文档,开始解析,遍历文档节点,生成DOM树。
  • 整个构建过程其实包括: 字节 -> 字符 -> 令牌 -> 节点对象 -> 对象模型,下面是示例代码和配图:
<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <link href="style.css" rel="stylesheet">
    <title>Critical Path</title>
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg"></div>
  </body>
</html>

解析css,生成cssom树

  • 解析css文件,生成css规则树。
  • 每个css文件都被分析成一个stylesheet对象,每个对象都包含CSS规则。
  • css规则对象包含对应于css语法的选择器和声明对象以及其他对象。
  • 构建过程没有什么特别的差别,下面是示例代码和配图:
body { font-size: 16px }
p { font-weight: bold }
span { color: red }
p span { display: none }
img { float: right }

将dom树和cssom树合并,生成渲染树

  • 浏览器会先从dom树的根节点开始遍历每个可见节点,找到其适配的CSS样式规则并应用。
  • 将dom树与cssom树结合在一起,这就是渲染树。

渲染树与dom树

  • 每一个渲染对象都对应着dom节点,但是非视觉(隐藏,不占位)dom元素不会插入渲染树,如<head>元素或声明display: none;的元素。
  • 渲染对象与dom节点不是简单的一对一的关系,一个dom可以对应一个渲染对象,但一个dom元素也可能对应多个渲染对象,因为有很多元素不止包含一个css盒子。(如当文本被折行时,会产生多个行盒,这些行会生成多个渲染对象;又如行内元素同时包含块元素和行内元素,则会创建一个匿名块级盒包含内部行内元素,此时一个dom对应多个渲染对象)

遍历渲染树,开始布局和计算

  • 布局阶段会从渲染树的根节点开始遍历,然后确定每个节点对象在页面上的确切大小与位置。
  • 布局阶段的输出是一个盒子模型,它会精确地捕获每个元素在屏幕内的确切位置与大小,所有相对的测量值也都会被转换为屏幕内的绝对像素值。

绘制渲染树,显示到屏幕

在绘制阶段,浏览器会立即发出Paint Setup与Paint事件,开始将渲染树绘制成像素,绘制所需的时间跟CSS样式的复杂度成正比,绘制完成后,用户就可以看到页面的最终呈现效果了。

关键点

重绘/重排/回流

重绘(replaint)——屏幕的一部分重画,不影响整体布局,比如某个CSS的背景色变了,但元素的几何尺寸和位置不变。

重排(relayout)——意味着元件的几何尺寸变了,我们需要重新验证并计算渲染树。

回流(reflow)——Gecko中布局的称谓,同时也是重排的别称,是指渲染树的一部分或全部发生了变化。

阻塞问题

现代浏览器总是并行加载资源——当html解析器被脚本阻塞时,解析器虽然会停止构建dom,但仍会识别该脚本后面的资源,并进行预加载

css阻塞dom渲染

  • 默认情况下,css被视为阻塞渲染的资源,这意味着浏览器将不会渲染任何已处理的内容,直至cssom构建完毕(毕竟渲染树依赖于dom树和cssom树)。
  • (无论是外链还是内联)虽然css会阻塞dom渲染,但不会阻塞dom解析
  • 这也是为什么我们把样式放在HTML内容之前,以防止被呈现内容发生样式跳动。 当然代价就是显示延迟,所以性能攸关的站点都会内联所有CSS

    js阻塞dom解析

  • 由于js引擎和gui引擎互斥,所以当js执行的时候,gui就会被挂起,dom解析停滞,形成阻塞(无论是外链还是内联)。
  • 值得注意的是,js只会阻塞后续的dom而不一定是整个dom,也就是说,在这个js前面的dom可以被正确地解析以及渲染(这也是为什么我们把脚本放在页面底部,脚本仍在下载时页面已经可以正常地显示)。

css阻塞js执行

  • 正常情况是不会的,但是当执行的js依赖于css的样式时就会发生,这种情况发生在css文件排在js文件之前
  • 当js执行时,考虑到文档解析,如果样式尚未加载或解析,将会得到错误信息。firefox和webkit浏览器处理策略不同,前者会阻塞所有脚本,后者只会阻塞操作了该文件内声明的样式属性的脚本。
  • 浏览器遇到 <script>且没有defer或async属性的标签时,会触发页面渲染,因而如果前面CSS资源尚未加载完毕时,浏览器会等待它加载完毕在执行脚本。
  • 当css造成js执行延迟时,也意味着dom解析被延迟了(包括DOMContentLoaded事件),这也意味着页面渲染被阻塞了。

动态插入js和css时

  • 动态插入的外部样式表或脚本不会阻塞dom解析或渲染。
  • 动态插入的内联样式表或脚本会阻塞dom解析和渲染。
  • 未加入到dom结构的样式表或脚本(外部或内联)不会被下载、解析或执行(Image.src是特例)。
  • 使用innerHTML引入script标签,其中的js不会执行,但link标签会起作用。

改变js阻塞的方式

  • defer是渲染完再执行,async是下载完就执行(即不能保证执行顺序)。
  • defer相比普通script,有两点区别——载入js文件时不阻塞html的解析,执行阶段被放到html标签解析完成之后。
  • async加载的js依然会阻塞load事件。换句话说,async-script可能在 DOMContentLoaded 触发之前或之后执行,但一定在 load 触发之前执行。
  • 两者只在外链js中有效,并且使用document.createElement创建的script默认是async的。

普通图层和复合图层

描述

  • 浏览器渲染的图层一般包含两大类:普通图层以及复合图层。
  • 首先,普通文档流内可以理解为一个复合图层,这里称为默认复合层,里面不管添加多少元素,其实都是在同一个复合图层中。
  • 其次,absolute布局(fixed也一样),虽然可以脱离普通文档流,但它仍然属于默认复合层。
  • 然后,可以通过硬件加速的方式,声明一个新的复合图层,它会单独分配资源 (当然也会脱离普通文档流,这样一来,不管这个复合图层中怎么变化,也不会影响默认复合层里的回流重绘)。
  • 可以简单地理解为,GPU中各个复合图层是单独绘制的,所以互不影响,这也是为什么某些场景硬件加速效果一级棒。

    absolute和硬件加速的区别

  • 需要强调的是,absolute虽然可以脱离普通文档流,但是无法脱离默认复合层。 所以,就算absolute中信息改变时不会改变普通文档流中render树, 但是,浏览器最终绘制时,是整个复合层绘制的,所以absolute中信息的改变,仍然会影响整个复合层的绘制(浏览器会重绘它,如果复合层中内容多,absolute带来的绘制信息变化过大,资源消耗是非常严重的)。
  • 硬件加速直接就是在另一个复合层了,相当于另起炉灶,所以它的信息改变不会影响默认复合层 (当然了,内部肯定会影响属于自己的复合层),仅仅是引发最后的合成(输出视图)

    复合图层的作用

    一般一个元素开启硬件加速后会变成复合图层,可以独立于普通文档流中,改动后可以避免整个页面重绘,提升性能。但是尽量不要大量使用复合图层,否则由于资源消耗过度,页面反而会变的更卡

    如何变成复合图层(硬件加速)

  • 最常用的方式:translate3d、translateZ
  • opacity属性/过渡动画(需要动画执行的过程中才会创建合成层,动画没有开始或结束后元素还会回到之前的状态)
  • will-chang属性(这个比较偏僻),一般配合opacity与translate使用(而且经测试,除了上述可以引发硬件加速的属性外,其它属性并不会变成复合层), 作用是提前告诉浏览器要变化,这样浏览器会开始做一些优化工作(这个最好用完后就释放)
  • <video><iframe><canvas><webgl>等元素

硬件加速时请使用index

  • 使用硬件加速时,尽可能的使用index,防止浏览器默认给后续的元素创建复合层渲染
  • 具体的原理时这样的: webkit CSS3中,如果这个元素添加了硬件加速,并且index层级比较低, 那么在这个元素的后面其它元素(层级比这个元素高的,或者相同的,并且releative或absolute属性相同的), 会默认变为复合层渲染,如果处理不当会极大的影响性能
  • 简单点理解,其实可以认为是一个隐式合成的概念:如果a是一个复合图层,而且b在a上面,那么b也会被隐式转为一个复合图层,这点需要特别注意

优化手段

针对html

  • html文档结构层次尽量少,最好不深于6层
  • 首屏html可以少量,主体结构动态插入

    针对css

  • 使用媒体查询,减少初次cssom树的构建量
  • 尽量用id和class,不要过渡层叠
  • 样式结构层次尽量简单

    针对js

  • 使用defer和async,避免对文档的阻塞
  • 可以的话,动态插入js,避免阻塞

    针对引入位置

  • css放到head,让cssom树先行构建;js放到</body>前,保证dom树先行构建,不被阻塞
  • 避免js文件的插入跟在css文件之后,避免css解析对js执行的延迟,造成阻塞

    针对资源载入

  • 对页面资源进行压缩,对传输进行gzip压缩
  • 利用link标签的rel属性进行预解析,运用http缓存

参考资料

原文地址:https://www.cnblogs.com/kanyu/p/9829558.html

时间: 2024-11-09 22:01:30

游览器渲染浅析的相关文章

关于多核游览器指定渲染内核的方法。

代码示例 在head标签中添加一行代码: 1 <html> 2 <head> 3 <meta name="renderer" content="webkit|ie-comp|ie-stand"> 4 </head> 5 <body> 6 </body> 7 </html> content的取值为webkit,ie-comp,ie-stand之一,区分大小写,分别代表用webkit内核

谷歌游览器对&lt;input type=&#39;file&#39;&gt; change只能响应1次解决和样式的改变

在项目过程中遇到的需要上传本地文件,file的原始控件不太美观,但是这个控件和button有点不太一样, 改变这个样式的思路就是在控件外面套一层链接,然后把file控件的透明度设置为0(透明).样式只需要对外面那层进行操作就行. html代码: <td style="text-align: left;"> <a href="javascript:;" class="file">选择文件 <input type=&qu

纯CSS无hacks的跨游览器多列布局

利用纯CSS创建一个等高多列的布局并不件易事,本教程将着重分析出现在多列布局的多个问题,然后为大家等来一个简单全游览器通吃的解决方法,不使用图片,脚本,CSS hacks并在最严格的XHTML 规范中都能通过验证. 问题的症结所在 如上图所示,由于各列的内容不一致导致其背景的高度也不统一.而背景实质上是内容(标准的盒子模型是由内容区,补白区,边框区与边界区组成,背景存在于前三者中:IE为前两者)的自适应的问题.再转化一下,如何撑大那些较短的列的高度,让所有列的高度等于最高的列的高度?这个,的确很

js判断游览器是移动端还是PC端

js判断网页游览器是移动端还是PC端 1 <script type="text/javascript"> 2 function browserRedirect() { 3 var sUserAgent = navigator.userAgent.toLowerCase(); 4 var bIsIpad = sUserAgent.match(/ipad/i) == "ipad"; 5 var bIsIphoneOs = sUserAgent.match(/

移动端游览器自适应代码

移动端游览器自适应调整 1 <script type="text/javascript">// <![CDATA[ 2 var jsVer = 28; 3 4 var phoneWidth = parseInt(window.screen.width); 5 6 var phoneScale = phoneWidth / 640; 7 8 var ua = navigator.userAgent; 9 10 if (/Android (\d+\.\d+)/.test(

游览器保存密码和自动填充密码的困惑 (browser save password and auto fill password )

原文 refer : http://www.cnblogs.com/happyfreelife/p/4240100.html 当一个带有username and password 的表单被提交, 游览器会智能的询问用户是否要保存密码. 如果开发人员不希望这“智能”的事儿,可以使用ajax去提交表单,这样游览器就不会“智能”了. “ 当允许浏览器保存该网站的密码之后,下次打开该网站的任何一个页面时,浏览器会自动检测该页面是否有 password元素 ,如果有或者有多个,则自动填充对应的上次已保存的

关于火狐游览器设置全屏状态不显示工具栏以及其他游览器全屏问题

无论是IE,火狐,还是谷歌游览器.按F11可以实现全屏状态显示,再次按F11可以回到原先的网页显示状态. 清楚游览器缓存快捷键:ctrl+shift+delete 实现火狐游览器在全屏状态显示工具栏的方法: 第一步打开火狐游览器:输入about:config如下图: 单击"我保证小心"打开如下网页并找到browser.fullscreen.autohide将此后面的"值"修改为"false"即可在全屏状态下显示工具栏.如下图所示: 之后将游览器关

C# 调用动态链接库,给游览器写入Cookie

给游览器写入Cookie class Program { /// <summary> /// 写 /// </summary> /// <param name="lpszUrlName"></param> /// <param name="lbszCookieName"></param> /// <param name="lpszCookieData"></

在游览器上可以连网,Ionic打包后不能连接网络

在游览器上可以连网,Ionic打包后不能连接网络.可能是没有安装cordova-plugin-whitelist插件.  解决方案: