DOM渲染基础

背景

由于开发手淘内游戏面临的特定机型crash问题,最近在拜读朱永盛老师的《webkit技术内幕》,希望能发现crash的一些可能的原因。主要从渲染原理上切入,现在我把学习的内容总结一下,算是给自己有一个交代。

DOM树和RenderObject树

  • DOM树

    浏览器中的DOM元素以树形结构组织起来,DOM元素主要有可视化元素和非可视化元素。DOM结构树大致形如下图:

  • RenderObject树

    DOM树被webkit构建之后,就需要构建本DOM树对应的RenderObject树。一个RenderObject节点包含了绘制本节点所需要的各种信息,如相对位置,绘制上下文等,这些节点构成了一颗新树,这颗新树是为了属性递归计算和渲染所需而构建的内部表示。注意,RenderObject节点和DOM节点并非一一对应,
    具备以下条件的节点才会被webkit创建对应的RenderObject对象:

    1. DOM树的document节点;
    2. DOM树种的可视化节点,如html,div,img,canvas等,webkit不会为非可视化元素,如meta,script等创建RenderObject节点;
    3. 某些匿名的RenderObject对象,该对象不对应DOM中的任何节点,而是webkit处理的需要。典型的例子就是RenderBlock节点;
    4. 影子节点,如video组件中的播放控制栏,虽然js无法访问这些节点,但是需要渲染,所以也会为其创建RenderObject节点;

    RenderObject节点对象包含了一系列类似于DOM节点的API,除此之外,最重要的一组接口是与测算和绘图相关的,如paint,repaint等。

    _图中的RenderBoxModelObject是描述所有跟css样式中框模型相关的类的基类;RenderBlock用来表示块元素。_

    RenderObject对象以及RenderObject树的基本过程
    Webkit先检查DOM节点是否需要创建RenderObject对象,如果需要,webkit建立或者获取一个创建RenderObject对象的NodeRenderingContext对象,NodeRenderingContext对象会分析需要创建的RenderObject对象的父亲节点、兄弟节点等,设置这些信息后完成插入树的动作。建立后的RenderObject树和DOM树之间的对应关系大致如图:

    _HTMLDocument节点对应RenderView节点,RenderView节点是RenderObject树的根节点_

RenderLayer和RenderLayer树

  • 网页分层渲染
    网页上的DOM对象在渲染之前,都会按照一定逻辑划分不同的层次,这主要基于两方面考虑:一是数据结构的管理,二是渲染的优化。对于不同层次的划分,方便利用组合来递归管理;而对于渲染来说,分层可以对特定层次单独渲染,而不是每次渲染所有对象。
  • RenderLayer
    在理想情况下,网页的每个层次会被webkit创建对应的RenderLayer对象,一个RenderObject树约略对应一个RenderLayer节点,除非该RenderObject树中有节点具备某些规则,需要webkit单独为其创建对应的RenderLayer对象。RenderObject节点需要被创建对应RenderLayer取决于以下基本规则:

    1. DOM树的Document节点对应的RenderView节点。
    2. DOM树种的Document的子女节点,也就是HTML节点对应的RenderBlock节点。
    3. 显示的指定CSS位置的RenderObject节点。
    4. 有透明效果的RenderObject节点。
    5. 节点有溢出(Overflow)、alpha或者反射等效果的RenderObject节点。
    6. 使用Canvas2d或3D(WebGL)技术的RenderObject节点。
    7. Video节点对应的RenderObject节点。

    是否需要为RenderObject单独创建RenderLayer,取决于绘制效率和层次管理复杂度的平衡;因为RenderLayer是重绘区域的基本单位,RenderObject是绘制的基本单位,如果一个RenderObject节点复杂,而它所在的树其他节点都无需重绘,那么单独为其创建RenderLayer来重绘会比较划算。

  • RenderLayer树
    除了根节点,一个RenderLayer的父亲就是该RenderLayer对应的RenderObject节点祖先链中最近的祖先,并且祖先所在的RenderLayer节点同该节点的RenderLayer节点不同。基于这一原理,这些RenderLayer节点也构成了一颗RenderLayer树。

    理想情况下,一个RenderLayer对象会存在一个后端存储类与之对应,用于存储这个RenderLayer绘制的结果数据。

    RenderObject树和RenderLayer树之间的关系:

Render Context