【原理】深刻理解浏览器工做原理

1、浏览器的组成

1. 用户界面 - 包括地址栏、后退/前进按钮、书签目录等,也就是所看到的除了用来显示所请求页面的主窗口以外的其余部分css

  2. 浏览器引擎 - 用来查询及操做渲染引擎的接口html

  3. 渲染引擎 - 用来显示请求的内容,例如,若是请求内容为html,它负责解析html及css,并将解析后的结果显示出来。(webkit引擎)node

  4. 网络 - 用来完成网络调用,例如http请求,它具备平台无关的接口,能够在不一样平台上工做。(Loader模块)web

  5. UI后端 - 用来绘制相似组合选择框及对话框等基本组件,具备不特定于某个平台的通用接口,底层使用操做系统的用户接口。chrome

  6. JS解释器 - 用来解释执行JS代码。(V8引擎,nodeJs其实就是封装了chrom的V8引擎)编程

  7. 数据存储 - 属于持久层,浏览器须要在硬盘中保存相似cookie的各类数据,HTML5定义了web database技术,这是一种轻量级完整的客户端存储技术后端

2、浏览器内核

    浏览器内核分红两部分:渲染引擎和js引擎,因为js引擎愈来愈独立,内核就倾向于只指渲染引擎,负责请求网络页面资源加以解析排版并呈现给用户数组

  默认状况下,渲染引擎能够显示html、xml文档及图片,它也能够借助插件显示其余类型数据,例如使用PDF阅读器插件,能够显示PDF格式浏览器

【渲染引擎】缓存

  firefox使用gecko引擎

  IE使用Trident引擎,2015年微软推出本身新的浏览器,原名叫斯巴达,后更名edge,使用edge引擎

  opera最先使用Presto引擎,后来弃用

  chrome\safari\opera使用webkit引擎,13年chrome和opera开始使用Blink引擎

  UC使用U3引擎

  QQ浏览器和微信内核使用X5引擎,16年开始使用Blink引擎

【js引擎】

  老版本IE使用Jscript引擎,IE9以后使用Chakra引擎,edge浏览器仍然使用Chakra引擎

  firefox使用monkey系列引擎

  safari使用的SquirrelFish系列引擎

  Opera使用Carakan引擎

  chrome使用V8引擎。nodeJs其实就是封装了V8引擎

渲染流程

  从资源的下载到最终的页面展示,渲染流程可简单地理解成一个线性串联的变换过程的组合,原始输入为URL地址,最终输出为页面Bitmap,中间依次通过了Loader、Parser、Layout和Paint模块

chrome1

  渲染引擎的核心流程以下所示

chrome

【Loader】

  Loader模块负责处理全部的HTTP请求以及网络资源的缓存,至关因而从URL输入到Page Resource输出的变换过程。HTML页面中一般有外链的JS/CSS/Image资源,为了避免阻塞后续解析过程,通常会有两个IO管道同时存在,一个负责主页面下载,一个负责各类外链资源的下载

chrome

  注意:虽然大部分状况下不一样资源能够并发下载异步解析(如图片资源能够在主页面解析显示完成后再被显示),但JS脚本可能会要求改变页面,所以有时保持执行顺序和下载管道后续处理的阻塞是不可避免的

3、缓存

  缓存在浏览器中也获得了普遍的应用,对提升用户体验起到了重要做用。在浏览器中,主要存在三种类型的缓存: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缓存,分为强缓存和协商缓存,它们的区别在于强缓存不发请求到服务器,协商缓存会发请求到服务器

 

网页解析

  能够将浏览器总体看做一个网页处理模块,这个模块的输入是网络上接收到的字节流形式的网页内容。输出是三棵树型逻辑结构:DOM树、Render树及RenderLayer树

  浏览器的解析过程就是将字节流形式的网页内容构建成DOM树、Render树及RenderLayer树的过程

  浏览器的解析对象是网页内容,网页内容包括如下三个部分:

  一、HTML文档:超文本标记语言,制做Web页面的标准语言

  二、CSS样式表(Cascading Style Sheet):级联样式表,用来控制网页样式,并容许样式信息与网页内容相分离的一种标记性语言

  三、JavaScript脚本:JavaScript是一种无类型的解释型脚本语言。经常使用于为网页添加动态功能

  HTML文档决定了DOM树及Render树的结构。CSS样式表决定了Render树上节点的排版布局方式。JavaScript代码能够操做DOM树,改变DOM树的结构,也能够用来给页面添加更丰富的动态功能

  HTML文档被解析生成DOM树,由DOM节点建立Render树节点时,会触发CSS匹配过程,CSS匹配的结果是RenderStyle实例,这个实例由Render节点持有,保存了Render节点的排版布局信息。CSS的解析过程便是CSS语法在浏览器的内部表示过程,解析的结果是获得一系列的CSS规则。CSS的匹配过程主要依据CSS选择器的不一样优先级进行,高优先级选择器优先适用。根据网页上定义的JavaScript脚本的不一样属性,JavaScript脚本的下载和执行时机会有所不一样。JavaScript脚本的执行是由渲染引擎转交给JS引擎执行的。下面分别看一下HTML、CSS、JavaScript的具体解析和执行

