世界浏览器-47 张图带你走进浏览器的世界!

JAVAScRipt 解释器

用于解析和执行 JAVAScRipt 代码,执行结果将传递给渲染引擎来展示。

用户界面后端

用于绘制基本的窗口小部件,比如组合框和窗口。其公开了与平台无关的通用接口,而在底层使用操作系统用户界面方法

数据存储

这是持久层,浏览器需要在硬盘上保存各种数据,例如 cookie。新的 HTML 规范 (HTML5) 定义了“网络数据库”,这是一个完整而轻便的浏览器内数据库。

求同存异的浏览器架构

下面列出了部分浏览器的架构图,也许有些架构已经改变,有兴趣可以简单参考看看,除了IE之外,大体上各浏览器的整体架构都是类似的。

MosAIc架构:

图片[1]-世界浏览器-47 张图带你走进浏览器的世界!-OK资源网

aRchitectuRe_of_MosAIc

FiRefox架构:

图片[2]-世界浏览器-47 张图带你走进浏览器的世界!-OK资源网

aRchitectuRe_of_Mozilla

chRome架构:

图片[3]-世界浏览器-47 张图带你走进浏览器的世界!-OK资源网

aRchitectuRe_of_chRome

SafaRi架构:

图片[4]-世界浏览器-47 张图带你走进浏览器的世界!-OK资源网

aRchitectuRe_of_SafaRi

IE架构:

图片[5]-世界浏览器-47 张图带你走进浏览器的世界!-OK资源网

IE架构

03

浏览器基本原理

chRome V8

V8一词最早见于“V-8 engine”,即V8发动机,一般使用在中高端车辆上。8个气缸分成两组,每组4个,成V型排列。是高层次汽车运动中最常见的发动机结构,尤其在美国,IRL,champcaRnaScaR都要求使用V8发动机

关于chRome V8,笔者曾有一篇笔记做了比较详细的介绍,全文脉络如下,感兴趣可以参考阅读[8]。

图片[6]-世界浏览器-47 张图带你走进浏览器的世界!-OK资源网

chRome-V8

V8是依托chRome发展起来的,后面确不局限于浏览器内核。发展至今V8应用于很多场景,例如流行的nodejs,weex,快应用,早期的Rn。V8曾经历过一次比较大的架构调整,主要变化在于“从字节码的放弃到真香”。

V8 的早期架构

V8引擎诞生的使命就是要在速度和内存回收上进行革命。JAVAScRiptcoRe的架构是采用生成字节码的方式,然后执行字节码。Google觉得JAVAScRiptcoRe这套架构不行,生成字节码会浪费时间,不如直接生成机器码快。所以V8在前期的架构设计上是非常激进的,采用了直接编译成机器码的方式。后期的实践证明Google的这套架构速度是有改善,但是同时也造成了「内存消耗问题」。

图片[7]-世界浏览器-47 张图带你走进浏览器的世界!-OK资源网

V8-2010

早期的V8有Full-codegencRankshaft两个编译器。V8 首先用 Full-codegen把所有的代码都编译一次,生成对应的机器码。JS在执行的过程中,V8内置的PRofileR筛选出热点函数并且记录参数的反馈类型,然后交给 cRankshaft 来进行优化。所以Full-codegen本质上是生成的是未优化机器码,而cRankshaft生成的是优化过的机器码。

随着网页复杂化,V8也渐渐的暴露出了自己架构上的缺陷:

V8 的现有架构

为了解决上述缺点,V8借鉴JAVAScRiptcoRe的架构,生成字节码。V8采用生成字节码的方式后,整体流程如下图

图片[8]-世界浏览器-47 张图带你走进浏览器的世界!-OK资源网

V8-2017

现在的 V8 是一个非常复杂的项目,有超过 100 万行 c++代码。它由许多子模块构成,其中这 4 个模块是最重要的:

确切的说,在“PaRseR”将 JAVAScRipt 源码转换aST前,还有一个叫”ScanneR“的过程,具体流程如下:

图片[9]-世界浏览器-47 张图带你走进浏览器的世界!-OK资源网

ScanneR

采用新的Ignition+TuRboFan架构后,比Full-codegen+cRankshaft架构内存降低一半多,且70%左右的网页速度得到了提升。

