[TOC]

bilibili up主视频整理

学习浏览器原理,让你写出更好的代码、追求更好的用户体验

浏览器对于前端工程师,就像是赛车对于一个赛车手。你需要对自己的战车足够了解才能跑得更快。

不懂浏览器的前端工程师不是好码农 —— 鲁迅

浏览器的发展史

1991年蒂姆·伯纳斯-李(Tim Berners-Lee) 发明了浏览器WorldWideWeb。功能简单,只支持展示文本、图片
1993年,我出生了。Mosaic问世,可以同时显示文本和图像。
1994年,网景浏览器(Netscape)发布,曾经参与开发Mosaic的人共同创建。只能显示html,没有js和css。变成主流浏览器,获得绝大多数的市场份额。【同一年,出现Opera】
1995年,万恶的IE浏览器问世,IE1.0、IE2.0/
第一次浏览器世界大战打响

1996年,IE3.0问世,集成到了window操作系统中(此时网景的市场份额86%)。接下来的4年内,IE借助window操作系统,逐渐蚕食网景的市场份额。到了1999年,IE的市场份额达到了75%
1998年,网景公司成立Mozilla基金会,用于开发开源项目火狐浏览器FireFox,来对抗IE。并在2004年发布了FireFox的1.0版本
第二次浏览器世界大战打响

2003年,苹果发布Safari浏览器,并集成在所有的苹果IOS操作系统中
2004年,Firefox 1.0发布
2005年,苹果开源了Safari的浏览器内核webkit
2008年,谷歌以苹果开源项目webkit作为内核,创建了一个新的项目Chromium。并基于该项目,谷歌发布了自己的浏览器Chrome。目前广受欢迎

由于IE的性能和体验问题,逐渐被市场所抛弃。
2015年,微软放弃了IE,并基于webkit内核开发了Edge来替代IE,但是此时,微软在浏览器的天下已经没了。
image.png
(2020.05 统计 Chrome已经占据60%多的市场份额了)

浏览器众多,但功能都类似,框架结构也都大同小异。

简化的浏览器结构图

  • 用户界面:用于展示除“标签页”、“窗口”之外的其他页面内容。
  • 浏览器引擎:用户界面和渲染引擎之前传递数据
  • 渲染引擎:负责渲染用户请求的页面内容
    • 下边还包括很多小的模块:
    • 网络模块:负责网络请求
    • JS解释器:解析和执行JS
  • 数据存储持久层:帮助浏览器存储数据(比如Cookie)

image.png

渲染引擎

可以说是一个浏览器的核心。
我们往往把浏览器的渲染引擎称为浏览器的内核,不同浏览器使用的内核不同:

浏览器与浏览器内核

IE:Trident
FireFox:Gecko
Safari:Webkit
Chrome、Opera、Edge:Blink(Blink是Google基于Webkit独立创建的开源的项目)

浏览器是一个多进程的应用程序

一个运行在操作系统中的应用程序。
一个应用程序至少启动一个进程来执行其功能。每个程序需要运行很多任务,进程就会创建很多线程来帮助他去执行这些小的任务。

应用程序的进程和线程

进程

进程是操作系统进行资源分配和调度的基本单元,可以申请和拥有计算机资源。进程是程序的基本执行实体。

线程

线程是操作系统能够进行运算调度的最小单位,一个进程可以并发多个线程,每条线程并行执行不同的任务。

工作原理

进程

当我们启动一个程序时,就会启动一个进程来执行任务代码。同时会为该进程分配内存空间,该应用程序的状态都保存在在应用的内存空间里。当应用关闭时,该内存空间就会被回收。
进程可以启动更多的进程来执行任务。
由于每个进程分配的内存空间是独立的,因此多个进程之间共享数据将通过“进程间通信管道IPC”来传递(Inter-Process Communication)
很多应用程序都是多进程的结构,多个进程间相互独立、互不影响,避免某一个进程卡死造成整个应用程序的崩溃。
举例子:可以把笔记本想象成一个应用程序,外接鼠标是其中的一个进程,外接鼠标坏了,必会影响笔记本的工作。

线程

进程可以将任务分成更多细小的任务,然后通过创建不同的线程来并行执行多个不同的任务。
统一进程下的线程是可以直接进行通信并共享数据的。

早期单进程结构浏览器的问题

在早期单进程结构的浏览器中,有一个页面线程负责渲染页面和展示页面,JS线程执行JS代码。还有其他各种线程
image.png

问题:

1、不稳定

其中一个线程的卡死可能导致整个进程阻塞。比如打开多个标签页(早期多个标签页就是多个线程),其中一个标签页卡死,整个浏览器就卡死了。