【DOM树构建】

  DOM(Document Object Model,文档对象模型),是中立于平台和语言的接口。它容许程序和脚本动态地访问和更新文档的内容结构和样式。DOM是页面上数据和结构的一个树形表示,使用DOM接口能够对DOM树结构进行操做。DOM规范只是定义了编程接口,没有对文档的表示方式作任何限制。以树状结构表示DOM文档是比较广泛的实现方式。这个树状结构就称为DOM树。DOM树是DOM文档中的节点按照层次组织构成的。以HTML文档为例,每个标签都对应着DOM树上的一个节点。因为是树形结构表示,这些节点之间的关系也是经过父子或兄弟维系的

  渲染引擎解析HTML文档的过程就是将字节流形式的网页内容解析成DOM Tree、Render Tree、Render Layer Tree三棵树的过程。这个过程能够分为解码、分词、解析、建树四个步骤

  一、解码:将网络上接收到的通过编码的字节流,解码成Unicode字符

  二、分词:按照必定的切词规则,将Unicode字符流切成一个个的词语(Tokens)

  三、解析:根据词语的语义,建立相应的节点(Node)

  四、建树:将节点关联到一块儿,建立DOM树、Render树和RenderLayer树

【Render树构建】

  Render树用于表示文档的可视信息,记录了文档中每一个可视元素的布局及渲染方式。Render树与DOM树是同时建立的

  HTML页面经过CSS控制页面布局,因此RenderObject须要知道自身的CSS属性,CSSStyleSelector负责为元素提供RenderStyle。RenderObject包含自身的RenderStyle的引用。CSSStyleSelector是在CSS解析过程当中生成的。Render节点建立后,就会被attach到Render树上

  当前Render节点的父节点负责将当前Render节点插入到合适的位置,当父Render节点设置好当前Redner节点的先后兄弟节点后,当前Render节点就attach到了Render树上

  RenderObject是Render树全部节点的基类,做用相似于DOM树的Node类。这个类存储了绘制页面可视元素所须要的样式及布局信息,RenderObject对象及其子类都知道如何绘制本身。事实上绘制Render树的过程就是RenderObject按照必定顺序绘制自身的过程。DOM树上的节点与Render树上的节点并非一一对应的。只有DOM树的根节点及可视节点才会建立对应的RenderObject节点

【Render Layer树构建】

  RenderLayer树以层为节点组织文档的可视信息,网页上的每一层对应一个RenderLayer对象。RenderLayer树能够看做Render树的稀疏表示,每一个RenderLayer树的节点都对应着一棵Render树的子树,这棵子树上全部Render节点都在网页的同一层显示

  RenderLayer树是基于RenderObject树构建的,知足必定条件的RenderObject才会创建对应的RenderLayer节点。下面是RenderLayer节点的建立条件:

  一、网页的root节点

  二、有显式的CSS position属性(relative,absolute,fixed)

  三、元素设置了transform

  四、元素是透明的,即opacity不等于1

  五、节点有溢出(overflow)、alpha mask或者反射(reflection)效果。

  六、元素有CSS filter(滤镜)属性

  七、2D Canvas或者WebGL

  八、Video元素

  当知足这些条件之一时,RenderLayer实例被建立。RenderObject节点与RenderLayer节点是多对一的关系,即一个或多个RenderObject节点对应一个RenderLayer节点。这一点能够理解为网页的一层中可包含一个或多个可视节点。RenderLayer树的根节点是RenderView实例

  RenderLayer的一个重要用途是能够在绘制时实现合成加速,即每个RenderLayer对应系统的一块后端存储,这样在网页内容发生更新时,能够只更新有变化的RenderLayer,从而提升渲染效率

【CSS解析】

  CSS解析过程便是将原始的CSS文件中包含的一系列CSS规则表示成渲染引擎中相应规则类的实例的过程

