浏览器页面渲染

浏览器页面渲染

1、HTML 的加载

页面上输入 URL 后,会先拿到 HTML 文件。HTML是一个页面的基础,所以会在最开始的时候下载它,下载完毕后就开始对它进行解析

2、其他静态资源的下载

HTML 在解析的过程中,如果发现 HTML 文本里面有一些外部的资源链接,比如 CSS、JS 和图片等,会立即启用别的线程下载这些静态资源。在 head 中遇到 JS 文件时,HTML 的解析会停 下来,等 JS 文件下载结束并且执行完,HTML 的解析工作再接着来,防止 JS 修改已经完成的解析结果

知识拓展

由上得知,JS 文件放在 head 中属于同步加载,会阻塞 DOM 树的构建,进而影响页面的加载。当 JS 文件较多时,页面白屏的时间也会变长

那如何避免这个问题呢?下面有三个解决方案

1、 script 放在 body 里(一般是 </body> 上面)

由于 DOM 是自上而下解析的,因此 JS 不会阻塞 DOM 的解析,而且这时候可以在 JS 中操作 DOM

2、设置 defer 属性

通过给 script 标签设置 defer 属性,将脚本文件设置为延迟加载,当浏览器遇到带有 defer 属性的 script 标签时,会再开启一个线程去下载 JS 文件,同时继续解析 HTML,等 HTML 全部解析完、DOM 加载完成之后,再去执行加载好的 JS 文件。只适用于引用外部 JS 文件,并且可以确保所有加了 defer 属性的脚本会按顺序执行

3、设置 async 属性

async 属性和 defer 属性类似,也是会开启一个线程去下载js文件,但和 defer 不同的是,aysnc 会在 JS 加载完成后立刻执行,而不是会等到 DOM 加载完成之后再执行,所以还是有可能会造成阻塞。同样的也是只适用于外部 JS 文件,如果有多个设置了 aysnc 的 JS 文件,不能像 defer 那样保证按照顺序执行

4、动态创建脚本

这样去动态创建文件也不会影响到页面的加载,以下是百度统计的demo

<script>
 var _hmt = _hmt || [];
(function () {
  var hm = document.createElement("script");
  hm.src = "https://hm.baidu.com/hm.js";
  var s = document.getElementsByTagName("script")[0];
  s.parentNode.insertBefore(hm, s);
})();
</script>

3、DOM 树的构建

DOM 的全称是:文档对象模型(Document Object Model),在 HTML 解析时,解析器会把解析完的 HTML转化成 DOM 对象,再通过深度优先遍历构建 DOM 树

  • Loading:网络通信和HTML解析

  • Scripting:Javascript执行

  • Rendering:样式计算和布局,即重排

  • Painting:重绘 对应的详细事件

4、CSSOM 树的构建

当 CSS 下载完,CSS 解析器就开始对 CSS 进行解析,把 CSS 解析成 CSS 对象,然后把这些 CSS 对象组装成一颗 CSSOM 树

5、渲染树的构建

(1)DOM 树和 CSSOM 树都构建完成以后,浏览器会根据这两棵树构建出一棵渲染树render tree

  • 每个node都有自己的style,而且render tree不包含隐藏的节点(display:none的节点,还有head节点) 。

  • 有了render tree,我们就知道了所有节点的样式,然后计算他们在页面上的大小和位置(布局),最后就可以把节点绘制到页面上(绘制)。

  • 由于浏览器使用流式布局,对Render Tree的计算通常只需要遍历一次就可以完成,table及其内部元素除外,他们可能需要多次计算,通常要花3倍于同等元素的时间,这也是为什么要避免使用table布局的原因之一

(2)回流:当render tree中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建。这就称为回流(reflow)。每个页面至少需要一次回流,就是在页面第一次加载的时候,这时候是一定会发生回流的,因为要构建render tree。在回流的时候,浏览器会使渲染树中受到影响的部分失效,并重新构造这部分渲染树,完成回流后,浏览器会重新绘制受影响的部分到屏幕中,该过程成为重绘。

(3)重绘: 当render tree中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会影响布局的,比如background-color。则就叫称为重绘。

区别: 回流必将引起重绘,而重绘不一定会引起回流。比如:只有颜色改变的时候就只会发生重绘而不会引起回流。当页面布局和几何属性改变时就需要回流。比如:添加或者删除可见的DOM元素,元素位置改变,元素尺寸改变——边距、填充、边框、宽度和高度,内容改变

优化:

浏览器:所以我们能得知回流比重绘的代价要更高,回流的花销跟render tree有多少节点需要重新构建有关系。因为这些机制的存在,所以浏览器会帮助我们优化这些操作,浏览器会维护1个队列,把所有会引起回流、重绘的操作放入这个队列,等队列中的操作到了一定的数量或者到了一定的时间间隔,浏览器就会flush队列,进行一个批处理。这样就会让多次的回流、重绘变成一次回流重绘。

