Skip to content

Latest commit

 

History

History
140 lines (93 loc) · 8.36 KB

0.zh.md

File metadata and controls

140 lines (93 loc) · 8.36 KB

2014年8月8日

让我们构建一个浏览器引擎!

第1部分: 入门

我正在构建一个玩具 - HTML渲染引擎,我认为你也应该这样做. 这是一系列文章中的第一篇:

完整系列将描述我编写的代码,并展示如何制作自己的代码. 但首先,让我解释一下原因.

你在建一个什么?

我们来谈谈术语. 一个浏览器引擎是Web浏览器的"底层工作"的一部分,用于从互联网上获取网页,并将其内容翻译成您可以 阅读,观看,收听 的表格. Blink,Gecko,WebKit和Trident 是浏览器引擎. 相比之下,浏览器有了自己的UI选项卡,工具栏,菜单等叫做chrome. Firefox和SeaMonkey是两个不同的浏览器chrome,但是同样的 Gecko 发动机.

浏览器引擎包括许多子组件: HTTP客户端,HTML解析器,CSS解析器,JavaScript引擎 (本身由解析器,解释器和编译器组成) 等等. 那些涉及解析Web格式 (如HTML和CSS) 并将它们转换为您在屏幕上看到的内容的组件有时也称为 布局引擎要么 渲染引擎.

为什么一个"玩具"渲染引擎?

功能齐全的浏览器引擎非常复杂. Blink,Gecko,WebKit -- 每个都有数百万行代码. 甚至更年轻,更简单的渲染引擎ServoWeasyPrint都是万条线. 对于新手来说,理解并不是很简单的事情!

说到非常复杂的软件: 如果你在编译器或操作系统上过课,在某些时候你可能会创建或修改"玩具"编译器或内核. 这是一个专为学习而设计的简单模型; 除了编写它的人之外,它可能永远不会被任何人操作. 但制作玩具系统 是 学习真实物品如何运作的有用工具. 即使您从未构建真实的编译器或内核,了解它们的工作方式也可以帮助您在编写自己的程序时更好地使用它们.

那么,如果你想成为一名浏览器开发者,或者只是想了解浏览器引擎内部发生了什么,为什么不建立一个玩具呢? 就像实现"真实"编程语言子集的玩具编译器一样,玩具渲染引擎可以实现 HTML和CSS 的一小部分. 它不会取代日常浏览器中的引擎,但应该说明渲染 简单HTML文档 所需的基本步骤.

在家尝试一下.

我希望我说服你试一试. 如果您已经拥有一些可靠的编程经验并且了解一些 高级HTML和CSS概念,那么本系列将是最容易理解的. 但是,如果你刚开始使用这些东西,或遇到你不理解的事情,请随意提问,我会尽量让它更清楚.

在开始之前,您可以对一些选择做一些评论:

论编程语言

您可以使用任何编程语言构建玩具布局引擎. 真的! 来吧,使用你熟悉和喜爱的语言. 或者以此为借口学习一门新语言,如果这听起来很有趣.

如果您想开始为 Gecko或WebKit 等主要浏览器引擎做贡献,您可能希望使用C ++,因为它是这些引擎中使用的主要语言,使用它可以更容易地将代码与他们的代码进行比较.

我自己的玩具项目,罗宾逊,是用rust写的. 我是 Mozilla的Servo团队的一员,所以我非常喜欢 Rust编程. 此外,我在这个项目中的目标之一 是更多地了解 Servo的实现. Robinson有时会使用 来自Servo数据结构和代码 的简化版本.

论图书馆与捷径

在这样的学习练习中,你必须决定是否"借借地"使用别人的代码, 而不是从头开始编写自己的代码. 我的建议是为 你真正想要理解的部分 编写自己的代码,但不要害怕将 库 用在 其他所有的部份. 学习如何使用 特定的库本身 就是一项有价值的练习.

我正在写robinson,不仅仅是为了我自己,还需要 这些文章和练习 的示例代码. 由于这个原因和其他原因, 我希望它尽可能地小而且独立. 到目前为止,除了 Rust标准库之外,我没有使用任何外部代码. (这也解决了在使用相同版本的Rust时,使用相同版本的Rust构建多个依赖项的轻微麻烦,但语言仍在开发中. ) 但这条规则并非一成不变. 例如,我可能稍后决定使用图形库 而不是 编写我自己的低级绘图代码.

避免编写代码的另一种方法是将 跳过 事情. 例如,robinson 还没有网络代码; 它只能读取本地文件. 在玩具程序中,如果您愿意,可以跳过一些东西. 我会指出这样的潜在快捷方式,所以你可以绕过不感兴趣的步骤,直接跳到好东西. 如果你改变主意,你可以随时填补空白.

第一步: DOM

你准备好写一些代码吗? 我们将从一些小事做起: 数据结构DOM. 我们来看看罗宾逊的dom 模块.

DOM是节点树. 节点具有零个或多个子节点. (它还有其他各种属性和方法,但我们暂时可以忽略其中的大部分属性和方法. )

    struct Node {
        // data common to all nodes:
        children: Vec<Node>,

        // data specific to each node type:
        node_type: NodeType,
    }

有几个节点类型,但是现在我们将忽略它们中的大部分,并说节点是 Element或Text节点. 在具有继承的语言中,这些将是子类型Node. 在Rust中,它们可以是枚举 (Rust的关键字用于"标记联合-tagged union"或"求和类型-sum type"):

    enum NodeType {
        Text(String),
        Element(ElementData),
    }

元素包括 标记名称和任意数量的属性,这些属性可以存储为从名称到值的映射. Robinson不支持名称空间,因此它只将 标记和属性名称 存储为简单字符串.

    struct ElementData {
        tag_name: String,
        attributes: AttrMap,
    }

    type AttrMap = HashMap<String, String>;

最后,一些构造函数可以轻松创建新节点:

    fn text(data: String) -> Node {
        Node { children: Vec::new(), node_type: NodeType::Text(data) }
    }

    fn elem(name: String, attrs: AttrMap, children: Vec<Node>) -> Node {
        Node {
            children: children,
            node_type: NodeType::Element(ElementData {
                tag_name: name,
                attributes: attrs,
            })
        }
    }

就是这样!一个完整的DOM实现将包括更多的数据和许多方法,但这是我们开始需要的全部内容.

练习

这些只是在家中遵循的一些建议方法. 做你感兴趣的练习并跳过任何不想做的.

  1. 以您选择的语言启动一个新程序,并编写代码来表示 DOM文本节点和元素的树.

  2. 安装最新版本rust,然后下载并构建罗宾逊. 打开dom.rs并扩展NodeType包括 注释节点等其他类型.

  3. 编写代码以漂亮打印 DOM节点树.

在里面下一篇文章,我们将添加一个解析器,将 HTML源代码 转换为 这些DOM节点的树.

参考

有关浏览器引擎内部的更多详细信息,请参阅 Tali Garsiel 的精彩内容浏览器如何工作及其与更多资源的联系.

例如代码,这里是"小"开源Web渲染引擎的简短列表. 他们中的大多数 比 鲁宾逊大很多倍,但仍然比 Gecko或WebKit 小. WebWhir,2000行代码,是唯一,一个我称之为"玩具"引擎.

您可能会发现这些对于 灵感或参考 有用. 如果你知道任何其他类似的项目 - 或者如果你自己开始 - 请告诉我!