chrome

  解析选择器和解析属性值的过程均可能执行屡次。渲染引擎为解析出来的选择器建立一个CSSSelector实例,因为可能存在多个选择器,渲染引擎使用CSSSelectorList类保存全部的选择器,并为解析出来的每一个属性值对建立CSSProperty实例

  CSS文件解析完成后,CSS规则都保存在了CSSRuleList实例中,这些规则会在建立Render节点的过程当中使用到。Node节点经过调用CSSStyleSelector实例的StyleForElement()函数为Render节点建立RenderStyle实例。有了RenderStyle实例才能够建立RenderObject实例。RenderStyle描述了RenderObject的排版布局信息,也就是匹配后的样式信息

chrome

  CSS规则匹配过程就发生在CSSStyleSelector建立RenderStyle实例的过程当中。CSSStyleSelector负责从CSSRuleList中找出全部匹配相应元素的样式属性的Property-Value对

  CSS规则匹配是按照选择器类型的优先级进行的,不一样类型的选择器具备不一样的优先级。经常使用选择器类型的优先级以下:

ID选择器 > 类型选择器 > 标签选择器 > 相邻选择器 > 子选择器 > 后代选择器

  全部匹配上元素的CSSStyleRule都会放入一个结果数组中。渲染引擎会对全部存入结果数组中的规则按照选择器的优先级进行排序,高优先级规则优先使用,最终使用的规则会用来建立RenderStyle实例。RenderStyle实例由RenderObject对象持有,RenderObject就是根据RenderStyle中包含的信息,进行自身排版绘制

【JS执行】

  JavaScript是一种解释型的动态脚本语言,须要由专门的JavaScript引擎执行。Android 4.2版本的WebKit采用的JavaScript执行引擎为V8,V8是由Google支持的开源项目。它的设计目的就是追求更高的性能,最大限度地提升JavaScript的执行效率。与JavaScriptCore等传统引擎不一样,V8把JavaScript代码直接编译成机器码运行,比起传统“中间代码+解释器”的引擎,性能优点很是明显。JS代码一般保存在独立的JS文件中,经过script标签引用到HTML文档中

  DOM树建立过程当中遇到script标签时会建立HTMLScriptElement实例。HTMLScript-Element的父类ScriptElement中包含了对JS脚本的全部处理,包括下载、缓存、执行等。根据script标签的不一样属性,JS脚本加载后的执行时机会有所不一样。若是script标签中使用了async属性,JS脚本加载过程不会阻塞文档解析,脚本加载完成后会当即执行。若是script标签中使用了defer属性,JS脚本加载过程不会阻塞文档解析,当脚本的执行要等获得文档解析完成以后。对于外部引用的脚本文件,从脚本下载到脚本执行完,文档解析过程会一直被阻塞

 

4、硬件加速

  WebKit渲染引擎的渲染方式分为软件渲染和硬件渲染,这两种渲染方式均可以分红两个大的过程:一是获得网页的绘制信息;二是将网页绘制信息转换成像素并上屏

  获得网页绘制信息的过程须要遍历RenderLayer树,将RenderLayer树包含的网页绘制信息先记录下来,等到渲染时使用。记录网页绘制信息这一步对渲染引擎而言,就是绘制的过程,渲染引擎自己并不知道绘制命令是否有被真正执行

【软件渲染】

  软件渲染的流程可归纳为如下三步:

  一、从SurfaceFlinger得到一块图形缓冲区

  二、在封装这块图形缓冲区的SkCanvas上执行网页绘制命令

  三、将绘制好的图形缓冲区归还SurfaceFlinger

  软件渲染实现简单,网页内容直接绘制到一块图形缓冲区上,内存占用更少。不足之处在于,因为网页内容绘制在同一块图形缓冲区上,更新网页内容时须要所有更新,没法局部更新

