Browser rendering

浏览器渲染原理

工作流程

1.浏览器解析三个东西

  • 解析HTML/SVG/XHTML,webkit有3个C++的类对应这三个文档,解析以后会生成一个DOM tree
  • css,解析css会生成css的rule tree
  • js,通过DOM API,CSSOM API来操作

2.解析完成后,浏览器会通过DOM Tree和Css rule tree来构造rendering tree

  • Rendering Tree 渲染树并不等同于Dom树,因为像display:none这种东西没必要存在到rendering tree中
  • css rule tree 是为了把css匹配到每一个rendering tree的element上,也就是所谓的frame
  • 然后计算每个frame的位置,这叫做layout和reflow的过程

3.最后通过操作系统的native GUI的API绘制

渲染

渲染流程

  • 计算css样式
  • 构建rendering tree
  • Layout:定位坐标的大小,是否换行,各种position,overflow,z-index属性
  • 正式开画

重新渲染

  • repaint:屏幕的一部分要重画,比如某得css的背景色变了,但是元素的尺寸没有变
  • reflow:意味着元件的几何尺寸变了,我们需要重新验证并计算render tree。html使用的是flow base layout ,也就是流式布局,所以,如果某元件的尺寸发生了变化,就需要重新布局,reflow会从<html>这个root frame 开始递归往下

渲染成本

  • reflow的成本比repaint的成本高的多,Dom tree里的每个节点都会有reflow方法,一个节点的reflow可能导致子节点,甚至父节点以及同级节点的reflow。

以下动作会引起渲染成本增高

  • 当你增加,删除,修改dom节点时,会导致repaint或reflow
  • 当你移动dom位置,或是搞个动画
  • 当你修改css样式的时候
  • 当你的resize窗口的时候,或是滚动的时候
  • 当你修改网页默认字体的时候
  • 注:display:none 会发生reflow,而visibility:hidden只会触发repaint,因为没有位置发生改变。
  • 滚屏的时候如果有position:fixed的时候,滚动浏览器是一个相当痛苦的过程。因为滚屏都可能造成reflow

造成reflow的原因

  • inital
  • incremental:js操作dom
  • resize:某些元素的尺寸变化
  • styleChange:css的属性发生变化
  • Dirty:几个incremental的reflow发生在同一个frame的子树上
1
2
3
4
5
6
7
8
9

var bstyle = document.body.style;

bstyle.padding = '20px'; //reflow, repaint
bstyle.color = 'blue'; //repaint
bstyle.fontSize = '2em'; //reflow, repaint

//new Dom reflow, repaint
document.body.appendChild(document.createTextNode('dude!'));

当然浏览器不会每次改一次样式,就reflow或repaint一次,一般来说,它会把这样的操作积攒,然后做一次reflow,这叫异步的reflow或者增量异步reflow,但是有些情况并不会这么做,比如:resize窗口,改变了页面默认的字体,这些操作会马上进行reflow

减少reflow/repaint

  • 1.不要一条一条的修改dom的样式,先定义好css的class,然后修改dom的className
  • 2.把dom离线后修改

    • 使用documentFragment对象在内存里操作dom,documentFragements是一些dom节点,他们不是dom树的一部分,创建一个文档片段,然后将创建的dom元素插入到文档片段中,最后把文档片段插入到dom中,因为文档片段存在于内存中,并不在dom树中,所以将子元素插入到文档片段不会引起页面回流

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      var ul = document.getElementByTagName('ul')[0];
      var docfrag = document.createDocumentFragment();
      var ss = ['asdsa', 'ssss', 'sssssdsd'];

      ss.foreach(function(e) {
      var li = document.createElement('li');
      li.textContent = e;
      docfrag.appendChild(li);
      })

      ul.appendChild(docfrag);
    • 不要把dom节点的属性值放在一个循环里当成循环的变量,会导致大量的读写这个节点的属性。

    • 尽可能的修改层级比较低的dom
    • 为动画的html原件使用fixed或absulote,修改他们的css不会reflow
    • 千万不要使用table布局,一个小改动会造成整个table重新布局

此文为总结文,可以查看原文,原文有很多很棒的信息
点此查看原文