目录
浏览器中进程
进程列表
- 1.Browser进程:浏览器主进程,只有一个,负责协调、主控。
- 负责浏览器界面显示,与用户交互。如前进,后退等。
- 负责各个页面的管理,创建和销毁其他进程。
- 将Renderer进程得到的内存中的Bitmap,绘制到用户界面上。
- 网络资源的管理,下载等。
当访问一个新的网站时 ,主进程会启动一个新的渲染进程。
谷歌浏览器占用很多系统进程来使每个网站进程之间相互独立,且与计算机上的其他进程独立,Chrome利用这一点将网络应用以及插件放在浏览器本身的每个子进程中。基本上,每个标签都有一个进程,除非这些进程是来自同一个子域的。比如说渲染器就有着其本身的进程,每个插件以及其扩展功能也都有其自身的进程。这就意味着一个网络应用的渲染引擎崩溃不会影响整个浏览器或者其他的网络应用,操作系统可以并行运行网络应用来增强他们的响应能力,浏览器自身不会因为某个网络应用或者插件停止响应而停止工作。这也就意味着我们可以在开发时利用沙箱(一个安全的执行受限制的环境)来运行渲染引擎进程,减少意外操作而导致损失的几率。
chrome task manager
- 2.GPU进程
- 用于硬件加速图形绘制
- 3.渲染进程(也就是我们常说的浏览器内核Webkit等)
- 内部是多线程的
- 每个页面一个渲染进程互不影响(Chrome中有一些优化,同一域名下的网站可共用一个渲染进程)
- 用于解析页面,渲染页面,执行脚本,处理事件等等
- 第三方插件进程:每种类型的插件对应一个进程,仅当使用该插件时才创建
多进程优点
- 避免单个Tab页或第三方插件奔溃从而影响整个浏览器
- 多进程充分利用多核优势
- 方便使用沙盒模型隔离插件等进程,提高浏览器稳定性
在浏览器中打开一个网页相当于新起了一个渲染进程
JS执行机制
同步任务:
同步任务在JS线程上优先执行,形成一个执行栈
异步任务:
JS的异步是通过回调函数来实现的,JS只有一个执行线程。 通过把回调函数添加到任务队列中来实现
执行步骤:
- 先执行执行栈中的同步任务
- 异步任务放入任务队列
- 同步任务执行完毕,从任务队列中读取异步任务,进入执行栈,开始执行
异步任务的主要类型
1、普通事件 如click resize 等
2、资源加载 如load error 等
3、 定时器 setInterval 等
事件循环(event loop)
渲染进程(浏览器内核)
渲染进程的组成
浏览器内核主要包括以下三个技术分支:排版渲染引擎、 JavaScript引擎,以及其他。
1.GUI渲染线程
- 当浏览器收到响应的html后,该线程开始解析HTML文档构建DOM树,解析CSS文件构建CSSOM,合并构成渲染树,并计算布局样式,绘制在页面上(HTML解析规则,CSS解析规则,渲染流程细节)
- 当界面样式被修改的时候可能会触发reflow和repaint,该线程就会重新计算,重新绘制,是前端开发需要着重优化的点。
2.JS引擎线程
3. JS内核,也称JS引擎(例如V8引擎),负责处理执行javascript脚本程序
4. 由于js是单线程(一个Tab页内中无论什么时候都只有一个JS线程在运行JS程序),依靠任务队列来进行js代码的执行,所以js引擎会一直等待着任务队列中任务的到来,然后加以处理。
3.事件触发线程
5. 归属于渲染(浏览器内核)进程,不受JS引擎线程控制。主要用于控制事件(例如鼠标,键盘等事件),当该事件被触发时候,事件触发线程就会把该事件的处理函数添加进任务队列中,等待JS引擎线程空闲后执行
4.定时器出发线程
6. setInterval与setTimeout所在线程
7. 浏览器的定时器并不是由JavaScript引擎计数的,因为JavaScript引擎是单线程的, 如果处于阻塞线程状态就会影响计时的准确,因此通过单独的线程来计时并触发定时器,计时完毕后,满足定时器的触发条件,则将定时器的处理函数添加进任务队列中,等待JS引擎线程空闲后执行。
8. W3C在HTML标准中规定,规定要求setTimeout中低于4ms的时间间隔算为4ms
5.异步HTTP请求线程
当HttpRequest连接后,浏览器会新开的一个线程,当监控到readyState状态变更时,如果设置了该状态的回调函数,则将该状态的处理函数推进任务队列中,等待JS引擎线程空闲后执行
注意:浏览器对同一域名请求的并发连接数是有限制的,Chrome和Firefox限制数为6个,ie8则为10个。
总结:2-5 四个线程参与了JS的执行,但是永远只有JS引擎线程在执行JS脚本程序,其他三个线程只负责将满足触发条件的处理函数推进任务队列,等待JS引擎线程执行。
GUI渲染线程与JS引擎线程互斥 JS阻塞页面加载
由于JavaScript是可操纵DOM的,如果在修改这些元素属性同时渲染界面(即JS线程和UI线程同时运行),那么渲染线程前后获得的元素数据就可能不一致了。
因此为了防止渲染出现不可预期的结果,浏览器设置GUI渲染线程与JS引擎为互斥的关系。
当JS引擎执行时GUI线程会被挂起,GUI更新则会被保存在一个队列中等到JS引擎线程空闲时立即被执行。
从上述的互斥关系,可以推导出,JS如果执行时间过长就会阻塞页面。
譬如,假设JS引擎正在进行巨量的计算,此时就算GUI有更新,也会被保存到队列中,等待JS引擎空闲后执行。然后,由于巨量计算,所以JS引擎很可能很久很久后才能空闲,自然会感觉到巨卡无比。
所以,要尽量避免JS执行时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞的感觉。
CSS加载是否会阻塞DOM树渲染?
css加载不会阻塞DOM树解析(css是由单独的下载线程异步下载的。异步加载时DOM照常构建)
但会阻塞render树渲染(渲染时需等css加载完毕,因为render树需要css信息) 为什么会阻塞render树的渲染?
因为你加载css的时候,可能会修改下面DOM节点的样式,如果css加载不阻塞render树渲染的话,那么当css加载完之后,render树可能又得重新重绘或者回流了,这就造成了一些没有必要的损耗。
所以就先把DOM树的结构先解析完,把可以做的工作做完,然后等你css加载完之后,在根据最终的样式来渲染render树。
CSS 选择器优先级
选择器的优先级是CSS最重要的内容之一
选择器的优先级排序如下:
1. !important
在属性后面写上这条样式,会覆盖掉页面上任何位置定义的元素的样式。
2. 行内样式,在style属性里面写的样式。
3. id选择器
4. class选择器
5. 标签选择器
6. 通配符选择器
7. 浏览器的自定义属性和继承
浏览器的渲染过程
定义
- DOM Tree: 浏览器将HTML解析成树形的数据结构。
- CSS Rule Tree:浏览器将CSS解析成树形的数据结构。
- Render Tree:DOM树和CSS规则树合并后生产Render树。
- Layout(布局):有了Render Tree,浏览器已经能知道网页中有哪些节点、各个节点的CSS定义以及他们的从属关系,从而去计算出每个节点在屏幕中的位置。
- Painting(绘制): 按照算出来的规则,通过显卡,把内容画到屏幕上。
- Reflow(回流):当浏览器发现某个部分发生了点变化影响了布局,需要倒回去重新渲染,内行称这个回退的过程叫 reflow。
- Repaint(重绘):改变某个元素的背景色、文字颜色、边框颜色等等不影响它周围或内部布局的属性时,屏幕的一部分要重画,但是元素的几何尺寸没有变。
注意:display:none的节点不会被加入Render Tree,而visibility:
hidden则会,所以display:none会触发reflow,visibility: hidden会触发repaint。
浏览器内核(渲染进程)拿到响应报文之后,渲染大概分为以下步骤:
- 解析HTML生成DOM Tree,同时浏览器主进程负责下载CSS文件
- CSS文件下载完成,解析CSS生成CSS Tree。
- 根据DOM Tree和CSS Tree生成Render Tree。
- 根据Render树进行Layout,负责各个元素节点的尺寸、位置计算。
- 绘制Render树(Painting),绘制页面像素信息。
- 浏览器主进程将默认图层和复合图层交给GPU进程,GPU进程再将各个图层合成(composite),最后显示出页面
渲染完毕后JS引擎开始执行load事件,绘制流程见下图:
load事件与DOMContentLoaded事件的先后
当DOMContentLoaded事件触发时,仅当DOM加载完成,不包括CSS,图片,flash等等。(譬如如果有async加载的脚本就不一定完成)
当onload事件触发时,页面上所有的DOM,样式表,脚本,图片都已经加载完成了。(渲染完毕了)
所以,顺序是:DOMContentLoaded -> load
普通图层和复合图层
渲染步骤中就提到了composite概念。
可以简单的这样理解,浏览器渲染的图层一般包含两大类:普通图层以及复合图层
首先,普通文档流内可以理解为一个复合图层(这里称为默认复合层,里面不管添加多少元素,其实都是在同一个复合图层中)
其次,absolute布局(fixed也一样),虽然可以脱离普通文档流,但它仍然属于默认复合层。
然后,可以通过硬件加速的方式,声明一个新的复合图层,它会单独分配资源
(当然也会脱离普通文档流,这样一来,不管这个复合图层中怎么变化,也不会影响默认复合层里的回流重绘)
可以简单理解下:GPU中,各个复合图层是单独绘制的,所以互不影响,这也是为什么某些场景硬件加速效果一级棒
可以Chrome源码调试 -> More Tools -> Rendering -> Layer borders 中看到,黄色的就是复合图层信息
复合图层的好处
合成层的位图,会交由 GPU 合成,比 CPU 处理要快
当需要 repaint 时,只需要 repaint 本身,不会影响到其他的层
对于 transform 和 opacity 效果,不会触发 layout 和 paint
但是尽量不要大量使用复合图层,否则由于资源消耗过度,页面反而会变的更卡
如何变成复合图层(硬件加速)
最常用的方式:translate3d、translateZ(3D或透视变换)
等元素
元素有一个 z-index 较低且包含一个复合层的兄弟元素(换句话说就是该元素在复合层上面渲染)
元素有一个包含复合层的后代节点(换句话说,就是一个元素拥有一个子元素,该子元素在自己的层里)
硬件加速时请使用index
使用3D硬件加速提升动画性能时,最好给元素增加一个z-index属性,人为干扰合成层的排序,可以有效减少chrome创建不必要的合成层,防止层爆炸,提升渲染性能,移动端优化效果尤为明显。
原理:如果这个元素添加了硬件加速,并且index层级比较低,那么在这个元素的后面其它元素(层级比这个元素高的,或者相同的,并且releative或absolute属性相同的), 会默认变为复合层渲染,如果处理不当会极大的影响性能
简单理解:如果a是一个复合图层,而且b在a上面,那么b也会被隐式转为一个复合图层。
参考:浏览器多进程
浏览器如何执行JS
JS是在渲染进程里的JS引擎线程执行的,在此之后还要了解几个概念,编译器(Compiler)、解释器(Interpreter)、抽象语法树(AST)、字节码(Bytecode)、即时编译(JIT).
编译器和解释器
之所以存在编译器和解释器,是因为机器不能直接理解我们所写的代码,所以在执行程序之前,需要将我们所写的代码“翻译”成机器能读懂的机器语言。按语言的执行流程,可以把语言划分为编译型语言和解释型语言。
编译型语言
编译型语言在程序执行之前,需要经过编译器的编译过程,并且编译之后会直接保留机器能读懂的二进制文件,这样每次运行程序时,都可以直接运行该二进制文件,而不需要再次重新编译了
解释型语言
在每次运行时都需要通过解释器对程序进行动态解释和执行。比如 Python、JavaScript 等都属于解释型语言。
V8执行代码流程