浏览器的运行原理

一、什么是浏览器

百度百科的解释是:网页浏览器(英语:web browser),常被简称为浏览器,是一种用于检索并展示万维网信息资源的应用程序。这些信息资源可为网页、图片、影音或其他内容,它们由统一资源标志符标志。信息资源中的超链接可使用户方便地浏览相关信息。

网页浏览器虽然主要用于使用万维网,但也可用于获取专用网络中网页服务器之信息或文件系统内之文件。

现在主流的浏览器有:Mozilla FirefoxInternet Explorer(、Microsoft EdgeGoogle ChromeOperaSafari、QQ浏览器、搜狗浏览器、UC浏览器等等。

浏览器的页面主要包括的界面元素有:

  • 用来输入URI的地址栏
  • 前进、后退按钮
  • 书签选项
  • 用于刷新及暂停当前加载文档的刷新、暂停按钮
  • 用于到达主页的主页按钮

二、浏览器的主要组件

  1. 用户界面- 包括地址栏、后退/前进按钮、书签目录等,也就是你所看到的除了用来显示你所请求页面的主窗口之外的其他部分
  2. 浏览器引擎- 用来查询及操作渲染引擎的接口
  3. 渲染引擎- 用来显示请求的内容,例如,如果请求内容为html,它负责解析html及css,并将解析后的结果显示出来
  4. 网络- 用来完成网络调用,例如http请求,它具有平台无关的接口,可以在不同平台上工作
  5. UI 后端- 用来绘制类似组合选择框及对话框等基本组件,具有不特定于某个平台的通用接口,底层使用操作系统的用户接口
  6. JS解释器- 用来解释执行JS代码
  7. 数据存储- 属于持久层,浏览器需要在硬盘中保存类似cookie的各种数据,HTML5定义了web database技术,这是一种轻量级完整的客户端存储技术

                        

                                                                   浏览器的组成部分

三、浏览器工作原理

组件见的通信Communication between the components

渲染引擎 The rendering engine

渲染引擎的职责就是渲染,即在浏览器窗口中显示所请求的内容。 默认情况下,渲染引擎可以显示html、xml文档及图片,它也可以借助插件(一种浏览器扩展)显示其他类型数据,例如使用PDF阅读器插件,可以显示PDF格式,将由专门一章讲解插件及扩展,这里只讨论渲染引擎最主要的用途——显示应用了CSS之后的html及图片。

主流程 The mainflow渲染引擎的职责就是渲染,即在浏览器窗口中显示所请求的内容。 默认情况下,渲染引擎可以显示html、xml文档及图片,它也可以借助插件(一种浏览器扩展)显示其他类型数据,例如使用PDF阅读器插件,可以显示PDF格式,将由专门一章讲解插件及扩展,这里只讨论渲染引擎最主要的用途——显示应用了CSS之后的html及图片。

渲染引擎 Rendering engines

本文所讨论得浏览器——Firefox、Chrome和Safari是基于两种渲染引擎构建的,Firefox使用Geoko——Mozilla自主研发的渲染引擎,Safari和Chrome都使用webkit。 Webkit是一款开源渲染引擎,它本来是为linux平台研发的,后来由Apple移植到Mac及Windows上,相关内容请参考http://webkit.org

主流程 The main flow

渲染引擎首先通过网络获得所请求文档的内容,通常以8K分块的方式完成。 下面是渲染引擎在取得内容之后的基本流程: 解析html以构建dom树->构建render树->布局render树->绘制render树 图2:渲染引擎基本流程 渲染引擎开始解析html,并将标签转化为内容树中的dom节点。接着,它解析外部CSS文件及style标签中的样式信息。这些样式信息以及html中的可见性指令将被用来构建另一棵树——render树。 Render树由一些包含有颜色和大小等属性的矩形组成,它们将被按照正确的顺序显示到屏幕上。 Render树构建好了之后,将会执行布局过程,它将确定每个节点在屏幕上的确切坐标。再下一步就是绘制,即遍历render树,并使用UI后端层绘制每个节点。 值得注意的是,这个过程是逐步完成的,为了更好的用户体验,渲染引擎将会尽可能早的将内容呈现到屏幕上,并不会等到所有的html都解析完成之后再去构建和布局render树。它是解析完一部分内容就显示一部分内容,同时,可能还在通过网络下载其余内容。                                                                        

图3:webkit主流程

                                   

图4:Mozilla的Geoko 渲染引擎主流程 从图3和4中可以看出,尽管webkit和Gecko使用的术语稍有不同,他们的主要流程基本相同。Gecko称可见的格式化元素组成的树为frame树,每个元素都是一个frame,webkit则使用render树这个名词来命名由渲染对象组成的树。Webkit中元素的定位称为布局,而Gecko中称为回流。Webkit称利用dom节点及样式信息去构建render树的过程为attachment,Gecko在html和dom树之间附加了一层,这层称为内容接收器,相当制造dom元素的工厂。下面将讨论流程中的各个阶段。

解析Parsing-ge既然解析是渲染引擎中一个非常重要的过程,我们将稍微深入的研究它。首先简要介绍一下解析。 解析一个文档即将其转换为具有一定意义的结构——编码可以理解和使用的东西。解析的结果通常是表达文档结构的节点树,称为解析树或  语法树。 例如,解析“2+3-1”这个表达式,可能返回这样一棵树。

                                                           

图5:

数学表达式树节点 文法 Grammars 解析基于文档依据的语法规则——文档的语言或格式。每种可被解析的格式必须具有由词汇及语法规则组成的特定的文法,称为上下文无关文法。人类语言不具有这一特性,因此不能被一般的解析技术所解析。 解析器-词法分析器 Parser-Lexer combination 解析可以分为两个子过程——语法分析及词法分析 词法分析就是将输入分解为符号,符号是语言的词汇表——基本有效单元的集合。对于人类语言来说,它相当于我们字典中出现的所有单词。 语法分析指对语言应用语法规则。 解析器一般将工作分配给两个组件——词法分析器(有时也叫分词器)负责将输入分解为合法的符号,解析器则根据语言的语法规则分析文档结构,从而构建解析树,词法分析器知道怎么跳过空白和换行之类的无关字符。

                                                                                  

                                                                                             图6:

从源文档到解析树 解析过程是迭代的,解析器从词法分析器处取道一个新的符号,并试着用这个符号匹配一条语法规则, 如果匹配了一条规则,这个符号对应的节点将被添加到解析树上,然后解析器请求另一个符号。如果没有匹配到规则,解析器将在内部保存该符号,并从词法分析器 取下一个符号,直到所有内部保存的符号能够匹配一项语法规则。如果最终没有找到匹配的规则,解析器将抛出一个异常,这意味着文档无效或是包含语法错误。 转换 Translation 很多时候,解析树并不是最终结果。解析一般在转换中使用——将输入文档转换为另一种格式。编译就是个例子,编译器在将一段源码编译为机器码的时候,先将源码解析为解析树,然后将该树转换为一个机器码文档。 图7:编译流程 解析实例 Parsing example 图5中,我们从一个数学表达式构建了一个解析树,这里定义一个简单的数学语言来看下解析过程。 词汇表:我们的语言包括整数、加号及减号。 语法: 1. 该语言的语法基本单元包括表达式、term及操作符 2. 该语言可以包括多个表达式 3. 一个表达式定义为两个term通过一个操作符连接 4. 操作符可以是加号或减号 5. term可以是一个整数或一个表达式 现在来分析一下“2+3-1”这个输入 第一个匹配规则的子字符串是“2”,根据规则5,它是一个term,第二个匹配的是“2+3”,它符合第2条规则——一个操作符连接两个term,下一次匹配发生在输入的结束处。“2+3-1”是一个表达式,因为我们已经知道“2+3”是一个term,所以我们有了一个term紧跟着一个操作符及另一个term。“2++”将不会匹配任何规则,因此是一个无效输入。 词汇表及语法的定义 词汇表通常利用正则表达式来定义。 例如上面的语言可以定义为: INTEGER:0|[1-9][0-9]* PLUS:+ MINUS:- 正如看到的,这里用正则表达式定义整数。 语法通常用BNF格式定义,我们的语言可以定义为: expression := term operation term operation := PLUS | MINUS term := INTEGER | expression 如果一个语言的文法是上下文无关的,则它可以用正则解析器来解析。对上下文无关文法的一个直观的定义是,该文法可以用BNF来完整的表达。可查看http://en.wikipedia.org/wiki/Context-free_grammar。 解析器类型 Types of parsers 有两种基本的解析器——自顶向下解析及自底向上解析。比较直观的解释是,自顶向下解析,查看语法的最高层结构并试着匹配其中一个;自底向上解析则从输入开始,逐步将其转换为语法规则,从底层规则开始直到匹配高层规则。 来看一下这两种解析器如何解析上面的例子: 自顶向下解析器从最高层规则开始——它先识别出“2+3“,将其视为一个表达式,然后识别出”2+3-1“为一个表达式(识别表达式的过程中匹配了其他规则,但出发点是最高层规则)。 自底向上解析会扫描输入直到匹配了一条规则,然后用该规则取代匹配的输入,直到解析完所有输入。部分匹配的表达式被放置在解析堆栈中。

Stack

Input

  2 + 3 – 1
term + 3 - 1
term operation 3 – 1
expression - 1
expression operation 1
expression  

自底向上解析器称为shift reduce 解析器,因为输入向右移动(想象一个指针首先指向输入开始处,并向右移动),并逐渐简化为语法规则。 自动化解析 Generating parse 解析器生成器这个工具可以自动生成解析器,只需要指定语言的文法——词汇表及语法规则,它就可以生成一个解析器。创建一个解析器需要对解析有深入的理解,而且手动的创建一个由较好性能的解析器并不容易,所以解析生成器很有用。Webkit使用两个知名的解析生成器——用于创建语法分析器的Flex及创建解析器的Bison(你可能接触过Lex和Yacc)。Flex的输入是一个包含了符号定义的正则表达式,Bison的输入是用BNF格式表示的语法规则。rs automaticallyneral

HTML解析器 HTML Parser

HTML 解析器的任务是将 HTML 标记解析成解析树。 HTML 的词汇和语法在 W3C 组织创建的规范中进行了定义,html不能简单的用解析所需的上下文无关文法来定义。

浏览器为html定制了专属的解析器。 Html5规范中描述了这个解析算法,算法包括两个阶段——符号化和构建树,Html解析流程如图1-3所示。

                                                     

图1-3 Html解析流程图

符号化是词法分析的过程,将输入解析为符号,html的符号包括开始标签、 结束标签、 属性名及属性值。符号识别器识别出符号后,将其传递给树构建器,并读取下一个字符,以识别下一个符号,这样直到处理完所有输入。

3.1.3 CSS解析
css属于上下文无关文法,可以用前面所描述的解析器来解析。 Css规范定义了css的词法及语法文法。 每个符号都由正则表达式定义了词法(词汇表),语法用BNF(由 John Backus 和 Peter Naur 首先引入的用来描述计算机语言语法的符号集)进行描述。

Webkit使用Flex和Bison解析生成器从CSS语法文件中自动生成解析器。

3.2 JS引擎

JS引擎是一个专门处理JS脚本的虚拟机,专门设计来解释和执行的 JavaScript 代码。 JS引擎会加载你的源代码,把它分解成字符串,把这些字符串转换成编译器可以理解的字节码,然后执行这些字节码。不同浏览器有不同的JS引擎 ,不同浏览器的js引擎如表1-1所示。

表1-1 不同浏览器的js引擎

css解析 CSS parsing

还记得简介中提到的解析的概念吗,不同于html,css属于上下文无关文法,可以用前面所描述的解析器来解析。Css规范定义了css的词法及语法文法。

  看一些例子:

  每个符号都由正则表达式定义了词法文法(词汇表):

comment///*[^*]*/*+([^/*][^*]*/*+)*// 
num[0-9]+|[0-9]*"."[0-9]+ 
nonascii[/200-/377] 
nmstart[_a-z]|{nonascii}|{escape} 
nmchar[_a-z0-9-]|{nonascii}|{escape} 
name{nmchar}+ 
ident{nmstart}{nmchar}*

  “ident”是识别器的缩写,相当于一个class名,“name”是一个元素id(用“#”引用)。

  语法用BNF进行描述:

ruleset
 : selector [ ',' S* selector ]* 
'{' S* declaration [ ';' S* declaration ]* '}' S*
 ; 
selector : simple_selector [ combinator selector | S+ [ combinator selector ] ] 
; 
simple_selector 
: element_name [ HASH | class | attrib | pseudo ]* 
| [ HASH | class | attrib | pseudo ]+ 
; 
class 
: '.' IDENT
; 
element_name 
: IDENT | '*' 
; 
attrib 
: '[' S* IDENT S* [ [ '=' | INCLUDES | DASHMATCH ] S* 
[ IDENT | STRING ] S* ] ']' 
; 
pseudo 
: ':' [ IDENT | FUNCTION S* [IDENT S*] ')' ] 
;
 说明:一个规则集合有这样的结构 
div.error , a.error { 
color:red; 
font-weight:bold; 
} 
div.error和a.error时选择器,大括号中的内容包含了这条规则集合中的规则,这个结构在下面的定义中正式的定义了: 
ruleset 
: selector [ ',' S* selector ]* 
'{' S* declaration [ ';' S* declaration ]* '}' S* 
;

     这说明,一个规则集合具有一个或是可选个数的多个选择器,这些选择器以逗号和空格(S表示空格)进行分隔。每个规则集合包含大括号及大括号中的一条或多条以分号隔开的声明。声明和选择器在后面进行定义。

猜你喜欢

转载自blog.csdn.net/qq_40978196/article/details/88823666