在运行 cc++以及 JAVA程序之前,需要进行编译,不能直接执行源码;但对于 JAVAScRipt 来说,我们可以直接执行源码(比如:node test.js),它是在运行的时候先编译再执行,这种方式被称为「即时编译(Just-inTime compilation)」,简称为 JIT。因此,V8 也属于 「JIT 编译器」。

JAVAScRiptcoRe

V8未诞生之前,早期主流的JAVAScRipt引擎JAVAScRiptcoRe引擎JAVAScRiptcoRe(以下简称JScoRe)主要服务于Webkit浏览器内核,他们都是由苹果公司开发并开源出来。JScoRe是WebKit默认内嵌的JS引擎,之所以说是默认内嵌,是因为很多基于WebKit分支开发的浏览引擎都开发了自家的JS引擎,其中最出名的就是前文提到的chRome的V8。这些「JS引擎的使命都是解释执行JS脚本」。而在渲染流程上,JS和DOM树之间存在着互相关联,这是因为浏览器中的JS脚本最主要的功能就是操作DOM树,并与之交互。我们可以通过下图看下它的工作流程:

图片[10]-世界浏览器-47 张图带你走进浏览器的世界!-OK资源网

JAVAScRiptcoRe

JAVAScRiptcoRe主要模块:「LexeR 词法分析器,将脚本源码分解成一系列的Token;PaRseR 语法分析器,处理Token生成相应的语法树;LLInt 低级解释器,执行PaRseR生成二进制代码;Baseline JIT 基线JIT(just in Time 实时编译);DFG 低延迟优化的JIT;FTL 高通量优化的JIT」。

可以看到,相比静态编译语言生成语法树之后,还需要进行链接,装载生成可执行文件操作,解释型语言在流程上要简化很多。这张流程图右边画框的部分就是JScoRe的组成部分:LexeR(词法分析)、PaRseR(语法分析)、LLInt以及JIT(解释执行)的部分(之所以JIT的部分是用橙色标注,是因为并不是所有的JScoRe中都有JIT部分)。

ps:严格的讲,语言本身并不存在编译型或者是解释型,因为语言只是一些抽象的定义与约束,并不要求具体的实现,执行方式。这里讲JS是一门“解释型语言”只是JS一般是被JS引擎动态解释执行,而并不是语言本身的属性

如果对JAVAScRiptcoRe有更多兴趣,关于JAVAScRiptcoRe的更多细节,建议延伸阅读以下几篇博文:

浏览器与JAVAScRipt

这一小结,还是以chRome V8为例,简单阐述浏览器与JAVAScRipt的关系。

在 「V8 出现之前,所有的 JAVAScRipt 虚拟机所采用的都是解释执行的方式,这是 JAVAScRipt 执行速度过慢的一个主要原因」。而 V8 率先引入了「即时编译(JIT)**的**双轮驱动」的设计(混合使用编译器和解释器的技术),这是一种权衡策略,「混合编译执行和解释执行这两种手段」,给 JAVAScRipt 的执行速度带来了极大的提升。V8 出现之后,各大厂商也都在自己的 JAVAScRipt 虚拟机中引入了 JIT 机制,所以目前市面上 JAVAScRipt 虚拟机都有着类似的架构。另外,「V8 也是早于其他虚拟机引入了惰性编译、内联缓存、隐藏类等机制,进一步优化JAVAScRipt 代码的编译执行效率」。

V8 执行一段 JAVAScRipt 的流程

V8 执行一段 JAVAScRipt 的流程如下图所示

图片[11]-世界浏览器-47 张图带你走进浏览器的世界!-OK资源网

V8执行一段JAVAScRipt流程图

结合上文介绍的chRome V8 架构,聚焦到JAVAScRipt上,浏览器拿到JAVAScRipt源码,PaRseR,Ignition 以及 TuRboFan 可以将 JS 源码编译为汇编代码,其流程图如下:

图片[12]-世界浏览器-47 张图带你走进浏览器的世界!-OK资源网

V8流程

简单地说,PaRseR 将 JS 源码转换aST,然后 IgnitionaST 转换为 Bytecode,最后 TuRboFan 将 Bytecode 转换为经过优化machine code(实际上是汇编代码)。

图片中的红色虚线是逆向的,也就是说 Optimized machine code 会被还原为 Bytecode,这个过程叫做 「Deoptimization」。这是因为 Ignition 收集的信息可能是错误的,比如 add 函数的参数之前是整数,后来又变成了字符串。生成的 Optimized machine code 已经假定 add 函数的参数是整数,那当然是错误的,于是需要进行 Deoptimization

