前端工作零零散散已经有了一年左右的时间了,上周参加一个前端开发者分享会,才发现其实大牛们不光光是在堆叠工作时长,所掌握的知识是真真切切的与工作时长成正比的,从今天开始,要开始结构化地梳理一下前端的专业知识了,不能是修修补补地完善知识,今天的话,先摘一篇前端必须要学的,浏览器工作原理吧。(纯手动翻译,若与原文有所冲突,自行选择高明的见解)

英文原文:How Browsers Work: Behind the Scenes of Modern Web Browsers

介绍

浏览器可以被认为是使用最广泛的软件,本文将介绍浏览器的工作原理,我们将看到,从你在地址栏输入google.com到你看到google主页过程中都发生了什么。

将讨论的浏览器

今天,有五种主流浏览器——IE、Firefox、Safari、Chrome及Opera。
移动端,主要的浏览器有安卓浏览器,iPhone,Opera Mini和Opera Mobile,UC浏览器,诺基亚S40/S60和谷歌Chrome浏览器。除了Opera意外,都是基于Webkit的。我给出的例子将是基于开源的浏览器,例如火狐和谷歌,和Safari(部分开源),根据 StatCounter statistics 统计,chrome,firefox和safari大约占领了71%的桌面端浏览器使用量。在移动端,安卓浏览器,iPhone和chrome大约占据了54%的使用量

浏览器的主要功能

浏览器的主要作用是展现你选择的web资源,通过向服务器请求并且展现在浏览器窗口上。这些资源往往是HTML文档,也有可能是PDF文件,图像,或者其他类型的内容。这些资源是以URI(Uniform Resource Identifier)统一资源标识符来特别指向的。

浏览器来展示这些HTML文件的方式是用HTML和CSS样式的规则,这些规则主要是由==W3C==(World Wide Web Consortium)组织来制定的,这是一个制定web标准的组织。过去的数年来,浏览器都倾向于只遵守少量的规则并且发展出了各自的扩展定义,这样会对web开发人员造成严重的兼容性难题。今天,大多数的浏览器都基本上贴近于规则。

浏览器界面大多都是大同小异,这些共同之处有:

  • 提供一个可以输入地址的横条
  • 返回和前进的按钮
  • 书签选项
  • 刷新和停止按钮以便于刷新或者是停止加载当前页面
  • 主页按钮可以让你去主页

奇怪的是,浏览器的用户界面并没有以一种特定的规则制定,它是来自于这么多年来浏览器所做的比较好的经验和相互间的模仿。HTML5的标准并没有规定浏览器的UI必须是怎么样的,但是还是列举了一些常用的元素。这里面的就是地址栏,状态栏和工具栏。当然了,还有一些个性化的部分,例如Firefox的下载管理。

浏览器高级架构

浏览器的主要组件有:

  1. 用户界面:这里包括了地址栏,前进/后退按钮,书签菜单等等。除了页面场口外的每一部分。
  2. 浏览器引擎:UI界面与渲染引擎之间的一些要处理的事件。
  3. 渲染引擎:负责展示请求的内容,例如,请求的内容是HTML,渲染引擎就会解析HTML和CSS,然后将解析好的内容展示在屏幕上。
  4. 网络部分:处理网络请求,例如HTTP请求,使用不同的实现手段用于不同的平台。
  5. UI后台:用来回执基础的小组件,例如级联框或是窗口,这个后台的实现接口不是单一平台特有的,而是开放式的系统接口。
  6. JavaScript解释器:用来解析并执行JavaScript代码。
  7. 数据存储:这是一块持久的部分。浏览器或许需要在本地存储各种类型的数据,例如cookies。浏览器也支持一些存储形式,例如localStorage,IndexDB,WebSQL和文件系统。
  8. enter description here

需要说明的是有些浏览器,例如chrome运行多个渲染引擎:每个Tab标签页一个,每个标签页运行一个单独的进程。

渲染引擎

渲染引擎的职责是……额,当然是渲染了,那就是将请求得到内容展示在浏览器上。
浏览器引擎能够默认的展现HTML与XML文档和图像,同样的,它还能够展现起亚类型的插件或是扩展,例如,它能够利用PDF视图插件来展示PDF文件,这一章节里,我们将关注于主要的部分:展现由CSS作为样式的HTML和图像。

