浏览器进程

浏览器都包含哪些进程?

浏览器是多进程css

一、Browser进程:浏览器的主进程(负责协调、主控),只有一个。做用有:html

  • 负责浏览器界面显示,与用户交互。如前进,后退等前端

  • 负责各个页面的管理,建立和销毁其余进程java

  • 将Renderer进程获得内存中的Bitmap,绘制到用户界面上node

  • 网络资源的管理,下载等web

二、第三方插件进程:每种类型的插件对应一个进程,仅当使用该插件时才建立ajax

三、GPU进程:最多一个,用于3D绘制等canvas

四、浏览器渲染进程(浏览器内核)(Renderer进程,内部是多线程的):默认每一个Tab页面一个进程,互不影响。主要做用为页面渲染,脚本执行,事件处理等segmentfault

强化记忆:在浏览器中打开一个网页至关于新起了一个进程(进程内有本身的多线程)固然,浏览器有时会将多个进程合并(譬如打开多个空白标签页后,会发现多个空白标签页被合并成了一个进程)api

浏览器多进程的优点?

相比于单进程浏览器,多进程有以下优势:

  • 避免单个page crash(页面崩溃)影响整个浏览器

  • 避免第三方插件crash影响整个浏览器

  • 多进程充分利用多核优点

  • 方便使用沙盒模型隔离插件等进程,提升浏览器稳定性

简单点理解:若是浏览器是单进程,那么某个Tab页崩溃了,就影响了整个浏览器,体验有多差;同理若是是单进程,插件崩溃了也会影响整个浏览器;并且多进程还有其它的诸多优点。。。

固然,内存等资源消耗也会更大,有点空间换时间的意思

重点是浏览器内核(渲染进程)

重点来了,咱们能够看到,上面提到了这么多的进程,那么,对于普通的前端操做来讲,最终要的是什么呢?答案是渲染进程

能够这样理解,页面的渲染,JS的执行,事件的循环,都在这个进程内进行。接下来重点分析这个进程

请牢记,浏览器的渲染进程是多线程

终于到了线程这个概念了😭,好亲切。那么接下来看看它都包含了哪些线程(列举一些主要常驻线程):

GUI渲染线程

  • 负责渲染浏览器界面,解析HTML,CSS,构建DOM树和RenderObject树,布局和绘制等。

  • 当界面须要重绘(Repaint)或因为某种操做引起回流(reflow)时,该线程就会执行

  • 注意,GUI渲染线程与JS引擎线程是互斥的,当JS引擎执行时GUI线程会被挂起(至关于被冻结了),GUI更新会被保存在一个队列中等到JS引擎空闲时当即被执行。

JS引擎线程

  • 也称为JS内核,负责处理Javascript脚本程序。(例如V8引擎)

  • JS引擎线程负责解析Javascript脚本,运行代码。

  • JS引擎一直等待着任务队列中任务的到来,而后加以处理,一个Tab页(renderer进程)中不管何时都只有一个JS线程在运行JS程序

  • 一样注意,GUI渲染线程与JS引擎线程是互斥的,因此若是JS执行的时间过长,这样就会形成页面的渲染不连贯,致使页面渲染加载阻塞。

事件触发线程

  • 归属于浏览器而不是JS引擎,用来控制事件循环(能够理解,JS引擎本身都忙不过来,须要浏览器另开线程协助)

  • 当JS引擎执行代码块如setTimeOut时(也可来自浏览器内核的其余线程,如鼠标点击、AJAX异步请求等),会将对应任务添加到事件线程中

  • 当对应的事件符合触发条件被触发时,该线程会把事件添加到待处理队列的队尾,等待JS引擎的处理

  • 注意,因为JS的单线程关系,因此这些待处理队列中的事件都得排队等待JS引擎处理(当JS引擎空闲时才会去执行)

  • js为何是单线程,javaScript 的主要用途是与用户互动,以及操做 DOM。这决定了它只能是单线程,不然会带来很复杂的同步问题。好比,假定JavaScript 同时有两个线程,一个线程在某个 DOM 节点上添加内容,另外一个线程删除了这个节点,这时浏览器应该以哪一个线程为准?

