浏览器渲染的基本原理

做为前端开发,熟悉B/S架构中的浏览器渲染原理很是重要。css

浏览器大体上由几个部分组成:不一样浏览器会有区别html

  • 界面控件 – 包括地址栏,前进后退,书签菜单等窗口上除了网页显示区域之外的部分前端

  • 浏览器引擎 – 查询与操做渲染引擎的接口web

  • 渲染引擎 – 负责显示请求的内容。好比请求到HTML, 它会负责解析HTML、CSS并将结果显示到窗口中chrome

  • 网络 – 用于网络请求, 如HTTP请求。它包括平台无关的接口和各平台独立的实现数据库

  • UI后端 – 绘制基础元件,如组合框与窗口。它提供平台无关的接口,内部使用操做系统的相应实现后端

  • JS解释器 - 用于解析执行JavaScript代码浏览器

  • 数据存储持久层 - 浏览器须要把全部数据存到硬盘上,如cookies。新的HTML5规范规定了一个完整(虽然轻量级)的浏览器中的数据库 web database安全

注意:chrome浏览器与其余浏览器不一样,chrome使用多个渲染引擎实例,每一个Tab页一个,即每一个Tab都是一个独立进程 服务器

相对于线程,进程之间是不共享资源和地址空间的,因此不会存在太多的安全问题,而因为多个线程共享着相同的地址空间和资源,因此会存在线程之间有可能会恶意修改或者获取非受权数据等复杂的安全问题

 

浏览器常驻线程:

1. GUI 渲染线程

GUI渲染线程负责渲染浏览器界面HTML元素,当界面须要重绘(Repaint)或因为某种操做引起回流(reflow)时,该线程就会执行。在Javascript引擎运行脚本期间,GUI渲染线程都是处于挂起状态的,也就是说被冻结了.

2. JavaScript引擎线程

JS为处理页面中用户的交互,以及操做DOM树、CSS样式树来给用户呈现一份动态而丰富的交互体验和服务器逻辑的交互处理。若是JS是多线程的方式来操做这些UI DOM,则可能出现UI操做的冲突;若是JS是多线程的话,在多线程的交互下,处于UI中的DOM节点就可能成为一个临界资源,假设存在两个线程同时操做一个DOM,一个负责修改一个负责删除,那么这个时候就须要浏览器来裁决如何生效哪一个线程的执行结果,固然咱们能够经过锁来解决上面的问题。但为了不由于引入了锁而带来更大的复杂性,JS在最初就选择了单线程执行。

 

GUI渲染线程与JS引擎线程互斥的,是因为JavaScript是可操纵DOM的,若是在修改这些元素属性同时渲染界面(即JavaScript线程和UI线程同时运行),那么渲染线程先后得到的元素数据就可能不一致。若是JS执行的时间过长,这样就会形成页面的渲染不连贯,致使页面渲染加载阻塞的感受。

 

3. 定时触发器线程

浏览器定时计数器并非由JS引擎计数的, 由于JS引擎是单线程的, 若是处于阻塞线程状态就会影响记计时的准确, 所以经过单独线程来计时并触发定时是更为合理的方案。

4. 事件触发线程

当一个事件被触发时该线程会把事件添加到待处理队列的队尾,等待JS引擎的处理。这些事件能够是当前执行的代码块如定时任务、也可来自浏览器内核的其余线程如鼠标点击、AJAX异步请求等,但因为JS的单线程关系全部这些事件都得排队等待JS引擎处理。

5. 异步http请求线程

在XMLHttpRequest在链接后是经过浏览器新开一个线程请求,将检测到状态变动时,若是设置有回调函数,异步线程就产生状态变动事件放到JS引擎的处理队列中等待处理。

 

渲染过程

用户请求的HTML文本(text/html)经过浏览器的网络层到达渲染引擎后,渲染工做开始。每次一般渲染不会超过8K的数据块。

渲染流程有四个主要步骤:

  1. 解析HTML生成DOM树 - 渲染引擎首先解析HTML文档,生成DOM树

  2. 构建Render树 - 接下来不论是内联式,外联式仍是嵌入式引入的CSS样式会被解析生成CSSOM树,根据DOM树与CSSOM树生成另一棵用于渲染的树-渲染树(Render tree),

  3. 布局Render树 - 而后对渲染树的每一个节点进行布局处理,肯定其在屏幕上的显示位置

  4. 绘制Render树 - 最后遍历渲染树并用UI后端层将每个节点绘制出来

以上步骤是一个渐进的过程,为了提升用户体验,渲染引擎试图尽量快的把结果显示给最终用户。它不会等到全部HTML都被解析完才建立并布局渲染树。它会在从网络层获取文档内容的同时把已经接收到的局部内容先展现出来。

 