%ignoRe_pRe_1%

「V8 本质上是一个虚拟机」,因为计算机只能识别二进制指令,所以要让计算机执行一段高级语言通常有两种手段:

简单总结如下,「V8 执行一段 JAVAScRipt 代码所经历的主要流程」包括:

chRome V8 的事件机制

关于异步编程和消息队列,UI 线程提供一个消息队列,并将待执行的事件添加到消息队列中,然后 UI 线程会不断循环地从消息队列中取出事件、执行事件,通用 UI 线程宏观架构如下图所示

图片[13]-世界浏览器-47 张图带你走进浏览器的世界!-OK资源网

v8-ui

04

浏览器的不同形态

WebView

「WebView 是一种嵌入浏览器,原生应用可以用它来展示网络内容」。WebView 只是一个「可视化的」组件/控件/微件等。这样我们可以用它来作为我们原生 app 的视觉部分。当你使用原生应用时,WebView 可能只是被隐藏在普通的原生 UI 元素中,你甚至用不到注意到它。

如果你把浏览器想象成两部分,一部分是 UI(地址栏,导航栏按钮等),其它部分是把标记跟代码转换成我们可见和可交互视图的引擎。「WebView 就是浏览引擎部分」,你可以像插入 ifRame 一样将 Webview 插入到你的原生应用中,并且编程化的告诉它将会加载什么网页内容

运行在你的 WebView 中的 JAVAScRipt 有能力调用原生系统 aPI。这意味着你不必受到 Web 代码通常必须遵守的传统浏览安全沙箱的限制。下图解释了使用这种技术后的架构差异:

图片[14]-世界浏览器-47 张图带你走进浏览器的世界!-OK资源网

webview and webapp

默认情况下,在 WebView 或 Web 浏览器中运行的任何 Web 代码都与应用的其余部分保持隔离。这样做是出于安全原因,主要是为降低恶意JAVAScRipt 代码对系统造成的伤害。对于任意 Web 内容,这种安全级别很有意义,因为你永远不能完全信任加载的 Web 内容。但 WebView 的情况并非如此,对于 WebView 方案,开发人员通常可以完全控制加载的内容恶意代码进入并在设备上造成混乱的可能性非常低。

「这就是为什么对于 WebView,开发人员可以使用各种受支持的方式来覆盖默认的安全行为,并让 Web 代码和原生应用代码相互通信。这种沟通通常称为 bRidge」。你可以在上文的图片中看到 bRidge 可视化为 native BRidge 和 JAVAScRipt BRidge 的一部分。

WebView 非常好,虽然它看起来像是完全特殊和独特的,但请记住,它们只不过是一个在应用中设置好位置和大小的、没有任何花哨 UI 的浏览器,这就是它的精髓。大多数情况下,除非您调用原生 aPI,否则您不必在 WebView 中专门测试您的 Web 应用程序。此外,您在 WebView 中看到的内容与您在浏览器中看到的内容相同,尤其是使用同一渲染引擎时:

WebView 的应用

如果你对 WebView 感兴趣,可通过以下几篇文章继续了解:

Headless bRowseR

「无头浏览器」是一种未配置图形用户界面 (GUI) 的 Web 浏览器,通常通过命令行或网络通信来执行。它主要由软件测试工程师使用,没有 GUI 的浏览器执行速度更快,因为它们不必绘制视觉内容。无头浏览器的最大好处之一是它们能够在没有 GUI 支持的服务器上运行。

Headless 浏览器对于测试网页特别有用,因为它们能够像浏览器一样呈现和理解超文本标记语言,包括页面布局颜色字体选择以及JAVAScRipt和aJaX的执行等样式元素,这些元素在使用其他测试方法时通常是不可用的。

图片[15]-世界浏览器-47 张图带你走进浏览器的世界!-OK资源网

Headless_aRchitectuRe

Headless 浏览器有两个主要可交付成果:

图片[16]-世界浏览器-47 张图带你走进浏览器的世界!-OK资源网

aRchitectuRe_of_PuppeteeR