定时触发器线程

  • 传说中的setInterval与setTimeout所在线程

  • 浏览器定时计数器并非由JavaScript引擎计数的,(由于JavaScript引擎是单线程的, 若是处于阻塞线程状态就会影响记计时的准确)

  • 所以经过单独线程来计时并触发定时(计时完毕后,添加到事件队列中,等待JS引擎空闲后执行)

  • 注意,W3C在HTML标准中规定,规定要求setTimeout中低于4ms的时间间隔算为4ms

异步http请求线程

  • 在XMLHttpRequest在链接后是经过浏览器新开一个线程请求

  • 将检测到状态变动时,若是设置有回调函数,异步线程就产生状态变动事件,将这个回调再放入事件队列中。再由JavaScript引擎执行。

看到这里,若是以为累了,能够先休息下,这些概念须要被消化,毕竟后续将提到的事件循环机制就是基于事件触发线程的,因此若是仅仅是看某个碎片化知识,可能会有一种似懂非懂的感受。要完成的梳理一遍才能快速沉淀,不易遗忘。放张图巩固下吧:

Browser进程(总指挥)和浏览器内核(Renderer进程(劳模))的通讯过程

看到这里,首先,应该对浏览器内的进程和线程都有必定理解了,那么接下来,再谈谈浏览器的Browser进程(控制进程)是如何和内核通讯的,
这点也理解后,就能够将这部分的知识串联起来,从头至尾有一个完整的概念。

若是本身打开任务管理器,而后打开一个浏览器,就能够看到:任务管理器中出现了两个进程(一个是主控进程,一个则是打开Tab页的渲染进程),
而后在这前提下,看下整个的过程:(简化了不少)

  • Browser进程收到用户请求,首先须要获取页面内容(譬如经过网络下载资源),随后将该任务经过RendererHost接口传递给Render进程

  • Renderer进程的Renderer接口收到消息,简单解释后,交给渲染线程,而后开始渲染

  • 渲染线程接收请求,加载网页并渲染网页,这其中可能须要Browser进程获取资源和须要GPU进程来帮助渲染

  • 固然可能会有JS线程操做DOM(这样可能会形成回流并重绘)

  • 最后Render进程将结果传递给Browser进程

  • Browser进程接收到结果并将结果绘制出来

这里绘一张简单的图:

梳理浏览器内核中线程之间的关系

到了这里,已经对浏览器的运行有了一个总体的概念,接下来,先简单梳理一些概念

GUI渲染线程与JS引擎线程互斥

因为JavaScript是可操纵DOM的,若是在修改这些元素属性同时渲染界面(即JS线程和UI线程同时运行),那么渲染线程先后得到的元素数据就可能不一致了。

所以为了防止渲染出现不可预期的结果,浏览器设置GUI渲染线程与JS引擎为互斥的关系,当JS引擎执行时GUI线程会被挂起,
GUI更新则会被保存在一个队列中等到JS引擎线程空闲时当即被执行。

JS阻塞页面加载

从上述的互斥关系,能够推导出,JS若是执行时间过长就会阻塞页面。

譬如,假设JS引擎正在进行巨量的计算,此时就算GUI有更新,也会被保存到队列中,等待JS引擎空闲后执行。而后,因为巨量计算,因此JS引擎极可能好久好久后才能空闲,天然会感受到巨卡无比。

因此,要尽可能避免JS执行时间过长,这样就会形成页面的渲染不连贯,致使页面渲染加载阻塞的感受。

WebWorker,JS的多线程?

前文中有提到JS引擎是单线程的,并且JS执行时间过长会阻塞页面,那么JS就真的对cpu密集型计算无能为力么?

因此,后来HTML5中支持了Web Worker

MDN的官方解释是:

WebWorker为Web内容在后台线程中运行脚本提供了一种简单的方法。线程能够执行任务而不干扰用户界面一个worker是使用一个构造函数建立的一个对象(e.g.Worker())运行一个命名的JavaScript文件这个文件包含将在工做线程中运行的代码;workers运行在另外一个全局上下文中,不一样于当前的window所以,使用window快捷方式获取当前全局的范围(而不是self)在一个Worker内将返回错误

这样理解下:

  • 建立Worker时,JS引擎向浏览器申请开一个子线程(子线程是浏览器开的,彻底受主线程控制,并且不能操做DOM)

  • JS引擎线程与worker线程间经过特定的方式通讯(postMessage API,须要经过序列化对象来与线程交互特定的数据)