【硬件渲染】

  相较于软件渲染,硬件渲染实现比较复杂,网页内容须要先绘制到一块SkBitmap上,再经过图形缓冲区上传给GPU,须要更多内存

  硬件渲染是指网页各层的合成是经过GPU完成的,它采用分块渲染的策略,分块渲染是指:网页内容被一组Tile覆盖,每块Tile对应一个独立的后端存储,当网页内容更新时,只更新内容有变化的Tile。分块策略能够作到局部更新,渲染效率更高

  硬件渲染的过程分为如下5步:

  一、在一块封装了SkBitmap的SkCanvas上执行一个Tile覆盖的网页信息的绘制命令;

  二、将每一个Tile对应的SkBitmap copy到从SurfaceFlinger得到的一块图形缓冲区中;

  三、将全部Tile对应的图形缓冲区上传GPU进行合成;

  四、将合成好的网页内容blit到Tile对应的与OnScreen FrameBuffer相关联的Texture;

  五、经过GPU对Tile对应的Texture进行硬件绘制

  开启硬件渲染,即合成加速,会为须要单独绘制的每一层建立一个GraphicsLayer

  合成加速状况下,每一层网页内容都对应一个后端存储,这块后端存储由平台实现,Android 4.2平台提供的后端存储是GraphicsLayerAndroid。开始记录网页绘制命令时,RenderLayerCompositor负责控制RenderLayer的遍历,RenderLayer包含的绘制信息最终记录在其后端存储上,即GraphicsLayerAndroid包含的PicturePile实例中

  一个RenderLayer对象若是须要后端存储,它会建立一个RenderLayerBacking对象,该对象负责Renderlayer对象所须要的各类存储。理想状况下,每一个RenderLayer均可以建立本身的后端存储,事实上不是全部RenderLayer都有本身的RenderLayerBacking对象。若是一个RenderLayer对象被像样的建立后端存储,那么将该RenderLayer称为合成层(Compositing Layer)

  哪些RenderLayer能够是合成层呢?若是一个RenderLayer对象具备如下的特征之一,那么它就是合成层:

  一、RenderLayer具备CSS 3D属性或者CSS透视效果。

  二、RenderLayer包含的RenderObject节点表示的是使用硬件加速的视频解码技术的HTML5 ”video”元素。

  三、 RenderLayer包含的RenderObject节点表示的是使用硬件加速的Canvas2D元素或者WebGL技术。

  四、RenderLayer使用了CSS透明效果的动画或者CSS变换的动画。

  五、RenderLayer使用了硬件加速的CSSfilters技术。

  六、RenderLayer使用了剪裁(clip)或者反射(reflection)属性,而且它的后代中包括了一个合成层。

  七、RenderLayer有一个Z坐标比本身小的兄弟节点,该节点是一个合成层

  因此,进行硬件加速的渲染流程以下所示

browser

 

5、重绘回流

  重绘和回流是在页面渲染过程当中很是重要的两个概念。页面生成之后,脚本操做、样式表变动,以及用户操做均可能触发重绘和回流

【回流】

  回流reflow是firefox里的术语,在chrome中称为重排relayout

  回流是指窗口尺寸被修改、发生滚动操做,或者元素位置相关属性被更新时会触发布局过程,在布局过程当中要计算全部元素的位置信息。因为HTML使用的是流式布局,若是页面中的一个元素的尺寸发生了变化,则其后续的元素位置都要跟着发生变化,也就是从新进行流式布局的过程,因此被称之为回流

  前面介绍过渲染引擎生成的3个树:DOM树、Render树、Render Layer树。回流发生在Render树上。常说的脱离文档流,就是指脱离渲染树Render Tree

  触发回流包括以下操做:

  一、DOM元素的几何属性变化

  二、DOM树的结构变化

  三、获取下列属性

offsetTop\offsetLeft\offsetWidth\offsetHeight\scrollTop\scrollLeft\scrollWidth\scrollHeight\clientTop\clientLeft\clientWidth\clientHeight\getComputedStyle()\currentStyle()

  四、改变元素的一些样式

  五、调整浏览器窗口大小

  触发回流必定会触发后续的重绘操做,并且对一个元素的回流,可能会影响到父级元素。好比子元素浮动后,父元素会出现高度塌陷的状况。因此,性能优化的重点在于尽可能只触发小规模的重绘,尽可能不触发回流

【重绘】

  重绘是指当与视觉相关的样式属性值被更新时会触发绘制过程,在绘制过程当中要从新计算元素的视觉信息,使元素呈现新的外观

  因为元素的重绘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的传输压力

6、【性能优化】

  下面列举一些减小回流次数的方法

  一、不要一条一条地修改DOM样式,而是修改className或者修改style.cssText

  二、在内存中屡次操做节点,完成后再添加到文档中去

  三、对于一个元素进行复杂的操做时,能够先隐藏它,操做完成后再显示

  四、在须要常常获取那些引发浏览器回流的属性值时,要缓存到变量中

  五、不要使用table布局,由于一个小改动可能会形成整个table从新布局。并且table渲染一般要3倍于同等元素时间

  此外,将须要屡次重绘的元素独立为render layer渲染层,如设置absolute,能够减小重绘范围;对于一些进行动画的元素,能够进行硬件渲染,从而避免重绘和回流

 

转自:https://www.cnblogs.com/xiaohuochai/p/9174471.html

相关文章
相关标签/搜索