浏览器渲染引擎

背景

浏览器的内核中主要分为渲染引擎和 javascript 引擎,本篇主要围绕渲染引擎介绍一下浏览器的工做原理。javascript

首先,咱们先看几个 user-agent 的字符串:html

  • Mozilla/ 1.0 (Windows NT 6.1;rv:2.0.1) Gecko/2010010Firefox/4.0.1
  • Mozilla/ 4. 0 (compatible; MSIE 7. 0; Windows NT 6. 0)
  • Mozilla/ 5. 0 (Linux; Android4. 0. 4; Galaxy Nexus Build/ IMM76B) AppleWebKit/ 535. 19 (KHTML, like Gecko) Chrome/ 18. 0. 1025. 133 Mobile Safari/ 535.

这是3个不一样浏览器的 user-agent,第1个是Firefox的,第2个是IE7的,第3个是Chrome的。有没有以为很奇怪,为何全部字符串的前面都会有一个 Mozilla 开头呢? 并且在 Chrome 中包含了不少其余浏览器的标识,Android,Gecko,Safari … , 这些浏览器厂商为何要把这个user-agent字符串设计成这样?user-agent 按照正常的理解就是浏览器的标识,包含操做系统和浏览器信息带上版本号就好了,浏览器厂商为何搞的这么复杂呢?前端

这须要了解一下 user-agent 字符串历史 ,大体的意思是, 早期的浏览器 Netscape 的 user-agent 是以 Mozilla/Version [Language] (Platform; Encryption) 的格式,大多数服务器在加载页面前都会检查 user-agent 是否为该款浏览器,然而在 1995年, IE 发布首款浏览器,若是不兼容Netscape user-agent 字串,用户就根本访问不了页面,因而IE就设计了这种格式: Mozilla/2.0 (compatible; MSIE Version; Operating System) 。 其余新的浏览器发布也是同样的,为了溶入主流而不被踢出局,在 user-agent 字串中放详尽的信息,以便骗取网站的信任使它与其它流行的浏览器兼容。java

接下来,简单回顾一下浏览器的历史:web

  • WorldWideWeb 1991 年
  • Mosaic 1993年
  • Netscape 1994年
  • Opera 1995年
  • IE 1995年 一战
  • Safari 2003年
  • Firefox 2004年 二战
  • Chrome 2008年
  • Edge 2015年

不少人都觉得最先的浏览器是 Netscape, 其实在 Netscape 以前还有 WorldWideWeb 和 Mosaic 两个浏览器,WorldWideWeb 是世界上第一个浏览器,它同时也是一个编辑器,在 1991年发布, 当时 http 的版本仍是 0.9 ,只支持 get 请求。编程

随后在 1993年 由美国伊利诺州的伊利诺大学的 NCSA 组织,发布第一个能够显示图片的浏览器,叫 Mosaic。随后 NCSA 将 Mosaic 的商业运营权转售给了 Spyglass 公司,该公司又向包括微软公司在内的多家公司技术受权,而后,微软的IE浏览器就是从这里开始了。canvas

1994年,在 Mosaic 浏览器开发团队的核心成员,从新成立 Netscape 公司,今后 Netscape 浏览器诞生。浏览器

1995年,挪威的本土电信公司 Telenor 开发了一个新的浏览器 Opera,Opera 浏览器他特色就是速度快,可是兼容性不是很好,在浏览器上首创了不少功能,好比标签式浏览就是 Opera 发明的,Opera 在经历不少坎坷后,在 2016 年被奇虎 360 收购。安全

1995同年,微软在取得 Spyglass Mosaic 的源代码和受权后,发布了首款浏览器 Internet Explorer ,今后展开浏览器第一次大战。服务器

1998年,Netscape 公司内部成立Mozilla组织。

2003年,苹果公司发布了本身第一个浏览器 Safari,同时在两年后,也就是 2005 年开源了本身的浏览器内核 Webkit。

2004年,Mozilla 组织发布了 Firefox 浏览器,而后第二次浏览器大战开始。

2008年,是一个灾难不少的一年,四川大地震,过年回家又赶上雪灾,做为前端开发个人将要开始兼容一个由 Goolge 开发新的 Chrome 浏览器。 可是 Chrome 带来的体验是让人开心的 。

2015年,微软随着 Windows 10 一块儿发布 Edge 浏览器,伤透你们心的 IE ,估计维护不下去。

当前市场浏览器市场占比最高的是 Chrome 浏览器,其次就是 IE,Firefox ,详细占比查看 Net Marketshare 的统计。

浏览器内核

内核 浏览器 出生年份 JS 引擎 开源
Trident IE4 - IE11 1997 JScript,chakra(ie9+)
Gecko Firefox 2004 SpiderMonkey MPL
WebKit Safari,Chromium,Chrome(-2013) ,Android浏览器,ChromeOS,WebOS 等 2005 JavascriptCore BSD
Blink Chrome, Opera 2013 V8 GPL
Edge Edge 2015 Chakra MIT(chakra)