因此,若是有很是耗时的工做,请单独开一个Worker线程,这样里面无论如何翻天覆地都不会影响JS引擎主线程,只待计算出结果后,将结果通讯给主线程便可,perfect!

并且注意下,JS引擎是单线程的,这一点的本质仍然未改变,Worker能够理解是浏览器给JS引擎开的外挂,专门用来解决那些大量计算问题。

其它,关于Worker的详解就不是本文的范畴了,所以再也不赘述。

WebWorker与SharedWorker

既然都到了这里,就再提一下SharedWorker(避免后续将这两个概念搞混)

  • WebWorker只属于某个页面,不会和其余页面的Render进程(浏览器内核进程)共享

  • 因此Chrome在Render进程中(每个Tab页就是一个render进程)建立一个新的线程来运行Worker中的JavaScript程序。

  • SharedWorker是浏览器全部页面共享的,不能采用与Worker一样的方式实现,由于它不隶属于某个Render进程,能够为多个Render进程共享使用

  • 因此Chrome浏览器为SharedWorker单首创建一个进程来运行JavaScript程序,在浏览器中每一个相同的JavaScript只存在一个SharedWorker进程,无论它被建立多少次。

看到这里,应该就很容易明白了,本质上就是进程和线程的区别。SharedWorker由独立的进程管理,WebWorker只是属于render进程下的一个线程

简单梳理下浏览器渲染流程

原本是直接计划开始谈JS运行机制的,但想了想,既然上述都一直在谈浏览器,直接跳到JS可能再突兀,所以,中间再补充下浏览器的渲染流程(简单版本)

为了简化理解,前期工做直接省略成:(要展开的或彻底能够写另外一篇超长文)

浏览器输入url,浏览器主进程接管,开一个下载线程,而后进行http请求(略去DNS查询,IP寻址等等操做),而后等待响应,获取内容,随后将内容经过RendererHost接口转交给Renderer进程-浏览器渲染流程开始

浏览器器内核拿到内容后,渲染大概能够划分红如下几个步骤:

  1. 解析html创建dom树

  2. 解析css构建render树(将CSS代码解析成树形的数据结构,而后结合DOM合并成render树)

  3. 布局render树(Layout/reflow),负责各元素尺寸、位置的计算

  4. 绘制render树(paint),绘制页面像素信息

  5. 浏览器会将各层的信息发送给GPU,GPU会将各层合成(composite),显示在屏幕上。

全部详细步骤都已经略去,渲染完毕后就是load事件了,以后就是本身的JS逻辑处理了。既然略去了一些详细的步骤,那么就提一些可能须要注意的细节把。

这里重绘参考来源中的一张图:(参考来源第一篇)

load事件与DOMContentLoaded事件的前后

上面提到,渲染完毕后会触发load事件,那么你能分清楚load事件与DOMContentLoaded事件的前后么?

很简单,知道它们的定义就能够了:

  • 当 DOMContentLoaded 事件触发时,仅当DOM加载完成,不包括样式表,图片。
    (譬如若是有async加载的脚本就不必定完成)

  • 当 onload 事件触发时,页面上全部的DOM,样式表,脚本,图片都已经加载完成了。(渲染完毕了)

因此,顺序是:DOMContentLoaded -> load

 

css加载是否会阻塞dom树渲染?

这里说的是头部引入css的状况

首先,咱们都知道:css是由单独的下载线程异步下载的。

而后再说下几个现象:

  • css加载不会阻塞DOM树解析(异步加载时DOM照常构建)

  • 但会阻塞render树渲染(渲染时需等css加载完毕,由于render树须要css信息)

这可能也是浏览器的一种优化机制。

由于你加载css的时候,可能会修改下面DOM节点的样式,若是css加载不阻塞render树渲染的话,那么当css加载完以后,render树可能又得从新重绘或者回流了,这就形成了一些没有必要的损耗。

因此干脆就先把DOM树的结构先解析完,把能够作的工做作完,而后等你css加载完以后,在根据最终的样式来渲染render树,这种作法性能方面确实会比较好一点。

普通图层和复合图层

渲染步骤中就提到了composite概念。

能够简单的这样理解,浏览器渲染的图层通常包含两大类:普通图层以及复合图层。

首先,普通文档流内能够理解为一个复合图层(这里称为默认复合层,里面无论添加多少元素,其实都是在同一个复合图层中)

其次,absolute布局(fixed也同样),虽然能够脱离普通文档流,但它仍然属于默认复合层。

