线程和进程进程和线程的概念可以这样理解:一个进程就是一个工厂,工厂有它独立的资源——工厂之间是相互独立的——线程是进程中的workerfactory,多个worker协作完成任务-factory中有一个或多个worker-worker之间共享空间factory中有多个worker,相当于一个进程可以有多个线程,线程共享空间的过程。进程是cpu资源分配的最小单位(是能够拥有资源并独立运行的最小单位,系统会为它分配内存)线程是cpu调试的最小单位(线程是程序的运行单位基于一个进程,一个进程中可以有多个线程。核心还是属于一个进程。)Browsersaremulti-process浏览器是多进程的。每打开一个标签页,就相当于创建了一个独立的浏览器进程。浏览器包含的进程:浏览器进程:浏览器的主进程(负责协调和总控),只有一个,其作用是:负责浏览器的界面显示,与用户进行交互,如forward、backward等,负责管理各个页面,创建和销毁其他进程将Rendered进程获取到的内存中的Bitmap绘制到用户界面进行网络资源管理,下载第三方插件进程:各类型插件对应一个进程,只有在使用插件时才会创建。GPU进程:最多一个,用于3D绘图等。浏览器渲染进程(browserkernel)(渲染进程,内部多线程):默认情况下,每个tab页有一个进程,互不影响。主要功能有:页面渲染、脚本执行、事件处理等。在浏览器中打开一个网页相当于启动了一个新的进程(进程自带多线程)。多进程浏览器的优点是避免单个页面崩溃影响整个浏览器。避免第三方插件崩溃影响整个浏览器。多进程充分利用了多核的优势。方便使用沙箱模型隔离插件等进程,提高浏览器稳定性。简单的理解就是:如果浏览器是单进程的,一个tab页崩溃了,会影响到整个浏览器,体验会很差。同样,如果是单进程,插件的崩溃也会影响到整个浏览器;当然,内存等资源的消耗也会更大,就像用空间换取时间一样。重点是浏览器内核(渲染进程)是普通前端操作最重要的渲染进程:页面渲染、js执行、事件循环等都在这个进程中执行;浏览器是多进程的,浏览器的渲染进程是多线程的;GUI渲染线程负责渲染浏览器界面,解析HTML、CSS,构建DOM树和RenderObject树,布局绘制等。当界面需要重绘或某些操作触发回流时,该线程会执行.注意GUI渲染线程和JS引擎线程是互斥的。当JS引擎执行时,GUI线程会被挂起(相当于冻结),GUI更新会保存在一个队列中,当JS引擎空闲时立即执行。JS引擎线程也称为JS内核,负责处理JavaScript脚本程序。(例如V8发动机)。JS引擎线程负责解析JavaScript脚本并运行代码。JS引擎一直在等待任务队列中任务的到来,然后进行处理。任何时候一个Tab页(render进程)中只有一个JS线程在运行JS程序。另外需要注意的是,GUI渲染线程和JS引擎线程是互斥的,所以如果JS执行时间过长,会导致页面渲染不连贯,导致页面渲染加载阻塞。事件触发线程属于浏览器而非JS引擎,用于控制事件循环(可以理解为JS引擎本身太忙,需要浏览器另开线程辅助)。当JS引擎执行setTimeout等代码块时(或者来自浏览器内核的其他线程,比如鼠标点击,AJAX异步请求等),它会把相应的任务添加到事件线程中。当对应的事件满足触发条件被触发时,线程会将事件添加到待处理队列的尾部,等待JS引擎处理。注意,由于JS的单线程关系,这些pending队列中的事件不得不排队等待JS引擎处理(JS引擎空闲时执行)。浏览器计时计数器不被JavaScript引擎统计(因为JavaScript引擎是单线程的,如果处于阻塞线程状态,会影响计时精度)所以通过一个单独的线程来计时并触发计时(计时结束后加入事件队列,等待JS引擎空闲)注意W3C在HTML标准中规定,setTimeout中低于4ms的时间间隔按4ms计算。当异步http请求线程在连接XMLHttpRequest后通过浏览器的新线程请求检测到状态变化时,如果设置了回调函数,异步线程会产生一个状态变化事件,将回调放入事件队列中,然后JavaScript引擎执行总结,渲染过程如下:浏览器主进程与浏览器内核(渲染进程)的通信过程打开浏览器,可以看到任务管理器中有两个进程(一个是主进程,另一个是开启Tab页面渲染过程);当浏览器主进程收到用户请求时,首先需要获取页面内容(如通过网络下载资源),然后通过RendererHost接口将任务交给Render渲染进程。Render渲染进程的Renderer接口接收到消息,简单解释后,将GUI交给渲染线程,然后开始渲染GUI。渲染线程接收请求、加载和渲染网页,这可能需要浏览器主进程获取资源和GPU进程帮助渲染。当然也可能有JS线程去操作DOM(这可能会造成回流和重绘)最后Render渲染进程将结果传递给Browser主进程。Browser主进程接收结果并绘制结果。浏览器内核中线程(渲染进程)GUI渲染线程与JS引擎线程交互的关系因为JavaScript可以操作DOM,如果在渲染界面的同时修改这些元素属性(即JS线程和GUI线程运行在同时),渲染线程前后获取的元素数据可能不一致。因此,为了防止不可预测的渲染结果,浏览器设置了互斥关系,当JS引擎执行时,GUI线程会被挂起。GUI更新将存储在一个队列中,并在JS引擎线程空闲时立即执行。JS阻塞页面加载从上面的互斥关系可以推导出,如果JS执行时间过长,就会阻塞页面。例如,假设JS引擎正在做大量的计算,即使此时更新了GUI,也会存储在队列中,待JS引擎空闲后执行。然后由于计算量巨大,JS引擎可能会闲置很久,肯定会感觉很卡。因此,尽量避免JS执行时间过长,导致页面渲染不连贯,造成页面渲染加载阻塞的感觉。css加载会不会阻塞dom树的渲染?这里说的是header中css的引入。首先,我们都知道css是通过一个单独的下载线程异步下载的。那么有几个现象:css加载不会阻塞DOM树解析(异步加载时dom照常构建)但是会阻塞rendertree渲染(渲染时需要等待css加载完成,因为rendertree需要css信息)这也可能是浏览器的一种优化机制,因为当你加载css的时候,可能会修改下面DOM节点的样式。如果css加载没有阻塞rendertree的渲染,那么css加载完成后,rendertree可能要重绘或者回流。这样就造成了一些不必要的损失,所以干脆先解析DOM树的结构,把能做的工作做完,等css加载完成后再按照最终的样式渲染render树。性能好一点。WebWorker,JS的多线程?上一篇文章提到,JS引擎是单线程的,JS执行时间过长会阻塞页面,那么对于CPU密集型计算,JS真的无能为力吗?因此,后来的HTML5中也支持了WebWorker。这样理解:在创建Worker时,JS引擎向浏览器申请开启一个子线程(子线程由浏览器开启,完全受主线程控制,无法操作DOM)。JS引擎线程以特定的方式与worker线程通信(postMessageAPI需要序列化对象才能与线程进行特定数据的交互)因此,如果你有非常耗时的工作,请单独开启一个Worker线程,这样无论多么的惊天动地,竟然不影响JS引擎的主线程。计算出结果后,将结果传递给主线程即可,完美!并且注意JS引擎是单线程的,这个本质没有变。Worker可以理解为浏览器为JS引擎打开的一个插件。来解决那些计算密集型的问题。WebWorker和SharedWorker都说到这里了,我们再提一下SharedWorker(以免以后混淆这两个概念)。WebWorker只属于某个页面,不会与其他页面的Render进程(浏览器内核进程)共享。所以Chrome在Render进程中(每个Tab页都是一个render进程),会创建一个新的线程来运行Worker中的JavaScript程序。SharedWorker被浏览器的所有页面共享。它不能像Worker那样实现,因为它不属于一个Render进程,可以被多个Render进程共享。因此,Chrome浏览器为SharedWorker创建了一个单独的进程来运行JavaScript程序,浏览器中每个相同的JavaScript都只有一个SharedWorker进程,无论它被创建多少次。看到这里应该很容易理解,本质上就是进程和线程的区别。SharedWorker由一个独立的进程管理。WebWorker只是渲染进程下的一个线程。总结浏览器渲染过程。浏览器输入url,浏览器主进程接管,开启一个下载线程,然后发起http请求(省略DNS查询,IP寻址等操作),然后等待响应,获取内容,然后传输通过RendererHost接口将内容传递给Render进程——浏览器渲染进程启动。浏览器内核获取到内容后,渲染大致可以分为:解析html创建dom解析css构建渲染树(将css代码解析成树状数据结构,然后与dom结合形成一个rendertree)布局渲染树(Layout/reflow),负责计算每个元素的大小和位置,绘制渲染树(paint),绘制页面像素信息,浏览器会将每一层的信息发送给GPU,GPU会合成每一层(composite)并显示在屏幕上。渲染完成后就是load事件,然后由自己的JS逻辑处理,具体步骤不再赘述。load事件和DOMContentLoaded事件的顺序上面说了,load事件会在渲染后触发,那么你能区分load事件和DOMContentLoaded事件的顺序吗?很简单,知道他们的定义就可以了:当DOMContentLoaded事件被触发时,只有当DOM被加载时,不包括样式表和图片。(比如有async加载的脚本,不一定就完成了)当onload事件被触发时,页面上所有的DOM、样式表、脚本、图片都已经加载完毕。(渲染完成)所以,顺序是:DOMContentLoaded->loadnormallayersandcompositelayers渲染步骤提到了复合的概念;浏览器渲染的图层一般包括两类:普通图层和合成图层。普通文档流可以理解为一个复合层(这里默认复合层,不管往里面加多少元素,其实都是在同一个复合层)绝对布局(fixed也是一样),虽然可以与文档流分离,但它仍然属于默认的复合层。可以通过硬件加速声明一个新的复合层,它会单独分配资源(当然也会脱离正常的文档流,这样不管复合层怎么变化,都不会影响回流重绘inthedefaultcompositelayer)可以简单理解为:在GPU中,每个合成层都是单独绘制的,所以不会互相影响,这就是为什么有些场景硬件加速效果很好,如何成为一个合成层(Hardwareacceleration)把一个元素变成复合层是传说中的硬件加速技术最常用的方法:translate3d,translatezopacity属性/过渡动画(动画执行过程中会创建复合层,动画不开始也不结束之后元素会回到之前的状态)will-chang属性(这个比较遥远),一般配合opacity和translate一起使用(经测试,除了上述属性会引起硬件加速外,其他属性不会变成复合层),作用是提前告诉浏览器改变,让浏览器开始做一些优化工作(这个最好用完再放)