2、不安全

多个线程间是共享数据的。但是浏览器的多个标签页之间可不止一个站点啊,也就是taobao.com可以和jd.com共享同一份cookie等数据,这个可怕程度可想而知。

3、不流畅

一次性负责太多的事情,会导致运行效率的问题。

多进程浏览器结构

为了解决以上的问题,采用了多进程浏览器结构。根据进程功能来拆解浏览器。
image.png

浏览器进程

负责控制除标签页外的用户界面,包括地址栏、书签、前进、后退等按钮及其功能。以及负责与浏览器的其他进程协调工作。
image.png

网络进程

负责发起、接收网络请求

GPU进程

负责整个浏览器界面的渲染

插件进程

负责整个网站中使用的插件,比如Flash等。而不是Chrome的扩展插件。

渲染器进程

用来控制显示tab标签内的所有内容。
默认情况下,浏览器会为每个标签页都创建一个“渲染进程”。但是实际情况中,基于浏览器进程模型的设置来的。

Chrome的4种进程模型(Supported models)

  1. Process-per-site-instance(默认)
  2. Process-per-site
  3. Process-per-tab
  4. Single process

image.png

1、Process-per-site-instance

默认情况下,Chrome为用户访问的每个网站实力创建一个渲染器进程,这样可以确保来自不同站点的页面是独立呈现的。并且对同一站点的单独访问也是彼此隔离的。简而言之,就是访问不同站点或访问同一站点的不同页面都会创建一个新的进程。
这种模型,会创建更多的进程、占用更多的内存空间。但是这种形式确实是最安全的。这样可以使得每个tab、及tab内的每个每个站点都是相互隔离、互不影响的。其中一个标签页进程卡死,并不会影响其他标签页。

2、Process-per-site

表示同一站点使用同一进程
image.png

3、Process-per-tab

一个tab里的所有站点使用同一个进程
image.png

4、Single process

让浏览器引擎和渲染引擎共用一个进程。
image.png

输入网址按下回车后,浏览器内部的工作流程如下

这里作为浏览器内部工作原理的探究,忽略网络请求发出后的过程,具体流程看《互联网的原理》中相关部分的具体整理。

1、UI线程判断内容 + 请求数据

浏览器进程(Browser Process)的UI线程会捕捉你输入的内容,
如果输入的是关键词,浏览器会跳转到配置的默认搜索引擎界面并查询关键词。查询结果返回后渲染页面;
如果输入的是网址,则UI线程会启动一个网络线程来请求DNS进行域名解析,接着链接服务器、获取数据。数据返回后渲染页面。

2、获取数据后的安全检查

网络线程获取数据后,会通过SafeBrowsing来检查站点是否是恶意站点。
如果是,则会展示一个警告页面。告诉你这个站点有安全问题。浏览器会阻止你的访问,当然你也可以强制继续访问。

SafeBrowsing

谷歌内部的一套站点安全系统。通过检测该站点的数据来判断一个网站是否安全。比如查看站点的IP是否在谷歌的黑名单内。
image.png
继续访问
(返回数据准备完毕 + 安全校验通过)
网络线程通知UI线程,我准备好了,该你了。
UI线程创建一个渲染器进程(Renderer Thread)来渲染页面。【?线程可以创建进程吗?】
浏览器进程通过IPC管道将数据传递给渲染器进程。正式进入渲染流程。
image.png

3、页面渲染流程 Renderer Process — Main Thread

渲染器进程的核心工作就是把HTML、JS、CSS、Image等资源渲染成可以让用户交互的Web页面。

1)、Tokeniser词法解析 生成DOM Tree:

渲染器进程接收到HTML数据,渲染器进程的主进程将html进行解析,构造DOM树数据结构。

DOM(文档对象模型):是浏览器对页面在其内部的表现形式,是web开发程序员可以通过JS与浏览器进行交互的数据结构和API。

Html -> Tokeniser -> DOM Tree

html通过tokeniser标记化,通过词法分析将输入的HTML内容解析成多个标记。根据识别后的标记进行DOM树构造。
image.png

DOM树构造:Tree Construction

DOM树构造过程中,会创建document对象,然后以document为根结点的dom树不断进行修改。像其中添加各种元素。
HTML代码中往往会引入一些其他的资源:图片、css、js等。
图片和css资源需要通过网络下载、或者从缓存中直接加载。这些资源不会阻塞html的解析。因为他们不会影响DOM树的生成。【?但是他们影响页面的绘制吧?比如图片没加载过来,图片容器的高度很小,但当图片加载完,图片所占区域高度不断增加,下边的内容会不断下移,就是重排?】
但当HTML解析过程中遇到