渲染引擎们

不同的浏览器使用不同的渲染引擎:Internet Explorer使用的是Trident,Firefox使用的是Gecko,Safari使用的是Webkit,Chrome和Opera(从版本15开始)使用的是Blink,一种Webkit的分支。
Webkit是一款开源的渲染引擎,它最初是用于Linux上的一种引擎,然后被Apple公司修改成能够适用于Mac和Windows,看看更多的关于webkit

主要工作过程

渲染引擎会开始接收从网络服务器上获取的内容,这通常会在8KB的空间内完成。
之后,浏览器的工作过程大致上如下:

enter description here

渲染引擎开始解析HTML文档,并且将elements元素转化为一棵布满DOM节点的“内容树”。然后,引擎将会去解析样式数据,包括外联样式和内敛样式。样式描述和通常的结构属性在HTML里会被使用来创造另一棵树:渲染树
渲染树包括了很多矩形,这些矩形都带有一些常见的属性,例如:颜色和尺寸。这些矩形现在被要求显示在屏幕上。
渲染树完成之后,会经历一个“布局”的过程。这意味着,需要给每个节点一个精确的坐标来让它们展现在屏幕上。下一个阶段就是绘制,渲染树将会被展开,然后每个节点都会通过UI后台来绘制。
明白这样的一个渐进式过程是非常重要的。为了更好的用户体验,渲染引擎将会尽可能快的将内容展现在屏幕上。它不会去等待直到所有的HTML都被解析,然后再去构建、对渲染树做布局。内容的一部分将会被解析出来并且展示,同时,进程一直在持续不断地解析剩下的网络内容。

主要过程的例子

enter description here

enter description here

从上面的对比图中你能看到,虽然webkit和gecko叫法不太一样,但是流程基本上是一致的.
Gecko把格式化的元素树叫做”框架树”.每个节点是一个框架.Webkit把它称作”渲染树”,它是由许多”渲染对象”构成的.Webkit把安置元素成为”布局”,Gecko把它叫做”重排”.Webkit把连接各种节点和只做渲染树的信息叫做”依附关系”,一个值得一提的不同之处是,Gecko有额外的一层处于HTML与DOM树之间,它被叫做”内容墨水”,它是一个制造DOM元素的工厂,我们稍后会来谈一谈这部分的流程.

解析

解析在渲染引擎内部是一个非常重要的过程,我们将要稍微深入地探讨一下,我们先简单介绍一下有关解析.
解析一份文档意味着把它翻译成我们能够使用的代码结构,解析的结果往往是一棵代表着文档结构的节点树,它一般被叫做解析树或是语法树.
例如,解析表达式2+3-1会返回这样一棵树:

enter description here

解析概述

语法

解析是按照文档的语法规则:写入的语言或者是格式来决定.每一句你要解析的格式都必须按照既定的规则,叫做上下文语法关联.人类的语言不是这其中的一种,因此不能被解析为谈话式的语法.

解析-词法分析的组合

解析可以被分成两个过程:词法分析和语法分析.
词法分析是一个将输入的内容转为特定的符号的过程.这些特定的符号是语言的词汇表:一些构建的集合.在人类于艳丽,可以看做是字典里的所有单词组合起来的一种集合.
语法分析是将这些语言的规则应用起来.
解析通常是将这两个工作分开:==词法==分析主要是将输入内容拆分成标识符,==语法==解析是作为构建语法树.词法分析能够将空格或是断行区分出来

enter description here
解析过程是迭代进行的.解析器往往会去向词法请求一个新的token,然后用现有的语法规则去匹配这个token.如果有一个规则被匹配到了,解析树上会增加一个节点,然后再去请求下一个token.
如果没有规则被匹配到,解析器会将token暂时存下来,然后会继续请求token知道有一条规则能够匹配存储下来的所有token,如果还是没有匹配的规则,那么解析器就会收集一个异常,这意味着文档不是有效运行的,有语法错误.

翻译

在许多情况下,解析树并不是最终的产品。解析只是做了翻译的一步:将输入文档转换为另一种格式。一个例子就是编译。编译器先将源码解析成一棵解析树,然后将这棵树翻译成机器语言。