市面上的浏览器都有本身的内核,有些内核之间还存一些关系。 WebKit 是 Apple 公司在 2005年开源的一个内核,Apple 公司早期使用的引擎是 KHTML,KHTML 是 KDE 社区维护,Apple 技术团队也参与 KHTML 的开发,可是在开发过程当中,KDE 社区不太喜欢 Apple 团队人员提交的代码。因此 Apple 公司的人就本身独立出来,在 KHTML 基础之上建立了 WebKit 内核,并在 2005 年开源。 在 2008年,Google 发布 Chrome 浏览器,采用的也是 Webkit 内核,同时技术团队人员也参与 WebKit 项目的开发,可是在设计上与 Apple 团队存在分歧,因此 Google 的人就独立出来,基于 WebKit 开发了 Blink 内核,Blink 在 Webkit 的基础上加入多进程,沙箱等不少技术。

回过头看看国内,有不少浏览器,很牛逼,都是多核的,想要兼容国内银行系统就切换到 Trident 内核,想要访问速度就切换到 Webkit 内核,Blink 发布之后,就把 WebKit 换成了 Blink 。

  • QQ浏览器 Trident+Webkit (Blink)
  • 360安全浏览器 Trident+Webkit (Blink)
  • 猎豹浏览器 Trident+Webkit (Blink)
  • 世界之窗 Trident+Webkit (Blink)
  • 搜狗高速浏览器 Trident+Webkit (Blink)
  • UC浏览器 Trident+Webkit (Blink)
  • ....

WebKit 架构

接下来咱们进入到 Webkit 里面,首先看一下 WebKit 的架构图

enter image description here

在上图中实线部分,也就是 WebCore,基本上在各个浏览器中是共享的,虚线部分在各个浏览器中的存在差别。 WebCore 是渲染引擎,包含的 HTML 解释器,CSS 解释器,处理页面布局渲染等功能。 JavascriptCore 就是 WebKit 内置的 Javascript 引擎。 在最上层是 WebKit 嵌入式接口,这些接口提供给浏览器调用,可是咱们能够看到图中有 WebKit 和 WebKit2 两个嵌入式接口,这两有什么区别呢?

WebKit 2 是 2010年4月份发布的,抽象出一组新的编程接口,给开发者用,同时采用了多进程:,一个UI 进程,处理Web平台与浏览器接口的进程,另一个 Web 进程, Web 页面渲染的进程。 让 web 进程与 UI 进程隔离,在健壮性、安全性以及更好地使用多核 cpu 等方面带来了好处。 如下是WebKit 和 WebKit2的对比图:

enter image description here

enter image description here

更多详细信息能够阅读: https://trac.webkit.org/wiki/WebKit2

在 Javascript 旁边有很大一块区域是 Webkit Port,所谓 WebKit Port,并无确切的形式,能够看做是OS,平台(应用程序框架),Javacsript 引擎,以及各类第三方库的一个组合。WebKit Port 提供不一样的 Port 接口供外部程序使用,

以 Webkit 为核心存在不少移植:

  • Apple's Mac Port
  • Apple's Windows Port
  • Cairo-based Windows Port
  • JSCOnly Port
  • WebKitGTK+ Port
  • QTWebKit
  • ...

WebKit Port 一般处于如下几种目的:

  • 使用WebKit做为浏览器(或者相似的User Agent)的页面解析,排版和渲染的核心,如Safari,Chrome
  • 对WebKit进行封装,对外提供构建一个浏览器(或者相似的UA)的API接口,如Qt
  • 以上二者皆有,如Android,iOS,BlackBerry

不一样的 port 关注点不同

  • Mac 的 port 注意力集中在浏览器和操做系统的分割,它经过 Obj-C 和 C++ 代码把(WebKit)渲染引擎嵌入到本地应用中。
  • Chromium port 专一在浏览器。
  • QtWebKit 则把它的 WebKit 实现做为一个运行时的库或者渲染引擎,同其跨平台 GUI 应用程序框架一块儿提供给其它应用使用。 好比 无头浏览器 Phantomjs 就是基于 QtWebKit 实现的。

更多详细信息能够阅读:https://trac.webkit.org/wiki#WebKitPorts

接下来,咱们来看一下基于 Chromium port 的浏览器。

enter image description here

在图中咱们能够看到左下角是 WebKit 内核,和他平级的还有:

  • GPU/Command Buffer , Command Buffer 是 GPU 线程的通讯媒介,提供GPU 硬件加速。
  • V8,Javascript 引擎
  • 沙箱模型,浏览器安全保护的一种设计
  • CC(Chromium 合成器),对 RenderLayer Tree 分红渲染
  • IPC,UI,PPAPI ...

在上面一层是 Content , 若是没有 Content, 也能正常渲染,不过上面提到的这些GPU 加速,沙箱模型,HTML 5等功能将没有,Content API 提供了公开稳定的接口, 目标是支持全部的 HTML5功能和GPU硬件加速等功能。