自己的优化:但是靠浏览器不如靠自己,我们可以改变一些写法减少回流和重绘。比如改变样式的时候,不去改变他们每个的样式,而是直接改变className 就要用到cssText 但是要注意有一个问题,会把原有的cssText清掉,比如原来的style中有’display:none;’,那么执行完上面的JS后,display就被删掉了。 为了解决这个问题,可以采用cssText累加的方法,但是IE不支持累加,前面添一个分号可以解决。还有添加节点的时候比如要添加一个div里面有三个子元素p,如果添加div再在里面添加三次p,这样就触发很多次回流和重绘,我们可以用cloneNode(true or false) 来避免,一次把要添加的都克隆好再appened就好了,还有其他很多的方法就不一一说了

  1. CSS
  • 使用 transform 替代 top

  • 使用 visibility 替换 display: none ,因为前者只会引起重绘,后者会引发回流(改变了布局

  • 避免使用table布局,可能很小的一个小改动会造成整个 table 的重新布局。

  • 尽可能在DOM树的最末端改变class,回流是不可避免的,但可以减少其影响。尽可能在DOM树的最末端改变class,可以限制了回流的范围,使其影响尽可能少的节点。

  • 避免设置多层内联样式,CSS 选择符从右往左匹配查找,避免节点层级过多。

    <div>
      <a> <span></span> </a>
    </div>
    <style>
      span {
        color: red;
      }
      div > a > span {
        color: red;
      }
    </style>
  • 对于第一种设置样式的方式来说,浏览器只需要找到页面中所有的 span 标签然后设置颜色,但是对于第二种设置样式的方式来说,浏览器首先需要找到所有的 span 标签,然后找到 span 标签上的 a 标签,最后再去找到 div 标签,然后给符合这种条件的 span 标签设置颜色,这样的递归过程就很复杂。所以我们应该尽可能的避免写过于具体的 CSS 选择器,然后对于 HTML 来说也尽量少的添加无意义标签,保证层级扁平。

  • 将动画效果应用到position属性为absolutefixed的元素上,避免影响其他元素的布局,这样只是一个重绘,而不是回流,同时,控制动画速度可以选择 requestAnimationFrame,详见探讨 requestAnimationFrame

  • 避免使用CSS表达式,可能会引发回流。

  • 将频繁重绘或者回流的节点设置为图层,图层能够阻止该节点的渲染行为影响别的节点,例如will-changevideoiframe等标签,浏览器会自动将该节点变为图层。

  • CSS3 硬件加速(GPU加速),使用css3硬件加速,可以让transformopacityfilters这些动画不会引起回流重绘 。但是对于动画的其它属性,比如background-color这些,还是会引起回流重绘的,不过它还是可以提升这些动画的性能。

  1. JavaScript

  • 避免频繁操作样式,最好一次性重写style属性,或者将样式列表定义为class并一次性更改class属性。

  • 避免频繁操作DOM,创建一个documentFragment,在它上面应用所有DOM操作,最后再把它添加到文档中。

  • 避免频繁读取会引发回流/重绘的属性,如果确实需要多次使用,就用一个变量缓存起来。

  • 对具有复杂动画的元素使用绝对定位,使它脱离文档流,否则会引起父元素及后续元素频繁回流。

6、布局计算

渲染树构建完成以后,所有元素的位置关系和需要应用的样式就确定了。这时候浏览器会计算出所有元素的大小 和绝对位置

7、渲染

布局计算完成以后,浏览器就可以在页面上渲染元素了,经过渲染引擎的处理后,整个页面就显示在了屏幕上

performance介绍

1、点击录制,刷新一下页面或者滚动等操作让页面渲染一下,然后点击stop,就可以看到页面的渲染情况。

浏览器页面渲染浏览器页面渲染

浏览器页面渲染

  • Loading:网络通信和HTML解析

  • Scripting:Javascript执行

  • Rendering:样式计算和布局,即重排

  • Painting:重绘 对应的详细事件

 

2、点击DevTools右上角的菜单,在More tools里面选中Rendering,这里面包含了如下其他各种工具的设置项

浏览器页面渲染

Paint Flashing 高亮显示网页中需要被重绘的部分。

Layer Borders 显示Layer边界。

FPS Meter 每一秒的帧细节,帧速率的分布信息和GPU的内存使用情况。浏览器页面渲染

  • 1312.3ms:一帧的时间

  • 1fps: 每秒的帧数

Scrolling Performance Issues 分析鼠标滚动时的性能问题,会显示使屏幕滚动变慢的区域。

Emulate CSS Media 仿真CSS媒体类型,查看不同的设备上CSS样式效果,可能的媒体类型选项有print、screen