浏览器渲染的那些事(一)


今天在推特看到一篇文章《当页面渲染时,浏览器发生了什么》,于是找了些资料,分享一下所得。

浏览器的主要概念

浏览器渲染的那些事(一)

浏览器主要分为这及部分。

  • User Interface

    用户接口。浏览器中的地址栏、前进后退、书签菜单等。除了网页显示区域以外的都是。
  • Brower engine

    浏览器引擎。查询与操作渲染引擎的接口。
  • Rendering engine

    渲染引擎。今天的内容主角就是它~负责显示请求的内容
  • Networking

    网络。用于网络请求,例如HTTP请求。
  • JavaScript Interpreter

    用于解析执行JavaScript代码
  • UI Backend

    绘制基础原件,比如组合框、窗口。
  • Data Persistence

    持久层。HTML5规定了完整的浏览器中的数据库:web database
    

主要流程

Webkit主要流程如下:
浏览器渲染的那些事(一)

Mozilla的Gecko渲染引擎主要流程

浏览器渲染的那些事(一)

由图可以发现,两个引擎过程基本相同。主要有三个步骤:

  1. 解析。浏览器会解析HTML/SVG/XHTML,事实上,webkit有三个C++的类对应这三类文档。浏览器解析这三种文件会产生一个DOM Tree;解析CSS,产生style rules;解析Javacript,主要通过DOM API和CSSOM API来操作DOM Tree和CSS Rule Tree

  2. 解析完成后,浏览器引擎会通过DOM Tree和CSS Rule Tree来构造Rendering Tree。

  3. 调用操作系统Native GUI的API绘制。

两个引擎的差别在于它们起的名字不一样,即语义差别:

  • webkit把可视化好的可视元素成为Render Tree,用Layout来表示元素的布局

  • Gecko把可视化好的可视元素成为Frame Tree,每个元素就是一个frame,元素的布局成为Reflow

当然,也不是只有语义差别。还有一个细小的差别差别在于:
Gecko在HTML与DOM树之间还多一个层content Sink,这是创建DOM对象的工厂。

DOM解析

解析由两部分组成:分词+构建树。
HTML5规范中,HTML解析流程如下图:
浏览器渲染的那些事(一)

分词是词法分析,把输入解析成符号序列。
构建树的过程就是在不断处理分词器完成的节点。除了把元素添加到DOM树上,还会将其添加到一个开放元素堆栈,用于纠正嵌套错误和标签未关闭错误。
但其实浏览器比我们想象的更强大更包容。浏览器的错误处理相当统一,虽然这不是当前HTML规范的一部分。当很多格式不良的HTML文档出现在很多网站,浏览器会尝试用和其他浏览器一样的方式修复错误。

<html>
<head>
    <title>Web page parsing</title>
</head>
<body>
    <div>
        <h1>Web page parsing</h1>
        <p>This is an example Web page.</p>
    </div>
</body>
</html>

DOM Tree解析如下:

浏览器渲染的那些事(一)

CSS解析

浏览器渲染的那些事(一)

Webkit使用Flext and Bison 解析器生成器,通过CSS语法文件自动创建解析器。Bison会创建自下而上的移位归约解析器。Firefox使用的是人工编写的自上而下的解析器。

构建呈现树 Render Tree/Frame Tree

渲染的流程如下:
浏览器渲染的那些事(一)
在这部分我们来讲一下构建Render Tree的过程。
呈现树主要是负责布局并将自身及其子元素绘制出来。
Webkits RenderObject类是所有呈现器的基类。定义如下:

class RenderObject{
    virtual void layout();
    virtual void paint(PaintInfo);
    virtual void rect repeatRect();
    Node* node; //DOM node
    RenderStyle* style; //the computed style
    RenderLayer* containgLayer; //the 
}

每个呈现器都代表了一个矩形区域,一般对应于相关节点的css框,包含宽度、高度、位置等几何信息。
不过对于一些具有复杂结构的元素,就对应了几个可见对象,这就不是一个矩形能表现出来的了。例如select元素【对应一个显示区域,一个下拉列表以及一个按钮】。
不规范的html也会产生多个渲染对象。【css规范中,一个行内元素只能仅包含行内元素或仅包含块状元素,在存在混合内容时,将会创建匿名的块状渲染对象包裹住行内元素。】

呈现树与DOM元素相对应,但不是一一对应。
非可视化的DOM元素就不会插入呈现树,例如head元素。
display为”none”的元素也不会显示在呈现树中【visibility:hidden的元素仍会显示】

除此之外,一些渲染对象和其对应的DOM节点不是在树上的位置也有所不同。
例如浮动元素和绝对定位元素在文本流之外,那么呈现树会标识出真实的结构,并用一个占位结构标识出它们原先的位置。
webkit代码中说明了如何根据display属性决定某个节点创建什么对象的渲染对象。

RenderObject* RenderObject::createObject(Node* node, RenderStyle* style)
{
    Document* doc = node->document();
    RenderArena* arena = doc->renderArena();
    ...
    RederObject* o = 0;
    
    switch(style->display()){
        case NONE:
            break;
        case INLINE:
            o = new (arena) RenderInline(node);
            break;
        case BLOCK:
            o = new (arena) RenderBlock(node);
            break;
        ...
    }    
    return o;
}

关于创建树的流程以及样式计算、重排重绘等部分,留到下一篇文章再写咯~

参考文献:

  1. 浏览器内部工作原理

  2. 浏览器的渲染原理简介

  3. How browsers work

  4. 有关网页渲染,每个前端开发者都该知道的那点事

  5. 前端文摘:深入解析浏览器的幕后工作原理


8 responses on “浏览器渲染的那些事(一)

发表评论

电子邮件地址不会被公开。 必填项已用*标注

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>