而后,能够经过硬件加速的方式,声明一个新的复合图层,它会单独分配资源
(固然也会脱离普通文档流,这样一来,无论这个复合图层中怎么变化,也不会影响默认复合层里的回流重绘)

能够简单理解下:GPU中,各个复合图层是单独绘制的,因此互不影响,这也是为何某些场景硬件加速效果一级棒

如何变成复合图层(硬件加速)

将该元素变成一个复合图层,就是传说中的硬件加速技术

  • 最经常使用的方式:translate3dtranslateZ

  • opacity属性/过渡动画(须要动画执行的过程当中才会建立合成层,动画没有开始或结束后元素还会回到以前的状态)

  • will-chang属性(这个比较偏僻),通常配合opacity与translate使用(并且经测试,除了上述能够引起硬件加速的属性外,其它属性并不会变成复合层),做用是提早告诉浏览器要变化,这样浏览器会开始作一些优化工做(这个最好用完后就释放)

  • <video><iframe><canvas><webgl>等元素

  • 其它,譬如之前的flash插件

absolute和硬件加速的区别

能够看到,absolute虽然能够脱离普通文档流,可是没法脱离默认复合层。
因此,就算absolute中信息改变时不会改变普通文档流中render树,可是,浏览器最终绘制时,是整个复合层绘制的,因此absolute中信息的改变,仍然会影响整个复合层的绘制。

(浏览器会重绘它,若是复合层中内容多,absolute带来的绘制信息变化过大,资源消耗是很是严重的)

而硬件加速直接就是在另外一个复合层了(另起炉灶),因此它的信息改变不会影响默认复合层(固然了,内部确定会影响属于本身的复合层),仅仅是引起最后的合成(输出视图)

复合图层的做用?

通常一个元素开启硬件加速后会变成复合图层,能够独立于普通文档流中,改动后能够避免整个页面重绘,提高性能

可是尽可能不要大量使用复合图层,不然因为资源消耗过分,页面反而会变的更卡

硬件加速时请使用index

使用硬件加速时,尽量的使用index,防止浏览器默认给后续的元素建立复合层渲染

具体的原理时这样的:
webkit CSS3中,若是这个元素添加了硬件加速,而且index层级比较低,
那么在这个元素的后面其它元素(层级比这个元素高的,或者相同的,而且releative或absolute属性相同的),会默认变为复合层渲染,若是处理不当会极大的影响性能

简单点理解,其实能够认为是一个隐式合成的概念:若是a是一个复合图层,并且b在a上面,那么b也会被隐式转为一个复合图层,这点须要特别注意

另外,这个问题能够在这个地址看到重现(原做者分析的挺到位的,直接上连接):http://web.jobbole.com/83575/

从Event Loop谈JS的运行机制

到此时,已是属于浏览器页面初次渲染完毕后的事情,JS引擎的一些运行机制分析。

注意,这里不谈可执行上下文,VO,scop chain等概念(这些彻底能够整理成另外一篇文章了),这里主要是结合Event Loop来谈JS代码是如何执行的。

读这部分的前提是已经知道了JS引擎是单线程,并且这里会用到上文中的几个概念:(若是不是很理解,能够回头温习)

  • JS引擎线程

  • 事件触发线程

  • 定时触发器线程

而后再理解一个概念:

    • JS分为同步任务和异步任务

    • 同步任务都在主线程上执行,造成一个执行栈

    • 主线程以外,事件触发线程管理着一个任务队列,只要异步任务有了运行结果,就在任务队列之中放置一个事件。

    • 一旦执行栈中的全部同步任务执行完毕(此时JS引擎空闲),系统就会读取任务队列,将可运行的异步任务添加到可执行栈中,开始执行。

 

 

 

为何有时候setTimeout推入的事件不能准时执行?由于可能在它推入到事件列表时,主线程还不空闲,正在执行其它代码,因此天然有偏差。

事件循环机制进一步补充