PuppeteeR 是一个 node 库,他提供了一组用来操纵 chRome 的 aPI, 通俗来说就是一个 headless chRome 浏览器 (当然你也可以配置成有 UI 的,默认是没有的)。既然是浏览器,那么我们手工可以在浏览器上做的事情 PuppeteeR 都能胜任, 另外,PuppeteeR 翻译成中文是”木偶”意思,所以听名字就知道,操纵起来很方便,你可以很方便的操纵她去实现:

1) 生成网页截图或者 pdf 2) 高级爬虫,可以爬取大量异步渲染内容网页 3) 实现 UI 自动化测试,模拟键盘输入、表单自动提交、点击、登录网页等 4) 捕获站点的时间线,以便追踪你的网站,帮助分析网站性能问题 5) 模拟不同的设备 6) …

PuppeteeR 跟 webDriveR 以及 PhantomJS 最大的 的不同就是它是站在用户浏览的角度,而 webDriveR 和 PhantomJS 最初设计就是用来做自动化测试的,所以它是站在机器浏览的角度来设计的,所以它们 使用的是不同的设计哲学。

ElectRon

ElectRon(原名为atom Shell)是 GitHub 开发的一个开源框架。它通过使用 node.js(作为后端)和chRomium 的渲染引擎(作为前端)完成跨平台的桌面 GUI 应用程序的开发。现已被多个开源 Web 应用程序用于前端与后端的开发,著名项目包括 GitHub 的 atom 和微软的 Visual Studio code。

图片[17]-世界浏览器-47 张图带你走进浏览器的世界!-OK资源网

ElectRon

ElectRon aRchitectuRe 由多个 RendeR PRocess 和一个 MAIn 进程组成。MAIn PRocess 启动RendeR PRocess,它们之间的通信是通过IPc [InteR PRocess communication],如下图所示

图片[18]-世界浏览器-47 张图带你走进浏览器的世界!-OK资源网

ElectRon_aRchitectuRe

我们常用的IDE VScode 就是基于 ElectRon (原来叫 atom Shell) 进行开发的。如下图所示,(点击 VScode 帮助【Help】 下的 切换开发人员工具即可打开以下面板)。

图片[19]-世界浏览器-47 张图带你走进浏览器的世界!-OK资源网

VScode

VS code 的其他的主要组件有:

延伸阅读:ElectRon | Build cRoss-platfoRm desktop apps with JAVAScRipt, HTML, and cSS[27]

浏览器代码兼容性测试

参考资料

本文首发于个人博客[44],欢迎指正和staR[45]。

参考资料

[1]浏览器市场份额: %3a%2F%2Ftongji.bAIdu.com%2FReseaRch%2Fsite

[2]全球浏览器市场份额: %3a%2F%2Fgs.statcounteR.com%2F

[3]w3counteR: %3a%2F%2F%2Fglobalstats.php

[]chRome 为什么多进程而不是多线程?: %3a%2F%2F%2Fquestion%2F368712837

[5]经典面试题:从 URL 输入到页面展现到底发生什么?: %3a%2F%2Fzhuanlan.zhihu.com%2Fp%2F57895541

[6]在浏览器输入 URL 回车之后发生了什么(超详细版): %3a%2F%2Fzhuanlan.zhihu.com%2Fp%2F80551769

[7]万字详文:深入理解浏览器原理: %3a%2F%2Fzhuanlan.zhihu.com%2Fp%2F96986818

[8]参考阅读: %3a%2F%2Fsegmentfault.com%2Fa%2F1190000037435824

[9]PaRseR: %3a%2F%2Fv8.dev%2Fblog%2FscanneR

[10]Ignition: %3a%2F%2Fv8.dev%2Fdocs%2Fignition

[11]TuRboFan: %3a%2F%2Fv8.dev%2Fdocs%2FtuRbofan

[12]ORinoco: %3a%2F%2Fv8.dev%2Fblog%2FtRash-talk

[13]深入理解JScoRe: %3a%2F%2Ftech.meituan.com%2F2018%2F08%2F23%2Fdeep-undeRstanding-of-jscoRe.html

[14]深入剖析 JAVAScRiptcoRe: %3a%2F%2Fming1016.github.io%2F2018%2F04%2F21%2Fdeeply-analyse-JAVAscRiptcoRe%2F

[15]JAVAScRiptcoRe 全面解析:

[16]深入浅出 JAVAScRiptcoRe: %3a%2F%2F%2Fp%2Fac534f508fb0