“Chromium浏览器” 和 “ContentShell” 是构建在 ContentAPI 之上的两个“浏览器”,Chromium具备浏览器完整的功能,也就是咱们编译出来能看到的浏览器式样。而“ContentShell”是使用ContentAPI来包装的一层简单的“壳”,可是它也是一个简单的“浏览器”,用户可使用Content模块来渲染和显示网页内容。 ContentShell的做用很明显,其一能够用来测试Content模块不少功能的正确性,例如渲染、硬件加速等;其二是一个参考,能够被不少外部的项目参考来开发基于“ContentAPI”的浏览器或者各类类型的项目。

在 Android 系统上,ContentShell 的做用更大,这是由于同它并排的左侧的 “Chromium浏览器” 部分的代码根本就没有开源,这直接致使开发者只能依赖ContentShell。

“Android WebView”,它是为了知足Android系统上的 WebView 而设计的,其思想是利用Chromium的实现来替换原来Android系统默认的 WebView。

渲染过程

如下是浏览器渲染引擎及依赖模块

enter image description here

一个渲染引擎主要包括HTML解释器、CSS解释器、布局和JavaScript引擎等,JavaScript引擎如今都已经独立出来。 下面是所依赖的模块,包括网络,存储,2D/3D 图形,音频和视频,图片解码器等等…, 再下面就是操做系统相关的支持。

一个大体的渲染过程及依赖模块关系图以下:

enter image description here

接下来咱们再来看一下,在 WebKit 的渲染的详细过程:

enter image description here

首先是在浏览器输入 URL 之后,依赖网络模块加载各类资源,获得一个HTML , HTML 交给 HTML 解析器进行解析,最后生成 DOM 树,若是再解析过程当中有存在 Javascript 代码就交给 Javascript 引擎处理,处理完成返回给 DOM 树, 这个环节的主要目的就是构建一个DOM 树。

而后看一下 DOM树到绘制上下文

enter image description here

在网络资源中得到 CSS 代码之后,会把 CSS 交给 CSS 解析器处理,同时会计算布局。 DOM 树会构建成一个 RenderObject 树,它和 DOM 树节点是一一对应,而后再和 解析后的CSS 合并分析,生成 RenderLayer 树, 这个树就是最终用于渲染的树,而后绘制上下文。

如下是在网上找到的几张图,简单解释了 DOM 树到 RenderLayer 树最终的过程。

enter image description here

这一段简单的 HTML 代码,其中包含的 body, div canvas script 等元素,经过 HTML,CSS 解析器进行解析,最终会生成一个 RenderLayer 树, 前面在Chromium 架构图的时候有提到 CC 合成器,随着GPU硬件能力的加强,包括在不少小型设备上也是如此,浏览器能够借助于其处理图形方面的性能来对渲染实现加速。此时再也不将全部层绘制到一块儿,而是进行分层渲染,合成以后再显示到屏幕上。

enter image description here

如下 RenderLayer 树的结构,从图中能够看出整个树分了 3 个 Layer,在 Layer 下面包含了 RenderBlock,RenderText 等 Render 节点,每一个节点上都包含的坐标,大小,以及背景颜色等渲染依赖的信息。

enter image description here

RenderBlock 其实就是咱们 HTML 中的块级元素,咱们都知道一个元素是否为块级元素是能够经过 CSS 改变的,因此,一个 RenderLayer 树的结构也会根据CSS的变化变化,若是影响到元素的位置发生变化会都在整个树从新计算,也是就是咱们说的回流。 关于回流的解释能够参考 : https://www-archive.mozilla.org/newlayout/doc/reflow.html 。

最后咱们简单了解一下 Shadow DOM, 在前端开发过程当中你们都知道 React, Vue 这些框架中有组件的概念,一个页面中存在不少重复的组件,在一个组件内部又存在不少基础的 HTML 元素,这些元素能够组成一颗DOM树的子树。这样一个组件能够被处处使用,可是问题随之而来,那就是每一个使用组件的地方都会知道这个子树的结构。当网页的开发者须要访问网页DOM树的时候,这些控件内部的DOM子树都会暴露出来,这些暴露的节点不只可能给DOM树的遍历带来不少麻烦,并且也可能给CSS的样式选择带来问题,由于选择器无心中可能会改变这些内部节点的样式,从而致使很奇怪的界面。

那有什么办法能够把这些内部节点封装起来?就像咱们写 javascript 模块化同样,W3C 工做组提出了 Shadow DOM 的概念,好比在 HTML5 支持的 <Video> 等标签,在其内部有不少复杂的结构, 可是播放,暂停等按钮咱们在 DOM 树中没法直接找到相应的节点,这其实就是 Shadow DOM 的思想。 Shadow DOM 是能够经过 Javascript 自定义建立,在其内部能够维护本身的DOM, CSS ,事件等, 具备很好的密封性。 自定义 Shadow DOM 目前只有 Chrome 支持,不过,我相信在不远的未来 Shadow DOM 会给组件化开发带来更多美好的体验。

参考资料

  • 《WebKit 技术内幕》
  • 浏览器解密篇 http://www.nowamagic.net/academy/part/48/115/#
  • WebKit Wiki https://trac.webkit.org/wiki

原文地址: http://blog.hypers.io/2018/04/04/webkit-render/

相关文章
相关标签/搜索