浏览器的工作原理

浏览器的主要组件

  • The user interface/用户接口: 除了网页显示区域以外的部分,比如地址栏、搜索栏、前进后退、书签菜单等窗口。
  • The brower engine/浏览器引擎: 查询与操作渲染引擎的接口,包含事件驱动引擎,提供浏览器进程及其线程之间的资源共享调度机制。
  • The rendering engine/渲染引擎: 负责显示请求的内容,比如请求到HTML, 它会负责解析 HTML 与 CSS 并将结果显示到窗口中,也是后面几个线程或引擎的父级控制线程。
  • Networking/网络: 用于网络请求, 如HTTP请求,执行POST、GET等操作就是由它来处理的。
  • UI backend/UI后台: 绘制基础元件,如消息窗口(alert默认的样子),下拉选项卡等等。
  • JavaScript interpreter/JavaScript解释器:也就是 JavaScript 引擎,用于解析和执行 JavaScript 代码。
  • Data storage/数据存储:数据持久层,在我们浏览页面时,浏览器需要把一些数据存到硬盘或内存上,如 Cookies,localStorage,sessionStorage,webSql 等。

我们用浏览器看到的每一个页面,背后都是由以上的组件或功能来完成的。浏览器完成打开一个页面的整个过程,通俗地说这是页面“渲染”。这里的“渲染”,其实是一个组合概念,即浏览器的“渲染引擎”并不是单独工作的,必须依赖其他引擎(组件),经过某种协同机制联合起来完成页面的展示及交互。

需要明确的是,JavaScript 是单线程的,但是 JavaScript 的执行环境不是单线程的,JavaScript 的执行环境是指浏览器,Node.js。

Script 标签工作原理

  • 浏览器一边下载HTML网页,一边开始解析
  • 解析过程中,发现<script>标签
  • 暂停解析,网页渲染的控制权转交给JavaScript引擎
  • 如果<script>标签引用了外部脚本,就下载该脚本,否则就直接执行
  • 执行完毕,控制权交还渲染引擎,恢复往下解析HTML网页

如果外部脚本加载时间很长,就会造成网页失去响应,为了避免这种情况,较好的做法是将<script>标签放在页面底部,而不是头部。这样即使遇到脚本失去响应,网页主体的渲染也已经完成了,用户至少可以看到内容,而不是面对一张空白的页面。

js一定要放在最底部吗? link1 link2

浏览器内核(渲染引擎)

所谓的“浏览器内核”无非指的是一个浏览器最核心的部分——“Rendering Engine”,直译这个词汇叫做“渲染引擎”,不过我们也常称其为“排版引擎”、“解释引擎”。这个引擎的作用是帮助浏览器来渲染网页的内容,将页面内容和排版代码转换为用户所见的视图。

有时候我们所说的“浏览器内核”甚至“渲染引擎”,其实除了渲染引擎,也悄悄包含了 JavaScript 引擎,如 WebKit,它由渲染引擎 WebCore 和 JavaScript 引擎 JSCore 组成。

最开始渲染引擎和 JavaScript 引擎并没有区分的很明确,后来 JavaScript 引擎越来越独立,内核就倾向于只指渲染引擎。常见的浏览器内核可以分这四种:Trident、Gecko、Blink、Webkit。

Trident

Trident[‘traɪd(ə)nt](又称为MSHTML、IE内核),是Internet Explorer的排版引擎的名称,它的第一个版本随着 1997 年的 IE4 发布,之后不断的加入新的技术并随着新版本的 IE 发布。

Edge 最显著的特点就是新内核 EdgeHTML,原本 IE 团队是准备让这个新浏览器包含两个内核以方便向后兼容的(另一个是旧的 MSHTML,IE11- 的内核,好像前端们比较喜欢叫它 Trident,不过 M$ 的 session 里他们都管它叫 MSHTML),但是最后由于技术原因决定让新浏览器只保留新的内核,然后另外保留一个有旧内核的 IE11。

Gecko

Gecko[‘gekəʊ][gai kou] 是一套自由及开放源代码、以 C++ 编写的排版引擎,目前为 Mozilla Firefox 网页浏览器以及 Mozilla Thunderbird 电子邮件客户端等 Mozilla 基金会相关产品所使用。Gecko 原本由网景通信公司开发,现在则由 Mozilla 基金会维护。

Blink

Blink是一个由 Google 主导开发的开源浏览器排版引擎,Google 计划将这个渲染引擎作为 Chromium 计划的一部分。这一渲染引擎是开源引擎 WebKit 中 WebCore 组件的一个分支,Chrome 的开发者由于希望在浏览器的开发上拥有更大的自由度,同时避免与上游冲突,更可通过移除 Chrome 没有使用的组件而简化自己的程序库,所以决定开发WebKit的分支版本,并且在Chrome(28及往后版本)、Opera(15及往后版本)等诸多浏览器中使用。

Webkit

WebKit 是一种用来让网页浏览器绘制网页的排版引擎。它被用于 Apple Safari。其分支被用于基于 Chromuim 的网页浏览器,如:Opera 与 Google Chrome。WebKit 的 HTML 及 JavaScript 代码源自 KDE 的 KHTML 及 KJS 库的一个分支。

Webkit 引擎的 JavaScript 引擎 JSCore 是 KJS 的分支。而 Chrome 则搭载了自己的 JavaScript 引擎 V8。

Presto

Presto[‘prɛsto] 是一个由 Opera Software 开发的网页浏览器排版引擎,由Opera 7.0至12.18版本所使用。
Presto 取代了旧版 Opera 4至6版本使用的 Elektra 排版引擎

JavaScript引擎

JavaScript 引擎是一个专门处理 JavaScript 脚本的虚拟机,一般会附带在网页浏览器之中。

JavaScript 引擎会加载你的源代码,把它分解成字符串(又叫做分词),再 把这些字符串转换 成编译器可以理解的字节码,然后执行这些字节码。Google 的 V8 引擎 是用 C++ 编写的,它也能够编译并执行 JavaScript 源代码、处理内存分配和垃圾回收。

Browser, Headless Browser, or Runtime JavaScript Engine
Mozilla Spidermonkey
Chrome V8
Safari JavaScriptCore
IE and Edge Chakra
PhantomJS JavaScriptCore
HTMLUnit Rhino
TrifleJS V8
Node.js V8
Io.js* V8

Event loop

event loop

之所以称为事件循环,是因为它经常被用于类似如下的方式来实现

while(queue.waitForMessage()){
  queue.processNextMessage();
}

一个 JavaScript 运行时包含了一个待处理的消息队列。每一个消息都与一个函数相关联。当栈为空时,从队列中取出一个消息进行处理。这个处理过程包含了调用与这个消息相关联的函数(以及因而创建了一个初始堆栈帧)。当栈再次为空的时候,也就意味着消息处理结束。

setTimeout 函数会在一个时间段过去后在队列中添加一个消息。如果有其它消息,setTimeout 消息必须等待其它消息处理完。因此第二个参数仅仅表示最少的时间 而非确切的时间。

参考资料:

主流浏览器内核介绍
浏览器的工作原理:新式网络浏览器幕后揭秘
浏览器环境概述
如何评价 Microsoft Edge 浏览器?
一篇给小白看的 JavaScript 引擎指南
【朴灵评注】JavaScript 运行机制详解:再谈Event Loop
javascript既然是单线程语言 , 为什么会分主线程和消息线程(event loop) ?
并发模型与Event Loop