渲染细节

1. 生成DOM树

DOM树的构建过程是一个深度遍历过程:当前节点的全部子节点都构建好后才会去构建当前节点的下一个兄弟节点。DOM树的根节点就是document对象。

DOM树的生成过程当中可能会被CSS和JS的加载执行阻塞,具体能够参见下一章。当HTML文档解析过程完毕后,浏览器继续进行标记为deferred模式的脚本加载,而后就是整个解析过程的实际结束触发DOMContentLoaded事件,并在async文档文档执行完以后触发load事件。

 

2. 生成Render树

生成DOM树的同时会生成样式结构体CSSOM(CSS Object Model)Tree,再根据CSSOM和DOM树构造渲染树Render Tree,渲染树包含带有颜色,尺寸等显示属性的矩形,这些矩形的顺序与显示顺序基本一致。从MVC的角度来讲,能够将Render树当作是V,DOM树与CSSOM树当作是M,C则是具体的调度者,比HTMLDocumentParser等。

能够这么说,没有DOM树就没有Render树,可是它们之间不是简单的一对一的关系。Render树是用于显示,那不可见的元素固然不会在这棵树中出现了,譬如 <head>。除此以外,display等于none的也不会被显示在这棵树里头,可是visibility等于hidden的元素是会显示在这棵树里头的。

3. DOM树与Render树

DOM对象类型很丰富,什么head、title、div,而Render树相对来讲就比较单一了,毕竟它的职责就是为了之后的显示渲染用嘛。Render树的每个节点咱们叫它渲染器renderer。

 

4. 布局与绘制

上面肯定了renderer的样式规则后,而后就是重要的显示元素布局了。当renderer构造出来并添加到Render树上以后,它并无位置跟大小信息,为它肯定这些信息的过程,接下来是布局(layout)。

浏览器进行页面布局基本过程是以浏览器可见区域为画布,左上角为 (0,0)基础坐标,从左到右,从上到下从DOM的根节点开始画,首先肯定显示元素的大小跟位置,此过程是经过浏览器计算出来的,用户CSS中定义的量未必就是浏览器实际采用的量。若是显示元素有子元素得先去肯定子元素的显示信息。

布局阶段输出的结果称为box盒模型(width,height,margin,padding,border,left,top,…),盒模型精确表示了每个元素的位置和大小,而且全部相对度量单位此时都转化为了绝对单位。

绘制(painting)阶段,渲染引擎会遍历Render树,并调用renderer的 paint() 方法,将renderer的内容显示在屏幕上。绘制工做是使用UI后端组件完成的。

 

5. 回流与重绘

回流(reflow):当浏览器发现某个部分发生了点变化影响了布局,须要倒回去从新渲染。reflow 会从<html>这个 root frame 开始递归往下,依次计算全部的结点几何尺寸和位置。reflow 几乎是没法避免的。如今界面上流行的一些效果,好比树状目录的折叠、展开(实质上是元素的显示与隐藏)等,都将引发浏览器的 reflow。鼠标滑过、点击……只要这些行为引发了页面上某些元素的占位面积、定位方式、边距等属性的变化,都会引发它内部、周围甚至整个页面的从新渲染。一般咱们都没法预估浏览器到底会 reflow 哪一部分的代码,它们都彼此相互影响着。

重绘(repaint):改变某个元素的背景色、文字颜色、边框颜色等等不影响它周围或内部布局的属性时,屏幕的一部分要重画,可是元素的几何尺寸没有变。

每次Reflow,Repaint后浏览器还须要合并渲染层并输出到屏幕上。全部的这些都会是动画卡顿的缘由。Reflow 的成本比 Repaint 的成本高得多的多。一个结点的 Reflow 颇有可能致使子结点,甚至父点以及同级结点的 Reflow 。在一些高性能的电脑上也许还没什么,可是若是 Reflow 发生在手机上,那么这个过程是延慢加载和耗电的。能够在csstrigger上查找某个css属性会触发什么事件。

 

4. 优化渲染性能

结合渲染流程,能够针对性的优化渲染性能:

  1. 优化JS的执行效率

  2. 下降样式计算的范围和复杂度

  3. 避免大规模、复杂的布局

  4. 简化绘制的复杂度、减小绘制区域

  5. 优先使用渲染层合并属性、控制层数量

  6. 对用户输入事件的处理函数去抖动(移动设备)

 

参考连接:

https://mp.weixin.qq.com/s/Lp8XDZlTFBRG2vUKwSo0mA

相关文章
相关标签/搜索