一开始我只想弄明白js在浏览器里面究竟是怎么执行的,发现本身须要补补基础,因而打算总结一个文章来补一下基础,有什么不对的地方还请大佬们指正。css
这篇文章大算分几个章节总结一下本身学到的知识点,顺便复习一些计算机基础知识,了解JS代码在浏览器是如何执行的,如何渲染页面的。html
-进程是一个工厂,工厂有它本身独立的资源,工厂之间相互独立;前端
-线程是工厂中的工人,多个工人协做完成工厂的任务;git
-工厂内能够有一个或者多个工人;github
-工人之间共享空间;浏览器
而后咱们再看下图,稍微再解释一下。markdown
首先计算机能够有不少应用程序,浏览器就是其中之一。浏览器能够有不少个模块(进程),每一个模块又独立且分配有本身的资源(线程)。网络
1.应用程序是有一个或多个模块组成的,以如今的谷歌浏览器举例,他有一个浏览器主进程、一个GPU进程、一个网络进程、多个渲染进程和多个插件进程。数据结构
2.进程是由一个或者多个线程组成的,线程是进程的基本单位。多线程
3.进程之间互相独立,有本身的资源,好比CPU的时间片,占用的内存。
4.一个进程下的线程(工人)共享进程(工厂)的资源,包括代码段、数据段、内存等。
5.线程之间协做在进程中完成任务。
6.进程是CPU资源分配的最小单位,是能够拥有资源和独立运行的最小单位。
7.线程是CPU调度的最小单位,由于进程能够有一个或者多个线程。
浏览器是多进程的,有一个主控进程,以及每个tab页面都会新开一个进程(某些状况下多个tab会合并进程,例如多个空白页等特殊状况);以如今主流的谷歌浏览器来举例,它主要包括如下几个进程
1.浏览器主进程:浏览器主进程,负责协调主控,只有一个
2.GPU进程:最多一个,用于3D绘制
3.插件进程:每使用一类插件都会建立一个进程
4.浏览器渲染进程(内核):默认每打开一个tab页,都会新建立一个。互不影响,控制页面渲染,脚本执行,事件处理等等。(有时候会优化,如多个空白页tab会合成一个)
tips:如图,打开任务管理器,打开三个tab页,就会建立三个进程。负责控制渲染本身的页面内容。那为何浏览器是多进程的?你下下,若是是单进程,某个tab页卡死或者崩溃了,就会影响到其它tab页
对于浏览器应用的这些进程,咱们只须要知道只有一个浏览器主进程,多个协做进程(GPU、渲染进程、网络进程、插件进程等等)。前端只须要重点关注渲染进程就能够了。由于渲染进程是渲染页面的。而后这个渲染进程,它是多线程的,若是还不明白进程和线程的关系,联想一下工厂和工厂的关系。回头继续看图理解一下进程和线程的关系。
GUI渲染线程主要负责渲染页面的,解析HTML、CSS,构建成一个DOM树和render树;当界面须要重绘或者重排引起回流时,这个线程会执行。注意,GUI渲染线程和JS引擎执行是互斥的,当js引擎执行时,GUI线程会被挂起,保存到一个任务队列中,等到JS引擎线程空闲时候才会出队被当即执行。若是不互斥,你一边用户操做一遍渲染界面,会致使渲染不许确等,这就决定了JS是单线程的了。总结为如下几点:
负责处理js脚本,解析脚本运行代码。可是js引擎会一直等待着任务队列的到来,而后加以处理。一样,GUI渲染线程和JS引擎执行是互斥的,若是js执行的时候太长,就会致使页面渲染不连贯,也就是阻塞页面,致使了页面渲染加载被阻塞了,这就是为何经常不把script标签放在头部而放在body底部下的缘由。总结为如下几点:
这个事件触发线程是归属浏览器的,而不是JS引擎的,能够这么理解,由于JS引擎是单线程的,它很忙,必需要有协助线程协助它完成一些事件,而这些事情,就是事件循环。
当JS引擎执行到一些网络请求,setTimeOut等异步的代码块时,会将这些异步的任务加入到事件线程中。当这些任务符合处理条件的时候,该线程会把任务加到待处理队列的队尾。等待JS引擎执行。(JS引擎为空时候才执行这个队列的任务)总结为如下几点:
传说中的 setInterval 与 setTimeout 所在线程,浏览器定时器不是由JS引擎计时的,你想呀,由于JS引擎是单线程的,若是执行这些计算器,阻塞页面彻底中止了,用户就不用点网页了。所以经过单独线程来计时并触发定时,计时完毕后,添加到事件队列中,等待JS引擎空闲后执行。总结为如下几点:
在 XMLHttpRequest 在链接后是经过浏览器新开一个线程请求,当检测到状态变动时,若是设置有回调函数,异步线程就产生状态变动事件,将这个回调再放入事件队列中。再由JS引擎执行。
tips:到这里,你应该知道了浏览器打开一个页面显示出来的全过程了,若是不清楚,须要再回头看看。上面总提到一个事件队列,它是基于哪一个线程的呢?答案是事件触发线程
browser主进程是一开始说浏览器的主进程,是控制协调其它进程的主进程。在打开一个Tab页的时候,就会建立一个主线程。经过主线程协调其它线程完成页面渲染。这里会涉及一些并发并行的操做系统知道点,就不展开扩展了,同时简单回顾下以前的整个流程。
Renderer进程的Renderer接口收到消息,简单解释后,交给渲染线程,而后开始渲染
渲染进程,即浏览器内核,才是咱们前端关注的重点,再次强调一下。而后咱们必须知道一下几点。
这个过程也很复杂,能够参考我以前的计算机网络(前端版)的问题,过多的细节不在这里描述。这里只作简单的描述
1.浏览器输入URL,浏览器主线程接管,开一个下载线程,已进行http请求。(忽略DNS等等)
2.拿到响应内容,将内容经过RendererHost接口转交给Renderer渲染进程
3.浏览器开始渲染(可能会协调GPU等线程协做完成)
1.解析HTML创建dom树
2.解析CSS构建render树,其实就是讲css解析成树的数据结构,而后结合dom树造成render树。
3.布局render树(Layout/reflow),复杂计算元素的大小、位置等信息
4.开始绘制render树(paint),绘制页面像素信息
5.浏览器会将绘制信息发送给GPU,GPU会将合成(composite),显示在屏幕上。
步骤并不详细,只是列个大概,更详细的请参考别的渲染文章,这里不进行深究。
tips:这也是为何页面渲染至少会触发一次回流的缘由。
这里参考一张图来梳理一下上面的步骤:
到此时,页面已是完成了初次渲染。
event lop其实就渲染进程里面的事件触发线程(线程里维护的队列),若是不记得了往上翻图回忆一下。它其实就是和JS引擎线程协做完成任务的。(界面的一些交换)
先从新温习一下,浏览器渲染进程的三个线程:
而后再提JS引擎主执行栈几个概念:
1.JS分同步任务和异步任务
2.同步任务都在主线程上执行,造成一个主执行栈
3.主线程以外,事件触发线程管理着一个任务队列(就是上文屡次提到的任务队列),只要异步任务有了运行结果,就在任务队列之中入队一个事件。
4.一旦主执行栈中的全部同步任务执行完毕(此时JS引擎空闲),系统就会读取任务队列,将可运行的异步任务添加到可执行栈中,开始执行,以此循环。
tips:看到这里,应该就能够理解为何有时候setTimeout推入的事件不能准时执行?由于可能在它推入到事件列表时,主线程还不空闲,正在执行其它代码, 因此天然有偏差。
浏览器原理仍是很复杂的,了解它的渲染原理,能够很好的帮助咱们优化性能。对于像Event lop事件循环机制、渲染原理等等具体的细节。能够参考大佬们的文章,我这里只作一个小扩展。
参考文章: