一直对浏览器的进程、线程的运行一无所知,通过一次的刷刷刷相关的博客以后,对其有了初步的了解,是时候该总结一波了。javascript
一个进程有一个或多个线程,线程之间共同完成进程分配下来的任务。打个比方:css
再完善完善概念:html
进程是cpu资源分配的最小单位(是能拥有资源和独立运行的最小单位),线程是cpu调度的最小单位(线程是创建在进程的基础上的一次程序运行单位)。前端
知道了进程与线程之间的关系以后,下面是浏览器与进程的关系了。首先,浏览器是多进程的,之因此浏览器可以运行,是由于系统给浏览器分配了资源,如cpu、内存,简单的说就是,浏览器每打开一个标签页,就至关于建立了一个独立的浏览器进程。例如咱们查看chrome里面的任务管理器。java
注意: 在这里浏览器应该也有本身的优化机制,有时候打开多个tab页后,能够在Chrome任务管理器中看到,有些进程被合并了(譬如打开多个空白标签页后,会发现多个空白标签页被合并成了一个进程),因此每个Tab标签对应一个进程并不必定是绝对的。web
除了浏览器的标签页进程以外,浏览器还有一些其余进程来辅助支撑标签页的进程,以下:
① Browser进程:浏览器的主进程(负责协调、主控),只有一个。做用有chrome
② 第三方插件进程:每种类型的插件对应一个进程,仅当使用该插件时才建立
③ GPU进程:最多一个,用于3D绘制等
④ 浏览器渲染进程(浏览器内核),Renderer进程,内部是多线程的,也就是咱们每一个标签页所拥有的进程,互不影响,负责页面渲染,脚本执行,事件处理等浏览器
以下图:网络
浏览器内核,即咱们的渲染进程,有名Renderer进程,咱们页面的渲染,js的执行,事件的循环都在这一进程内进行,也就是说,该进程下面拥有着多个线程,靠着这些现成共同完成渲染任务。那么这些线程是什么呢,以下:数据结构
① 图形用户界面GUI渲染线程
② JS引擎线程
③ 事件触发线程
④ 定时触发器线程
setInterval
与setTimeout
所在线程4ms
的时间间隔算为4ms
⑤ 异步HTTP请求线程
浏览器内核,放图增强记忆:
JavaScript做为一门客户端的脚本语言,主要的任务是处理用户的交互,而用户的交互无非就是响应DOM的增删改,使用事件队列的形式,一次事件循环只处理一个事件响应,使得脚本执行相对连续。若是JS引擎被设计为多线程的,那么DOM之间必然会存在资源竞争,那么语言的实现会变得很是臃肿,在客户端跑起来,资源的消耗和性能将会是不太乐观的,故设计为单线程的形式,并附加一些其余的线程来实现异步的形式,这样运行成本相对于使用JS多线程来讲下降了不少。
由于JS引擎能够修改DOM树,那么若是JS引擎在执行修改了DOM结构的同时,GUI线程也在渲染页面,那么这样就会致使渲染线程获取的DOM的元素信息可能与JS引擎操做DOM后的结果不一致。为了防止这种现象,GUI线程与JS线程须要设计为互斥关系,当JS引擎执行的时候,GUI线程须要被冻结,可是GUI的渲染会被保存在一个队列当中,等待JS引擎空闲的时候执行渲染。
由此也能够推出,若是JS引擎正在进行CPU密集型计算,那么JS引擎将会阻塞,长时间不空闲,致使渲染进程一直不能执行渲染,页面就会看起来卡顿卡顿的,渲染不连贯,因此,要尽可能避免JS执行时间过长。
事件触发线程、定时触发器线程、异步HTTP请求线程三个线程有一个共同点,那就是使用回调函数的形式,当知足了特定的条件,这些回调函数会被执行。这些回调函数被浏览器内核理解成事件,在浏览器内核中拥有一个事件队列,这三个线程当知足了内部特定的条件,会将这些回调函数添加到事件队列中,等待JS引擎空闲执行。例如异步HTTP请求线程,线程若是检测到请求的状态变动,若是设置有回调函数,回调函数会被添加事件队列中,等待JS引擎空闲了执行。
可是,JS引擎对事件队列(宏任务)与JS引擎内的任务(微任务)执行存在着前后循序,当每执行完一个事件队列的时间,JS引擎会检测内部是否有未执行的任务,若是有,将会优先执行(微任务)。
由于JS引擎是单线程的,当JS执行时间过长会页面阻塞,那么JS就真的对CPU密集型计算无能为力么?
因此,后来HTML5中支持了 Web Worker。
来自MDN的官方解释
Web Workers 使得一个Web应用程序能够在与主执行线程分离的后台线程中运行一个脚本操做。这样作的好处是能够在一个单独的线程中执行费时的处理任务,从而容许主(一般是UI)线程运行而不被阻塞/放慢。
注意点:
因此,若是须要进行一些高耗时的计算时,能够单独开启一个WebWorker线程,这样无论这个WebWorker子线程怎么密集计算、怎么阻塞,都不会影响JS引擎主线程,只须要等计算结束,将结果经过postMessage传输给主线程就能够了。
另外,还有个东西叫 SharedWorker
,与WebWorker在概念上所不一样。
SharedWorker由进程管理,WebWorker是某一个Renderer进程下的线程。
每一个浏览器内核的渲染流程不同,下面咱们主要以webkit
为主。
首先是渲染的前奏:
在说渲染以前,须要理解一些概念:
reflow
。reflow 会从 <html> 这个 root frame 开始递归往下,依次计算全部的结点几何尺寸和位置。reflow 几乎是没法避免的。如今界面上流行的一些效果,好比树状目录的折叠、展开(实质上是元素的显 示与隐藏)等,都将引发浏览器的 reflow。鼠标滑过、点击……只要这些行为引发了页面上某些元素的占位面积、定位方式、边距等属性的变化,都会引发它内部、周围甚至整个页面的从新渲 染。一般咱们都没法预估浏览器到底会 reflow 哪一部分的代码,它们都彼此相互影响着。注意:display:none
的节点不会被加入Render Tree,而visibility: hidden
则会,因此display:none
会触发reflow
,visibility: hidden
会触发repaint
。
浏览器内核拿到响应报文以后,渲染大概分为如下步骤
详细步骤略去,大概步骤以下,渲染完毕后JS引擎开始执行load
事件,绘制流程见下图。
由图中能够看出,css在加载过程当中不会影响到DOM树的生成,可是会影响到Render树的生成,进而影响到layout,因此通常来讲,style的link标签须要尽可能放在head里面,由于在解析DOM树的时候是自上而下的,而css样式又是经过异步加载的,这样的话,解析DOM树下的body节点和加载css样式能尽量的并行,加快Render树的生成的速度,固然,若是css是经过js动态添加进来的,会引发页面的重绘或从新布局。
从有html标准以来到目前为止(2017年5月),标准一直是规定style元素不该出如今body元素中。
前面提到了load
事件,那么与DOMContentLoaded
事件有什么分别。
顺序是:DOMContentLoaded -> load
写到这里,总结了也有很多的内容,也对浏览器多线程、JS引擎有所了解,后面打算在看看JS的运行机制。前端知识也是无穷无尽,数不清的概念与无数个易忘的知识、各类框架原理,学来学去,仍是发现本身知道得太少了。