一、浏览器
浏览器的主要功能就是向服务器发出请求,在浏览器窗口中展示您选择的网络资源。这里所说的资源一般是指 HTML 文档,也可以是 PDF、 图片或其他的类型。 资源的位置由用户使用URI(统一资源标符)指定。多年以来,各浏览器都没有完全遵从这些规范,同时还在开发自己独有的扩展程序,这给网络开发人员带来了严重的兼容性问题。 如今,大多数的浏览器都是或多或少地遵从规范。
要展现一个网页,浏览器首先发送一个请求来获取页面的HTML文档,再解析文档中的资源信息发送其他请求,获取可执行脚本或CSS样式来进行页面布局渲染,以及一些其它页面资源(如图片和视频等)。然后,浏览器将这些资源整合到一起,展现出一个完整的文档,也就是网页。浏览器执行的脚本可以在之后的阶段获取更多资源,并相应地更新网页。
二、浏览器的组成
(1)用户界面 - 包括地址栏、 前进/后退按钮、 书签菜单等。 除了浏览器主窗口显示的请求的页面外,其他显示的各个部分都属于用户界面。
(2)浏览器引擎 - 在用户界面和呈现引擎之间传送指令。
(3)渲染引擎 - 负责显示请求的内容。 如果请求的内容是 HTML,它就负责解析 HTML 和 CSS 内容,并将解析后的内容显示在屏幕上。
(4)网络 - 用于网络调用,如 HTTP 请求。 其接口与平台无关,为所有平台提供底层实现。
(5)用户界面后端 - 用于绘制基本的窗口小部件,比如组合框和窗口。 其公开了与平台无关的通用接口,而在底层使用操作系统的用户界面方法。
(6) JavaScript 解释器 - 用于解析和执行 JavaScript 代码。
(7)数据存储 - 属于持久层,浏览器需要在硬盘中保存类似cookie的各种数据,HTML5定义了web database技术,这是一种轻量级完整的客户端存储技术
三、内核
浏览器内核分成两部分:渲染引擎和js引擎,由于js引擎越来越独立,内核就倾向于只指渲染引擎,负责请求网络页面资源加以解析排版并呈现给用户,所以渲染引擎也称为浏览器内核。
【渲染引擎】
firefox使用gecko引擎
IE使用Trident引擎,2015年微软推出自己新的浏览器,原名叫斯巴达,后改名edge,使用edge引擎
opera最早使用Presto引擎,后来弃用
chrome\safari\opera使用webkit引擎,13年chrome和opera开始使用Blink引擎
UC使用U3引擎
QQ浏览器和微信内核使用X5引擎,16年开始使用Blink引擎
【js引擎】
JavaScript最初由网景公司的Brendan Eich设计,是一种动态、弱类型、基于原型的语言,内置支持类。以它为基础,制定了ECMAScript标准。javascript在浏览器的实现中还必须含有DOM和BOM。Web浏览器一般使用公共 API来创建主机对象来负责将DOM对象反射进JavaScript。JS引擎负责对JavaScript进行解释、编译和执行,以使网页达到一些动态的效果。
老版本IE使用Jscript引擎,IE9之后使用Chakra引擎,edge浏览器仍然使用Chakra引擎
firefox使用monkey系列引擎
safari使用的SquirrelFish系列引擎
Opera使用Carakan引擎
chrome使用V8引擎。nodeJs其实就是封装了V8引擎
四、前端处理流程
1、输入url
2、查看浏览器缓存,看是否有缓存,如果有缓存,继续查看缓存是否过期,如果没有过期,直接返回缓存页面,如果没有缓存或者缓存过期,发送一个请求。
3、浏览器解析url地址,获取协议、主机名、端口号和路径。
4、获取主机ip地址过程
(1)浏览器缓存
(2)主机缓存
(3)hosts文件
(4)路由器缓存
(5)DNS缓存
(6)DNS递归查询
5、浏览器发起和服务器的TCP连接,执行三次握手(略)
6、三次握手连接后,浏览器发送一个http请求(这部分是重点,请查询相关资料,详细了解http协议关于请求格式和重要的几个请求头字段含义)。
7、服务器收到请求,转到相关的服务程序,期间可能需要连接并操作数据库(主要分get和post请求)。
8、服务器看是否需要缓存,服务器处理完请求,发出一个响应(这部分也是重点,请查询资料了解http响应头各个字段的含义)
9、服务器并根据请求头包含信息决定是否需要关闭TCP连接(如需关闭,则需要四次挥手过程)
10、浏览器对接收到的响应进行解码
11、浏览器解析收到的响应并根据响应的内容(假如是HTML文件)进行构建DOM树,构建render树,渲染render树等过程
12、处理嵌入的其他资源如css文件、js文件、图片文件、音视频等文件。
五、渲染流程
渲染引擎一开始会从网络层获取请求文档的内容,通常以8K分块的方式完成。 获取了文档内容之后,渲染引擎开始正式工作:
1、解析HTML
解析HTML文档,并将文档中的标签转化为DOM节点树,即” 内容树”
2、创建渲染树(Render Tree)
同时,它也会解析外部CSS文件以及style标签中的样式数据。 这些样式信息连同HTML中的” 可见内容” 一道,被用于构建另一棵树——” 渲染树(Render树)” 。
渲染树和DOM树大体能一一对应,两者在内核中同时存在但作用不同。DOM树是HTML文档的对象表示,同时也作为JavaScript操纵HTML的对象接口。Render树是DOM树的排版表示,用以计算可视DOM节点的布局信息(如宽、高、坐标)和后续阶段的绘制显示.
【注意】并非所有DOM节点都可视,也就是并非所有DOM树节点都会对应生成一个Render树节点。例如head标签(HTMLHeadElement节点)不表示任何排版区域,因而没有对应的Render节点。同时,DOM树可视节点的CSS Style就是其对应Render树节点的Style。
【注意】这个过程是逐步完成的,为了更好的用户体验,渲染引擎将会尽可能早的将内容呈现到屏幕上,并不会等到所有的html都解析完成之后再去构建和布局渲染树。 它是解析完一部分内容就显示一部分内容,同时,可能还在通过网络下载其余内容。
3、计算布局
渲染树构建完毕之后,将会进入” 布局” 处理阶段,即为每一个节点分配一个屏幕坐标。
布局就是安排和计算页面中每个元素大小位置等几何信息的过程。HTML采用流式布局模型,基本的原则是页面元素在顺序遍历过程中依次按从左至右、从上至下的排列方式确定各自的位置区域
一个HTML元素对应一个以CSS盒子模型描述的方块区域,HTML元素分成两个基本类型,Inline和Block。Inline元素不会换行,按从左到右来布局。Block元素的出现意味着需要从上至下换到下一行来布局。除了这种基本的顺序按照元素的Inline和Block来进行流式布局之外,还有特殊指定的一些布局方式,如Absolute/Fixed/Relative三种定位布局以及Float浮动布局
简单情况下,布局可以顺序遍历一次Render树完成,但也有需要迭代的情况。当祖先元素的大小位置依赖于后代元素或者互相依赖时,一次遍历就无法完成布局,如Table元素的宽高未明确指定而其下某一子元素Tr指定其高度为父Table高度的30%的情况
经过了Layout阶段的处理,把带Style的DOM树变换成包含布局信息和绘制信息的Render树,接下来的显示工作就交由Paint模块进行操作了
4、绘制(painting)
即遍历render树,并使用UI后端层绘制每个节点。
Paint模块负责将Render树映射成可视的图形,它会遍历Render树调用每个Render节点的绘制方法将其内容显示在一块画布或者位图上,并最终呈现在浏览器应用窗口中成为用户看到的实际页面。每个节点对应的大小位置等信息都已经由Layout阶段计算好了,节点的内容取决于对应的HTML元素,或是文本,或是图片,或是UI控件
通常情况下,布局和绘制是相当耗时的操作。如果DOM树每次略有改动都要重新布局和绘制一次,效率会相当低下。因此,一般浏览内核都会实现一种增量布局和增量绘制的方式。当一个DOM树节点(或者它的子节点)内容或者样式发生变化时,内核会确定其影响范围,在布局阶段会标记出受该节点布局影响的其他节点(比如可能是子节点),在绘制阶段则会标记出一个Dirty区域并通知系统重绘
按照HTML相关规范,页面元素的CSS属性也规定了其绘制顺序,如根据不同Layer必须按顺序绘制,否则覆盖叠加效果会出现错误,如元素的边框轮廓和内容背景的绘制次序也有规定。
CSS2 定义了绘制的顺序。其实就是元素进入堆栈样式上下文的顺序。这个顺序影响绘制,因为堆栈是从后到前绘制的。
块渲染器的堆叠顺序是:
- background color
- background image
- border
- children
- outline
浏览器会尝试做出响应更改的最小可能性。
对元素的颜色的更改仅影响元素的重绘。
元素位置更改,影响其子元素(可能还有兄弟元素)进行布局和重绘。
新增的 DOM 节点导致节点的布局和重绘。
一些重大变化,如增加“html”元素的字体大小,导致整个树的缓存无效,使得整个渲染树进行重新布局和绘制。
六、其他理解
缓存
缓存在浏览器中也得到了广泛的应用,对提高用户体验起到了重要作用。在浏览器中,主要存在三种类型的缓存:Page Cache、Memory Cache、Disk Cache。这三类Cache的容量都是可以配置的,比如限制Memory Cache最大不超过30MB,Page Cache缓存的页面数量不超过5个等
Page Cache:是将浏览的页面状态临时保存在缓存中,以加速页面返回等操作
Memory Cache:浏览器内部的缓存机制,对于相同url的资源直接从缓存中获取,不需重新下载
Disk Cache:资源加载缓存和服务器进行交互,服务器端可以通过HTTP头信息设置网页要不要缓存。
【内存缓存】
Memory Cache,顾名思义内存缓存,其主要作用为缓存页面使用各种派生资源。在使用浏览器浏览网页时,尤其是浏览一个大型网站的不同页面时,经常会遇到网页中包含相同资源的情况,应用Memory Cache可以显著提高浏览器的用户体验,减少无谓的内存、时间以及网络带宽开销
【页面缓存】
Page Cache,即页面缓存。用来缓存用户访问过的网页DOM树、Render树等数据。设计页面缓存的意图在于提供流畅的页面前进、后退浏览体验。几乎所有的现代浏览器都支持页面缓存功能
如果浏览器没有页面缓存,用户点击链接访问新页面时,原页面的各种派生资源、JavaScript对象、DOM树节点等占据的内存统统被回收,此后当用户点击后退按钮以浏览原页面时,浏览器必须先要重新从网络下载相关资源,然后进行解码、解析、布局、渲染一系列操作,最后才能为用户呈现出页面,这无疑增加了用户的等待时间,影响了用户的使用体验
所有的派生资源加载时都会与Memory Cache关联,如果Memory Cache中有资源的备份且条件合适,则可以直接从Memory Cache中加载。而Page Cache只会在用户点击前进或后退按钮时才会被查询,如果页面符合缓存条件并被缓存了,则直接从Page Cache中加载。即使某个需要被加载的页面在Page Cache中有备份,但若触发加载的原因是用户在地址栏输入url或点击链接,则页面仍然是通过网络加载。也就是说Page Cache并不是主资源的通用缓存
【磁盘缓存】
Disk Cache,即磁盘缓存。现代的浏览器基本都有磁盘缓存机制,为了提升用户的使用体验,浏览器将下载的资源保存到本地磁盘,当浏览器下次请求相同的资源时,可以省去网络下载资源的时间,直接从本地磁盘中取出资源即可
磁盘缓存即我们常说的Web缓存,分为强缓存和协商缓存,它们的区别在于强缓存不发请求到服务器,协商缓存会发请求到服务器
重绘回流
重绘和回流是在页面渲染过程中非常重要的两个概念。页面生成以后,脚本操作、样式表变更,以及用户操作都可能触发重绘和回流
【回流】
回流reflow是firefox里的术语,在chrome中称为重排relayout
回流是指窗口尺寸被修改、发生滚动操作,或者元素位置相关属性被更新时会触发布局过程,在布局过程中要计算所有元素的位置信息。由于HTML使用的是流式布局,如果页面中的一个元素的尺寸发生了变化,则其后续的元素位置都要跟着发生变化,也就是重新进行流式布局的过程,所以被称之为回流
前面介绍过渲染引擎生成的3个树:DOM树、Render树、Render Layer树。回流发生在Render树上。常说的脱离文档流,就是指脱离渲染树Render Tree
触发回流包括如下操作:
1、DOM元素的几何属性变化
2、DOM树的结构变化
3、获取下列属性
offsetTop\offsetLeft\offsetWidth\offsetHeight\scrollTop\scrollLeft\scrollWidth\scrollHeight\clientTop\clientLeft\clientWidth\clientHeight\getComputedStyle()\currentStyle()
4、改变元素的一些样式
5、调整浏览器窗口大小
触发回流一定会触发后续的重绘操作,而且对一个元素的回流,可能会影响到父级元素。比如子元素浮动后,父元素会出现高度塌陷的情况。所以,性能优化的重点在于尽量只触发小规模的重绘,尽量不触发回流
【重绘】
重绘是指当与视觉相关的样式属性值被更新时会触发绘制过程,在绘制过程中要重新计算元素的视觉信息,使元素呈现新的外观
由于元素的重绘repaint只发生在渲染层 render layer上。所以,如果要改变元素的视觉属性,最好让该元素成为一个独立的渲染层render layer
下面以元素显示为例,进行说明。实现元素显示隐藏的方式有很多
display: none/block,会引起回流,从而引起重绘,性能较差
visibility: visibile/hidden,只引起重绘,但由于没有成为一个独立的渲染层,会引起整个页面(或当前渲染层)的重绘,性能较好
opacity: 0/1,opacity小于1时,会产生render layer。所以opacity在0、1的变化中,引起了render layer的生成和销毁,因此,也会引起回流,从而引起重绘,性能较差。如果opacity: 0/0.9,则只会引起重绘
如果对一个元素使用硬件加速渲染,如具有CSS 3D属性,则不会进行重绘和回流。但如果使用硬件渲染的元素过多,会造成GPU的传输压力
【性能优化】
下面列举一些减少回流次数的方法
1、不要一条一条地修改DOM样式,而是修改className或者修改style.cssText
2、在内存中多次操作节点,完成后再添加到文档中去
3、对于一个元素进行复杂的操作时,可以先隐藏它,操作完成后再显示
4、在需要经常获取那些引起浏览器回流的属性值时,要缓存到变量中
5、不要使用table布局,因为一个小改动可能会造成整个table重新布局。而且table渲染通常要3倍于同等元素时间
此外,将需要多次重绘的元素独立为render layer渲染层,如设置absolute,可以减少重绘范围;对于一些进行动画的元素,可以进行硬件渲染,从而避免重绘和回流
参考
前端进阶(一)浏览器运行原理
浏览器工作原理及内核详解
深入理解浏览器工作原理