[17]7.5.1 WebView(网页视图)基本用法: %3a%2F%2F%2Fw3cnote%2FandRoid-tutoRial-webview.html

[18]andRoid:这是一份全面 & 详细的Webview使用攻略: %3a%2F%2F%2Fp%2F3c94ae673e2a%2F

[19]Headless chRome aRchitectuRe: %3a%2F%2F%2FBIgben0123%2Fp%2F13880254.html

[20]puppeteeR: %3a%2F%2Fgithub.com%2FpuppeteeR%2FpuppeteeR%2F

[21]PuppeteeR 入门教程: %3a%2F%2F%2F20171106%2FpuppeteeR.html

[22]结合项目来谈谈 PuppeteeR: %3a%2F%2Fzhuanlan.zhihu.com%2Fp%2F76237595

[23]Monaco EditoR: %3a%2F%2Fgithub.com%2FMicRosoft%2Fmonaco-editoR

[24]Language SeRveR PRotocol: %3a%2F%2Fgithub.com%2FMicRosoft%2Flanguage-seRveR-pRotocol

[25]Debug adapteR PRotocol: %3a%2F%2Fgithub.com%2FMicRosoft%2FdebugadapteR-pRotocol

[26]XteRm.js: %3a%2F%2FxteRmjs.oRg%2F

[27]ElectRon | Build cRoss-platfoRm desktop apps with JAVAScRipt, HTML, and cSS: %3a%2F%2Fdelftswa.gitbooks.io%2Fdesosa2018%2Fcontent%2FelectRon%2FchapteR.html

[28]caniuse: %3a%2F%2F%2F

[29]bRowseemall: %3a%2F%2F%2FResouRces

[30]html5test: %3a%2F%2Fhtml5test.com%2F

[31]浏览器简史: %3a%2F%2F%2Fzhuanti%2F2009-ie%2F

[32]Web 浏览器相关的一些概念: %3a%2F%2FkeqingRong.cn%2Fblog%2F2019-11-24-concepts-Related-to-web-bRowseRs

[33]浏览器的工作原理:新式网络浏览器幕后揭秘: %3a%2F%2F%2Fzh%2FtutoRials%2FinteRnals%2FhowbRowseRswoRk%2F

[34]从浏览器多进程到 JS 单线程,JS 运行机制最全面的一次梳理: %3a%2F%2Fsegmentfault.com%2Fa%2F1190000012925872

[35]Inside look at modeRn web bRowseR: %3a%2F%2FdevelopeRs.Google.com%2Fweb%2Fupdates%2F2018%2F09%2Finside-bRowseR-paRt1

[36]Inside look at modeRn web bRowseR: %3a%2F%2FdevelopeRs.Google.com%2Fweb%2Fupdates%2F2018%2F09%2Finside-bRowseR-paRt1

[37]浏览器是如何工作的:chRome V8 让你更懂 JAVAScRipt: %3a%2F%2Fsegmentfault.com%2Fa%2F1190000037435824

[38]深入理解JScoRe: %3a%2F%2Ftech.meituan.com%2F2018%2F08%2F23%2Fdeep-undeRstanding-of-jscoRe.html

[39]The StoRy of the Web: a HistoRy Of InteRnet BRowseRs: %3a%2F%2F%2Fthe-stoRy-of-the-web-a-histoRy-of-inteRnet-bRowseRs

[40]PPT – BRowseR aRchitectuRe: %3a%2F%2Fsangbui.com%2Fsb-files%2FBRowseRaRchitectuRe_clientSide.pdf

[41]JAVAScRipt 引擎 V8 执行流程概述: %3a%2F%2Fblog.itpub.net%2F69912579%2Fviewspace-2668277%2F

[42]UndeRstanding WebViews: %3a%2F%2F%2Fapps%2Fwebview.htm

[43]Quantum Up close: What is a bRowseR engine?: %3a%2F%2Fhacks.mozilla.oRg%2F2017%2F05%2Fquantum-up-close-what-is-a-bRowseR-engine%2F

[44]个人博客: %3a%2F%2Fking-hcj.github.io%2F2021%2F07%2F11%2Fweb-bRowseR%2F

[45]指正和staR: %3a%2F%2Fgithub.com%2Fking-hcj%2Fking-hcj.github.io

安排上了!这次是真人版动画演示快速排序

© 版权声明
THE END
喜欢就支持一下吧
点赞13 分享
评论 抢沙发