前端工作零零散散已经有了一年左右的时间了,上周参加一个前端开发者分享会,才发现其实大牛们不光光是在堆叠工作时长,所掌握的知识是真真切切的与工作时长成正比的,从今天开始,要开始结构化地梳理一下前端的专业知识了,不能是修修补补地完善知识,今天的话,先摘一篇前端必须要学的,浏览器工作原理吧。(纯手动翻译,若与原文有所冲突,自行选择高明的见解)
英文原文: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的下载管理。
浏览器高级架构
浏览器的主要组件有:
- 用户界面:这里包括了地址栏,前进/后退按钮,书签菜单等等。除了页面场口外的每一部分。
- 浏览器引擎:UI界面与渲染引擎之间的一些要处理的事件。
- 渲染引擎:负责展示请求的内容,例如,请求的内容是HTML,渲染引擎就会解析HTML和CSS,然后将解析好的内容展示在屏幕上。
- 网络部分:处理网络请求,例如HTTP请求,使用不同的实现手段用于不同的平台。
- UI后台:用来回执基础的小组件,例如级联框或是窗口,这个后台的实现接口不是单一平台特有的,而是开放式的系统接口。
- JavaScript解释器:用来解析并执行JavaScript代码。
- 数据存储:这是一块持久的部分。浏览器或许需要在本地存储各种类型的数据,例如cookies。浏览器也支持一些存储形式,例如localStorage,IndexDB,WebSQL和文件系统。
需要说明的是有些浏览器,例如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的空间内完成。
之后,浏览器的工作过程大致上如下:
渲染引擎开始解析HTML文档,并且将elements元素转化为一棵布满DOM节点的“内容树”。然后,引擎将会去解析样式数据,包括外联样式和内敛样式。样式描述和通常的结构属性在HTML里会被使用来创造另一棵树:渲染树
渲染树包括了很多矩形,这些矩形都带有一些常见的属性,例如:颜色和尺寸。这些矩形现在被要求显示在屏幕上。
渲染树完成之后,会经历一个“布局”的过程。这意味着,需要给每个节点一个精确的坐标来让它们展现在屏幕上。下一个阶段就是绘制,渲染树将会被展开,然后每个节点都会通过UI后台来绘制。
明白这样的一个渐进式过程是非常重要的。为了更好的用户体验,渲染引擎将会尽可能快的将内容展现在屏幕上。它不会去等待直到所有的HTML都被解析,然后再去构建、对渲染树做布局。内容的一部分将会被解析出来并且展示,同时,进程一直在持续不断地解析剩下的网络内容。
主要过程的例子
从上面的对比图中你能看到,虽然webkit和gecko叫法不太一样,但是流程基本上是一致的.
Gecko把格式化的元素树叫做”框架树”.每个节点是一个框架.Webkit把它称作”渲染树”,它是由许多”渲染对象”构成的.Webkit把安置元素成为”布局”,Gecko把它叫做”重排”.Webkit把连接各种节点和只做渲染树的信息叫做”依附关系”,一个值得一提的不同之处是,Gecko有额外的一层处于HTML与DOM树之间,它被叫做”内容墨水”,它是一个制造DOM元素的工厂,我们稍后会来谈一谈这部分的流程.
解析
解析在渲染引擎内部是一个非常重要的过程,我们将要稍微深入地探讨一下,我们先简单介绍一下有关解析.
解析一份文档意味着把它翻译成我们能够使用的代码结构,解析的结果往往是一棵代表着文档结构的节点树,它一般被叫做解析树或是语法树.
例如,解析表达式2+3-1会返回这样一棵树:
解析概述
语法
解析是按照文档的语法规则:写入的语言或者是格式来决定.每一句你要解析的格式都必须按照既定的规则,叫做上下文语法关联.人类的语言不是这其中的一种,因此不能被解析为谈话式的语法.
解析-词法分析的组合
解析可以被分成两个过程:词法分析和语法分析.
词法分析是一个将输入的内容转为特定的符号的过程.这些特定的符号是语言的词汇表:一些构建的集合.在人类于艳丽,可以看做是字典里的所有单词组合起来的一种集合.
语法分析是将这些语言的规则应用起来.
解析通常是将这两个工作分开:==词法==分析主要是将输入内容拆分成标识符,==语法==解析是作为构建语法树.词法分析能够将空格或是断行区分出来
解析过程是迭代进行的.解析器往往会去向词法请求一个新的token,然后用现有的语法规则去匹配这个token.如果有一个规则被匹配到了,解析树上会增加一个节点,然后再去请求下一个token.
如果没有规则被匹配到,解析器会将token暂时存下来,然后会继续请求token知道有一条规则能够匹配存储下来的所有token,如果还是没有匹配的规则,那么解析器就会收集一个异常,这意味着文档不是有效运行的,有语法错误.
翻译
在许多情况下,解析树并不是最终的产品。解析只是做了翻译的一步:将输入文档转换为另一种格式。一个例子就是编译。编译器先将源码解析成一棵解析树,然后将这棵树翻译成机器语言。
解析的例子
在上面的图片中,我们构建了一棵简单的数学表达式解析树。让我们尝试着定义一个简单的计算语言,然后来看一下整个解析过程。
词汇:我们的语言包括证书,加法运算符和减法运算符
语法:
- 语法规则包含表达式,关键字,和操作符
- 语言能包括任何数字和表达式
- 一个表达式被定义为一个数据项与操作符,然后再接着一个变量
- 一个操作符是一个加法符或是一个减法符号
- 一个数据项可以使一个数字或是另一个表达式
让我们来分析输入==2+3-1==
第一个匹配的字符是==2==:根据第五条规则,这是一个项,第二个匹配到的是==2+3==:这符合第三条规则:一个数据项接着一个操作符再接着一个数据项。再下一条匹配到的是==2+3-1==是一个表达式,因为我们已经知道==2+3==是一个数据项,因此我们有一个数据项跟着一个操作符再跟着一个数据项。==2++==不会被匹配到任何规则,因此它不是一个有效输入。
通常的词法和语法定义
通常的词法可以在这里查找到regular expressions
举个栗子:
1 | INTEGER: 0|[1-9][0-9]* |
正如你所见到的,数字是使用一个表达式定义的
语法通常是用一种叫做BNF的格式定义。我们的语言可以被定义为:
1 | expression := term operation term |
一门语言能被解析为一个解析表达式,如果它的语法是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格式的语法规则库。