这里就直接引用一张图片来协助理解:(参考自Philip Roberts的演讲《Help, I’m stuck in an event-loop》)

 

  • 主线程运行时会产生执行栈,栈中的代码调用某些api时,它们会在事件队列中添加各类事件(当知足触发条件后,如ajax请求完毕)

  • 而栈中的代码执行完毕,就会读取事件队列中的事件,去执行那些回调

  • 如此循环

  • 注意,老是要等待栈中的代码执行完毕后才会去读取事件队列中的事件

  • 单独说说定时器

    上述事件循环机制的核心是:JS引擎线程和事件触发线程

    但事件上,里面还有一些隐藏细节,譬如调用setTimeout后,是如何等待特定时间后才添加到事件队列中的?

    是JS引擎检测的么?固然不是了。它是由定时器线程控制(由于JS引擎本身都忙不过来,根本无暇分身)

    为何要单独的定时器线程?由于JavaScript引擎是单线程的, 若是处于阻塞线程状态就会影响记计时的准确,所以颇有必要单独开一个线程用来计时。

    何时会用到定时器线程?当使用setTimeoutsetInterval,它须要定时器线程计时,计时完成后就会将特定的事件推入事件队列中。

    譬如:

    setTimeout(function(){console.log('hello!');},1000);

    这段代码的做用是当1000毫秒计时完毕后(由定时器线程计时),将回调函数推入事件队列中,等待主线程执行

    setTimeout(function(){console.log('hello!');},0);console.log('begin');

    这段代码的效果是最快的时间内将回调函数推入事件队列中,等待主线程执行

    注意:

    • 执行结果是:先beginhello!

    • 虽然代码的本意是0毫秒后就推入事件队列,可是W3C在HTML标准中规定,规定要求setTimeout中低于4ms的时间间隔算为4ms。
      (不过也有一说是不一样浏览器有不一样的最小时间设定)

    • 就算不等待4ms,就算假设0毫秒就推入事件队列,也会先执行begin(由于只有可执行栈内空了后才会主动读取事件队列)

    setTimeout而不是setInterval

    用setTimeout模拟按期计时和直接用setInterval是有区别的。

    由于每次setTimeout计时到后就会去执行,而后执行一段时间后才会继续setTimeout,中间就多了偏差(偏差多少与代码执行时间有关)

    而setInterval则是每次都精确的隔一段时间推入一个事件
    (可是,事件的实际执行时间不必定就准确,还有多是这个事件还没执行完毕,下一个事件就来了)

    并且setInterval有一些比较致命的问题就是:

    • 累计效应(上面提到的),若是setInterval代码在(setInterval)再次添加到队列以前尚未完成执行,就会致使定时器代码连续运行好几回,而之间没有间隔。就算正常间隔执行,多个setInterval的代码执行时间可能会比预期小(由于代码执行须要必定时间)

    • 譬如像iOS的webview,或者Safari等浏览器中都有一个特色,在滚动的时候是不执行JS的,若是使用了setInterval,会发如今滚动结束后会执行屡次因为滚动不执行JS积攒回调,若是回调执行时间过长,就会很是容器形成卡顿问题和一些不可知的错误(这一块后续有补充,setInterval自带的优化,不会重复添加回调)

    • 并且把浏览器最小化显示等操做时,setInterval并非不执行程序,它会把setInterval的回调函数放在队列中,等浏览器窗口再次打开时,一瞬间所有执行时

    因此,鉴于这么多但问题,目前通常认为的最佳方案是:用setTimeout模拟setInterval,或者特殊场合直接用requestAnimationFrame

    补充:JS高程中有提到,JS引擎会对setInterval进行优化,若是当前事件队列中有setInterval的回调,不会重复添加。不过,仍然是有不少问题。。。

    事件循环进阶:macrotask与microtask

    这段参考了参考来源中的第2篇文章(英文版的),(加了下本身的理解从新描述了下),
    强烈推荐有英文基础的同窗直接观看原文,做者描述的很清晰,示例也很不错,以下:

    https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/

    上文中将JS事件循环机制梳理了一遍,在ES5的状况是够用了,可是在ES6盛行的如今,仍然会遇到一些问题,譬以下面这题:

    console.log('script start');setTimeout(function(){console.log('setTimeout');},0);Promise.resolve().then(function(){console.log('promise1');}).then(function(){console.log('promise2');});console.log('script end');

    嗯哼,它的正确执行顺序是这样子的:

    scriptstartscriptendpromise1promise2setTimeout

    为何呢?由于Promise里有了一个一个新的概念:microtask

    或者,进一步,JS中分为两种任务类型:macrotaskmicrotask,在ECMAScript中,microtask称为jobs,macrotask可称为task

    它们的定义?区别?简单点能够按以下理解:

    • macrotask(又称之为宏任务),能够理解是每次执行栈执行的代码就是一个宏任务(包括每次从事件队列中获取一个事件回调并放到执行栈中执行)

    • 每个task会从头至尾将这个任务执行完毕,不会执行其它

    • 浏览器为了可以使得JS内部task与DOM任务可以有序的执行,会在一个task执行结束后,在下一个 task 执行开始前,对页面进行从新渲染
      task->渲染->task->...

    • microtask(又称为微任务),能够理解是在当前 task 执行结束后当即执行的任务

    • 也就是说,在当前task任务后,下一个task以前,在渲染以前

    • 因此它的响应速度相比setTimeout(setTimeout是task)会更快,由于无需等渲染

    • 也就是说,在某一个macrotask执行完后,就会将在它执行期间产生的全部microtask都执行完毕(在渲染前)

    分别很么样的场景会造成macrotask和microtask呢?

    • macrotask:主代码块,setTimeout,setInterval等(能够看到,事件队列中的每个事件都是一个macrotask)

    • microtask:Promise,process.nextTick等

    补充:在node环境下,process.nextTick的优先级高于Promise,也就是能够简单理解为:在宏任务结束后会先执行微任务队列中的nextTickQueue部分,而后才会执行微任务中的Promise部分。

    另外,setImmediate则是规定:在下一次Event Loop(宏任务)时触发(因此它是属于优先级较高的宏任务),(Node.js文档中称,setImmediate指定的回调函数,老是排在setTimeout前面),因此setImmediate若是嵌套的话,是须要通过多个Loop才能完成的,而不会像process.nextTick同样没完没了。

    参考:https://segmentfault.com/q/1010000011914016

    再根据线程来理解下:

    • macrotask中的事件都是放在一个事件队列中的,而这个队列由事件触发线程维护

    • microtask中的全部微任务都是添加到微任务队列(Job Queues)中,等待当前macrotask执行完毕后执行,而这个队列由JS引擎线程维护
      (这点由本身理解+推测得出,由于它是在主线程下无缝执行的)

    因此,总结下运行机制:

    • 执行一个宏任务(栈中没有就从事件队列中获取)

    • 执行过程当中若是遇到微任务,就将它添加到微任务的任务队列中

    • 宏任务执行完毕后,当即执行当前微任务队列中的全部微任务(依次执行)

    • 当前宏任务执行完毕,开始检查渲染,而后GUI线程接管渲染

    • 渲染完毕后,JS线程继续接管,开始下一个宏任务(从事件队列中获取)

       

    如图:

  • 另外,请注意下Promisepolyfill与官方版本的区别:

    • 官方版本中,是标准的microtask形式

    • polyfill,通常都是经过setTimeout模拟的,因此是macrotask形式

    • 请特别注意这两点区别

    注意,有一些浏览器执行结果不同(由于它们可能把microtask当成macrotask来执行了),可是为了简单,这里不描述一些不标准的浏览器下的场景(但记住,有些浏览器可能并不标准)

    MutationObserver能够用来实现microtask(它属于microtask,优先级小于Promise,通常是Promise不支持时才会这样作)

    它是HTML5中的新特性,做用是:监听一个DOM变更,当DOM对象树发生任何变更时,Mutation Observer会获得通知

    像之前的Vue源码中就是利用它来模拟nextTick的,具体原理是,建立一个TextNode并监听内容变化,而后要nextTick的时候去改一下这个节点的文本内容,以下:(Vue的源码,未修改)

    var counter=1
    var observer=newMutationObserver(nextTickHandler)
    var textNode=document.createTextNode(String(counter))
    observer.observe(textNode,{characterData:true})
    timerFunc=()=>{
       counter=(counter+1)%2
       textNode.data=String(counter)
    }

    对应Vue源码连接

    不过,如今的Vue(2.5+)的nextTick实现移除了MutationObserver的方式(听说是兼容性缘由),取而代之的是使用MessageChannel(固然,默认状况仍然是Promise,不支持才兼容的)。

    MessageChannel属于宏任务,优先级是:setImmediate->MessageChannel->setTimeout,因此Vue(2.5+)内部的nextTick与2.4及以前的实现是不同的,须要注意下。

    这里不展开,能够看下https://juejin.im/post/5a1af88f5188254a701ec230

相关文章
相关标签/搜索