enter description here

解析的例子

在上面的图片中,我们构建了一棵简单的数学表达式解析树。让我们尝试着定义一个简单的计算语言,然后来看一下整个解析过程。
词汇:我们的语言包括证书,加法运算符和减法运算符
语法:

  1. 语法规则包含表达式,关键字,和操作符
  2. 语言能包括任何数字和表达式
  3. 一个表达式被定义为一个数据项与操作符,然后再接着一个变量
  4. 一个操作符是一个加法符或是一个减法符号
  5. 一个数据项可以使一个数字或是另一个表达式

让我们来分析输入==2+3-1==
第一个匹配的字符是==2==:根据第五条规则,这是一个项,第二个匹配到的是==2+3==:这符合第三条规则:一个数据项接着一个操作符再接着一个数据项。再下一条匹配到的是==2+3-1==是一个表达式,因为我们已经知道==2+3==是一个数据项,因此我们有一个数据项跟着一个操作符再跟着一个数据项。==2++==不会被匹配到任何规则,因此它不是一个有效输入。

通常的词法和语法定义

通常的词法可以在这里查找到regular expressions
举个栗子:

1
2
3
INTEGER: 0|[1-9][0-9]*
PLUS: +
MINUS: -

正如你所见到的,数字是使用一个表达式定义的
语法通常是用一种叫做BNF的格式定义。我们的语言可以被定义为:

1
2
3
expression :=  term  operation  term
operation := PLUS | MINUS
term := INTEGER | expression

一门语言能被解析为一个解析表达式,如果它的语法是context free grammar(个人理解为上下文自由语法)。要想更加深入了解这方面可以查看 Wikipedia’s article on Context-free grammar

解析器的类型

有两种解析器:从上至下解析和从下至上解析。从上至下解析器就是从高级层阶上查找对应的语法规则。从下至上解析就是先将输入转化为词法规则,然后从低级规则往高级规则逐步查对应规则
让我们看看两种解析规则如何来解析我们的例子。
自顶向下的解析器将从更高级别的规则开始:它将识别2 + 3作为表达式。 然后识别2 + 3 - 1作为一个表达式(识别表达式的过程发展,匹配其他规则,但起点是最高级别的规则)。
自下而上的解析器将扫描输入,直到匹配规则。 然后它将用规则替换匹配的输入。 这将继续直到输入结束。 部分匹配的表达式放在解析器的堆栈上。
这种自下而上的解析器称为shift-reduce解析器,因为输入被移到右边(设想一个指针首先指向输入开始并向右移动)并逐渐减少到语法规则

自动生成解析器

有许多工具能够生成一个解析器。你只需要将你的语法规则填充进去-词法和语法规则,然后他们就会生成一个解析器。创造一个解析器需要有对解析过程有深层的理解,手动来创造一个解析器并不是一件容易的事,因此解析生成工具是非常有用的。
Webkit使用两个非常有名的解析生成器:Flex来创造词法分析器,Bison来创造一个解析器(你也许听说并使用过叫做Lex和Yacc),Flex输入是一个文件包含了许多常规的表达式。Bison的输入是一个BNF格式的语法规则库。

HTML解析器

HTML语法定义

不是一个上下文无关语法

HTML DTD

DOM

解析算法

标记化算法

构建树算法

解析完成的动作

浏览器容错

CSS解析

Webkit的CSS解析器

处理脚本和样式表的吮吸

脚本

推测解析

样式表

渲染树的构建

渲染树和DOM树的关系

构建树的过程

样式计算

共享样式数据

Firefox规则树

分割结构
使用规则树来计算样式

使用简单的规则来操作它的规则

以正确的级联顺序应用规则

样式表级联顺序
特别之处
规则分类

渐进式过程

布局

脏位系统

全局和增量布局

异步和同步布局

优化

布局进程

断行

绘制

全局和增量

绘制顺序

Firefox表现列表

WebKit矩形存储

动态变化

绘制引擎进程

事件循环

css2视觉模型

canvas

CSS盒子模型

定位方案

盒子类型

定位

相对定位

浮动

绝对定位和固定定位

分层表示

资源