<p>浅析 <strong><a href="http://www.js-code.com/tag/vue" title="Vue" target="_blank">Vue</a> 2.6</strong> 中的 <strong>nextTick</strong> 方法。</p> <h1 id="articleHeader0">事件循环</h1> <p>JS 的 <strong>事件循环</strong> 和 <strong>任务队列</strong> 其实是理解 nextTick 概念的关键。<br />这个<a href="https://juejin.im/entry/59082301a22b9d0065f1a186" rel="nofollow noreferrer" target="_blank">网上</a>其实有很多优质的文章做了详细介绍,我就简单过过了。</p> <p>以下内容适用于浏览器端 JS,NodeJS 的事件循环机制并不相同。</p> <p><a href="https://html.spec.whatwg.org/multipage/webappapis.html#event-loops" rel="nofollow noreferrer" target="_blank">规范</a>中规定 task 分为两大类: <code>task(macrotask)</code> 和 <code>microtask</code>。</p> <p>通常认为是 <code>task</code> 的任务源:</p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="setTimeout / setInterval setImmediate MessageChannel I/O UI rendering" title="" data-original-title="复制"></span> </div> </p></div> <pre class="bash hljs"><code class="bash">setTimeout / setInterval setImmediate MessageChannel I/O UI rendering</code></pre> <p>通常认为是 <code>microtask</code> 的任务源:</p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="Promise process.nextTick MutationObserver Object.observe(已废弃)" title="" data-original-title="复制"></span> </div> </p></div> <pre class="bash hljs"><code class="bash"><a href="http://www.js-code.com/tag/promise" title="浏览关于“Promise”的文章" target="_blank" class="tag_link">Promise</a> process.nextTick MutationObserver Object.observe(已废弃)</code></pre> <p>简单概况:<a href="https://html.spec.whatwg.org/multipage/webappapis.html#event-loop-processing-model" rel="nofollow noreferrer" target="_blank">(这里是官方规范)</a></p> <ol> <li>首先开始<strong>执行 script 脚本</strong>,直到执行上下文栈为空时,开始清空 <strong>microtask 队列</strong>里的任务,队列嘛,先入先出,出一个执行一个,清空完毕,走事件循环。</li> <li> <strong>事件循环</strong>:不断地去取 <strong>task 队列</strong>的中的一个任务推入栈中执行,并在当次循环里依次清空 <strong>microtask 队列</strong>里的任务,清空之后,可能会触发页面更新渲染(由浏览器决定)。</li> <li>之后重复 <strong>事件循环</strong> 步骤。</li> </ol> <h1 id="articleHeader1">nextTick</h1> <p><a href="http://www.js-code.com/tag/vue" title="Vue" target="_blank">Vue</a> 中数据的变化到 <a href="http://www.js-code.com/tag/dom" title="DOM" target="_blank">DOM</a> 的更新渲染是一个异步过程。<br />此方法便用于在 <a href="http://www.js-code.com/tag/dom" title="DOM" target="_blank">DOM</a> 更新循环结束之后执行延迟回调。<br />使用方法很简单:</p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="// 修改数据 vm.msg = 'Hello'; // DOM 还没有更新 Vue.nextTick(function() { // DOM 更新了 }); // 作为一个 Promise 使用 Vue.nextTick().then(function() { // DOM 更新了 });" title="" data-original-title="复制"></span> </div> </p></div> <pre class="javascript hljs"><code class="js"><span class="hljs-comment">// 修改数据</span> vm.msg = <span class="hljs-string">'Hello'</span>; <span class="hljs-comment">// <a href="http://www.js-code.com/tag/dom" title="浏览关于“DOM”的文章" target="_blank" class="tag_link">DOM</a> 还没有更新</span> <a href="http://www.js-code.com/tag/vue" title="浏览关于“Vue”的文章" target="_blank" class="tag_link">Vue</a>.nextTick(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{ <span class="hljs-comment">// DOM 更新了</span> }); <span class="hljs-comment">// 作为一个 <a href="http://www.js-code.com/tag/promis" title="浏览关于“Promis”的文章" target="_blank" class="tag_link">Promis</a>e 使用</span> Vue.nextTick().then(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{ <span class="hljs-comment">// DOM 更新了</span> });</code></pre> <p><a href="https://github.com/vuejs/vue/blob/v2.6.7/src/core/util/next-tick.js" rel="nofollow noreferrer" target="_blank">源码</a> 去除注释,其实也只有不到一百来行,整体还是很容易理解的。</p> <p>这里划成 3 个部分介绍。</p> <h2 id="articleHeader2">模块变量</h2> <p>介绍 引入的模块 和 定义的变量。</p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="// noop 空函数,可用作函数占位符 import { noop } from 'shared/util'; // Vue 内部的错误处理函数 import { handleError } from './error'; // 判断是IE/IOS/内置函数 import { isIE, isIOS, isNative } from './env'; // 使用 MicroTask 的标识符 <a href="http://www.js-code.com/tag/export" title="export" target="_blank">export</a> <a href="http://www.js-code.com/tag/let" title="let" target="_blank">let</a> isUsingMicroTask = false;</p> <p>// 以<a href="http://www.js-code.com/tag/%e6%95%b0%e7%bb%84" title="数组" target="_blank">数组</a>形式存储执行的函数<br /> <a href="http://www.js-code.com/tag/const" title="const" target="_blank">const</a> callbacks = [];</p> <p>// nextTick 执行状态<br /> <a href="http://www.js-code.com/tag/let" title="let" target="_blank">let</a> pending = false;</p> <p>// 遍历函数<a href="http://www.js-code.com/tag/%e6%95%b0%e7%bb%84" title="数组" target="_blank">数组</a>执行每一项函数<br /> function flushCallbacks() {<br /> pending = false;<br /> <a href="http://www.js-code.com/tag/const" title="const" target="_blank">const</a> copies = callbacks.<a href="http://www.js-code.com/tag/slice" title="slice" target="_blank">slice</a>(0);<br /> callbacks.length = 0;<br /> for (<a href="http://www.js-code.com/tag/let" title="浏览关于“let”的文章" target="_blank" class="tag_link">let</a> i = 0; i < copies.length; i++) { copies[i](); } }" title="" data-original-title="复制"></span> </div> </p></div> <pre class="javascript hljs"><code class="js"><span class="hljs-comment">// noop 空函数,可用作函数占位符</span> <span class="hljs-keyword">import</span> { noop } <span class="hljs-keyword">from</span> <span class="hljs-string">'shared/util'</span>; <span class="hljs-comment">// Vue 内部的错误处理函数</span> <span class="hljs-keyword">import</span> { handleError } <span class="hljs-keyword">from</span> <span class="hljs-string">'./error'</span>; <span class="hljs-comment">// 判断是IE/IOS/内置函数</span> <span class="hljs-keyword">import</span> { isIE, isIOS, isNative } <span class="hljs-keyword">from</span> <span class="hljs-string">'./env'</span>; <span class="hljs-comment">// 使用 MicroTask 的标识符</span> <span class="hljs-keyword"><a href="http://www.js-code.com/tag/export" title="浏览关于“export”的文章" target="_blank" class="tag_link">export</a></span> <span class="hljs-keyword">let</span> isUsingMicroTask = <span class="hljs-literal">false</span>; <span class="hljs-comment">// 以<a href="http://www.js-code.com/tag/%e6%95%b0%e7%bb%84" title="浏览关于“数组”的文章" target="_blank" class="tag_link">数组</a>形式存储执行的函数</span> <span class="hljs-keyword"><a href="http://www.js-code.com/tag/const" title="浏览关于“const”的文章" target="_blank" class="tag_link">const</a></span> callbacks = []; <span class="hljs-comment">// nextTick 执行状态</span> <span class="hljs-keyword">let</span> pending = <span class="hljs-literal">false</span>; <span class="hljs-comment">// 遍历函数数组执行每一项函数</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">flushCallbacks</span>(<span class="hljs-params"></span>) </span>{ pending = <span class="hljs-literal">false</span>; <span class="hljs-keyword">const</span> copies = callbacks.<a href="http://www.js-code.com/tag/slice" title="浏览关于“slice”的文章" target="_blank" class="tag_link">slice</a>(<span class="hljs-number">0</span>); callbacks.length = <span class="hljs-number">0</span>; <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; copies.length; i++) { copies[i](); } }</code></pre> <h2 id="articleHeader3">异步延迟函数</h2> <p>接下来是核心的 <strong>异步延迟函数</strong>。这里不同的 Vue 版本采用的策略其实并不相同。</p> <p><strong>2.6</strong> 版本优先使用 <strong>microtask</strong> 作为异步延迟包装器。</p> <p><strong>2.5</strong> 版本则是 <strong>macrotask 结合 microtask</strong>。然而,在重绘之前状态改变时会有小问题(如 <a href="https://github.com/vuejs/vue/issues/6813" rel="nofollow noreferrer" target="_blank">#6813</a>)。此外,在事件处理程序中使用 <strong>macrotask</strong> 会导致一些无法规避的奇怪行为(如<a href="https://github.com/vuejs/vue/issues/7109" rel="nofollow noreferrer" target="_blank">#7109</a>,<a href="https://github.com/vuejs/vue/issues/7153" rel="nofollow noreferrer" target="_blank">#7153</a>,<a href="https://github.com/vuejs/vue/issues/7546" rel="nofollow noreferrer" target="_blank">#7546</a>,<a href="https://github.com/vuejs/vue/issues/7834" rel="nofollow noreferrer" target="_blank">#7834</a>,<a href="https://github.com/vuejs/vue/issues/8109" rel="nofollow noreferrer" target="_blank">#8109</a>)。</p> <p>所以 <strong>2.6</strong> 版本现在又改用 <strong>microtask</strong> 了,为什么是又呢。。因为 <strong>2.4</strong> 版本及之前也是用的 <strong>microtask</strong>。。。</p> <div class="google-auto-placed ap_container" style="text-align: center; width: 100%; height: auto; clear: none;"><ins data-ad-format="auto" class="adsbygoogle adsbygoogle-noablate" data-ad-client="ca-pub-6330872677300335" data-adsbygoogle-status="done" style="display: block; margin: auto; background-color: transparent;"><ins id="aswift_4_expand" style="display:inline-table;border:none;height:175px;margin:0;padding:0;position:relative;visibility:visible;width:697px;background-color:transparent;"><ins id="aswift_4_anchor" style="display:block;border:none;height:175px;margin:0;padding:0;position:relative;visibility:visible;width:697px;background-color:transparent;"><iframe width="697" height="175" frameborder="0" marginwidth="0" marginheight="0" vspace="0" hspace="0" allowtransparency="true" scrolling="no" allowfullscreen="true" onload="var i=this.id,s=window.google_iframe_oncopy,H=s&amp;&amp;s.handlers,h=H&amp;&amp;H[i],w=this.contentWindow,d;try{d=w.document}catch(e){}if(h&amp;&amp;d&amp;&amp;(!d.body||!d.body.firstChild)){if(h.call){setTimeout(h,0)}else if(h.match){try{h=s.upd(h,i)}catch(e){}w.location.replace(h)}}" id="aswift_4" name="aswift_4" style="left:0;position:absolute;top:0;border:0px;width:697px;height:175px;"></iframe></ins></ins></ins></div> <p><strong>microtask</strong> 在某些情况下也是会有问题的,因为 <strong>microtask</strong> 优先级比较高,事件会在顺序事件(如<a href="https://github.com/vuejs/vue/issues/4521" rel="nofollow noreferrer" target="_blank">#4521</a>,<a href="https://github.com/vuejs/vue/issues/6690" rel="nofollow noreferrer" target="_blank">#6690</a> 有变通方法)之间甚至在同一事件的冒泡过程中触发(<a href="https://github.com/vuejs/vue/issues/6566" rel="nofollow noreferrer" target="_blank">#6566</a>)。</p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="// 核心的异步延迟函数,用于异步延迟调用 flushCallbacks 函数 let timerFunc; // timerFunc 优先使用原生 Promise // 原本 MutationObserver 支持更广,但在 iOS >= 9.3.3 的 UIWebView 中,触摸事件处理程序中触发会产生严重错误<br /> if (typeof Promise !== 'undefined' &amp;&amp; isNative(Promise)) {<br /> const p = Promise.resolve();<br /> timerFunc = () => {<br /> p.then(flushCallbacks);</p> <p> // IOS 的 UIWebView,Promise.then 回调被推入 microtask 队列但是队列可能不会如期执行。<br /> // 因此,添加一个空计时器“强制”执行 microtask 队列。<br /> if (isIOS) setTimeout(noop);<br /> };<br /> isUsingMicroTask = true;</p> <p> // 当原生 Promise 不可用时,timerFunc 使用原生 MutationObserver<br /> // 如 PhantomJS,iOS7,Android 4.4<br /> // issue #6466 MutationObserver 在 IE11 并不可靠,所以这里排除了 IE<br /> } else if (<br /> !isIE &amp;&amp;<br /> typeof MutationObserver !== 'undefined' &amp;&amp;<br /> (isNative(MutationObserver) ||<br /> // PhantomJS 和 iOS 7.x<br /> MutationObserver.toString() === '[object MutationObserverConstructor]')<br /> ) {<br /> let counter = 1;<br /> const observer = new MutationObserver(flushCallbacks);<br /> const textNode = document.createTextNode(String(counter));<br /> observer.observe(textNode, {<br /> characterData: true,<br /> });<br /> timerFunc = () => {<br /> counter = (counter + 1) % 2;<br /> textNode.data = String(counter);<br /> };<br /> isUsingMicroTask = true;</p> <p> // 如果原生 setImmediate 可用,timerFunc 使用原生 setImmediate<br /> } else if (typeof setImmediate !== 'undefined' &amp;&amp; isNative(setImmediate)) {<br /> timerFunc = () => {<br /> setImmediate(flushCallbacks);<br /> };<br /> } else {<br /> // 最后的倔强,timerFunc 使用 setTimeout<br /> timerFunc = () => {<br /> setTimeout(flushCallbacks, 0);<br /> };<br /> }" title="" data-original-title="复制"></span> </div> </p></div> <pre class="javascript hljs"><code class="js"><span class="hljs-comment">// 核心的异步延迟函数,用于异步延迟调用 flushCallbacks 函数</span> <span class="hljs-keyword">let</span> timerFunc; <span class="hljs-comment">// timerFunc 优先使用原生 Promise</span> <span class="hljs-comment">// 原本 MutationObserver 支持更广,但在 iOS &gt;= 9.3.3 的 UIWebView 中,触摸事件处理程序中触发会产生严重错误</span> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> <span class="hljs-built_in">Promise</span> !== <span class="hljs-string">'undefined'</span> &amp;&amp; isNative(<span class="hljs-built_in">Promise</span>)) { <span class="hljs-keyword">const</span> p = <span class="hljs-built_in">Promise</span>.resolve(); timerFunc = <span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> { p.then(flushCallbacks); <span class="hljs-comment">// IOS 的 UIWebView,Promise.then 回调被推入 microtask 队列但是队列可能不会如期执行。</span> <span class="hljs-comment">// 因此,添加一个空计时器“强制”执行 microtask 队列。</span> <span class="hljs-keyword">if</span> (isIOS) setTimeout(noop); }; isUsingMicroTask = <span class="hljs-literal">true</span>; <span class="hljs-comment">// 当原生 Promise 不可用时,timerFunc 使用原生 MutationObserver</span> <span class="hljs-comment">// 如 PhantomJS,iOS7,Android 4.4</span> <span class="hljs-comment">// issue #6466 MutationObserver 在 IE11 并不可靠,所以这里排除了 IE</span> } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> ( !isIE &amp;&amp; <span class="hljs-keyword">typeof</span> MutationObserver !== <span class="hljs-string">'undefined'</span> &amp;&amp; (isNative(MutationObserver) || <span class="hljs-comment">// PhantomJS 和 iOS 7.x</span> MutationObserver.toString() === <span class="hljs-string">'[object MutationObserverConstructor]'</span>) ) { <span class="hljs-keyword">let</span> counter = <span class="hljs-number">1</span>; <span class="hljs-keyword">const</span> observer = <span class="hljs-keyword">new</span> MutationObserver(flushCallbacks); <span class="hljs-keyword">const</span> textNode = <span class="hljs-built_in">document</span>.createTextNode(<span class="hljs-built_in">String</span>(counter)); observer.observe(textNode, { <span class="hljs-attr">characterData</span>: <span class="hljs-literal">true</span>, }); timerFunc = <span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> { counter = (counter + <span class="hljs-number">1</span>) % <span class="hljs-number">2</span>; textNode.data = <span class="hljs-built_in">String</span>(counter); }; isUsingMicroTask = <span class="hljs-literal">true</span>; <span class="hljs-comment">// 如果原生 setImmediate 可用,timerFunc 使用原生 setImmediate</span> } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> setImmediate !== <span class="hljs-string">'undefined'</span> &amp;&amp; isNative(setImmediate)) { timerFunc = <span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> { setImmediate(flushCallbacks); }; } <span class="hljs-keyword">else</span> { <span class="hljs-comment">// 最后的倔强,timerFunc 使用 setTimeout</span> timerFunc = <span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> { setTimeout(flushCallbacks, <span class="hljs-number">0</span>); }; }</code></pre> <p>一句话总结优先级:<strong>microtask 优先</strong>。<br /><strong><a href="http://www.js-code.com/tag/promis" title="Promis" target="_blank">Promis</a>e</strong> &gt; <strong>MutationObserver</strong> &gt; <strong>setImmediate</strong> &gt; <strong>setTimeout</strong></p> <h2 id="articleHeader4">nextTick 函数</h2> <p>nextTick 函数。接受两个参数:</p> <ol> <li> <strong>cb 回调函数</strong>:是要延迟执行的函数;</li> <li> <strong>ctx</strong>:指定 cb 回调函数 的 <strong><a href="http://www.js-code.com/tag/this" title="this" target="_blank">this</a> 指向</strong>;</li> </ol> <p>Vue 实例方法 <strong>$nextTick</strong> 做了进一步封装,把 <strong>ctx</strong> 设置为<strong>当前 Vue 实例</strong>。</p> <div class="widget-codetool" style="display:none;"> <div class="widget-codetool--inner"> <span class="selectCode code-tool" data-toggle="tooltip" data-placement="top" title="" data-original-title="全选"></span><br /> <span type="button" class="copyCode code-tool" data-toggle="tooltip" data-placement="top" data-clipboard-text="export function nextTick(cb?: Function, ctx?: Object) { let _resolve; // cb 回调函数会经统一处理压入 callbacks 数组 callbacks.push(() => {<br /> if (cb) {<br /> // 给 cb 回调函数执行加上了 try-catch 错误处理<br /> try {<br /> cb.call(ctx);<br /> } catch (e) {<br /> handleError(e, ctx, 'nextTick');<br /> }<br /> } else if (_resolve) {<br /> _resolve(ctx);<br /> }<br /> });</p> <p> // 执行异步延迟函数 timerFunc<br /> if (!pending) {<br /> pending = true;<br /> timerFunc();<br /> }</p> <p> // 当 nextTick 没有传入函数参数的时候,返回一个 Promise 化的调用<br /> if (!cb &amp;&amp; typeof Promise !== 'undefined') {<br /> return new Promise(resolve => {<br /> _resolve = resolve;<br /> });<br /> }<br /> }" title="" data-original-title="复制"></span> </div> </p></div> <pre class="javascript hljs"><code class="js"><span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">nextTick</span>(<span class="hljs-params">cb?: Function, ctx?: Object</span>) </span>{ <span class="hljs-keyword">let</span> _resolve; <span class="hljs-comment">// cb 回调函数会经统一处理压入 callbacks 数组</span> callbacks.push(<span class="hljs-function"><span class="hljs-params">()</span> =&gt;</span> { <span class="hljs-keyword">if</span> (cb) { <span class="hljs-comment">// 给 cb 回调函数执行加上了 try-catch 错误处理</span> <span class="hljs-keyword">try</span> { cb.call(ctx); } <span class="hljs-keyword">catch</span> (e) { handleError(e, ctx, <span class="hljs-string">'nextTick'</span>); } } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (_resolve) { _resolve(ctx); } }); <span class="hljs-comment">// 执行异步延迟函数 timerFunc</span> <span class="hljs-keyword">if</span> (!pending) { pending = <span class="hljs-literal">true</span>; timerFunc(); } <span class="hljs-comment">// 当 nextTick 没有传入函数参数的时候,返回一个 Promise 化的调用</span> <span class="hljs-keyword">if</span> (!cb &amp;&amp; <span class="hljs-keyword">typeof</span> <span class="hljs-built_in">Promise</span> !== <span class="hljs-string">'undefined'</span>) { <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function"><span class="hljs-params">resolve</span> =&gt;</span> { _resolve = resolve; }); } }</code></pre> <h1 id="articleHeader5">小结</h1> <p>整体看过来,感觉还是比较好理解的吧~ <strong>2.6</strong> 版本相比之前简化了一点。</p> <p>小结一下,每次调用 <code>Vue.nextTick(cb)</code> 会做些什么:<br /><strong>cb 函数</strong>经处理压入 <strong>callbacks 数组</strong>,执行 <strong>timerFunc 函数</strong>,延迟调用 <strong>flushCallbacks 函数</strong>,遍历执行 <strong>callbacks 数组</strong>中的所有函数。</p> <p>延迟调用优先级如下:<br /><strong><a href="http://www.js-code.com/tag/promise" title="Promise" target="_blank">Promise</a></strong> &gt; <strong>MutationObserver</strong> &gt; <strong>setImmediate</strong> &gt; <strong>setTimeout</strong></p> <h1 id="articleHeader6">版本差异</h1> <p>其实 <strong>Vue 2.4、2.5、2.6</strong> 版本的 <strong>nextTick</strong> 策略都略不一样。</p> <p>整体 <strong>2.6</strong> 和 <strong>2.4</strong> 的比较相似。(仔细瞅了瞅,基本就是一样的,<strong>2.6</strong> timerFunc 多了个 setImmediate 判断)</p> <p><strong>2.5</strong> 版本其实也差不多。。。源码写法有些不一样,整体优先级是:<strong><a href="http://www.js-code.com/tag/promis" title="Promis" target="_blank">Promis</a>e</strong> &gt; <strong>setImmediate</strong> &gt; <strong>MessageChannel</strong> &gt; <strong>setTimeout</strong>,如果更新是在 v-on 事件处理程序中触发的,nextTick 会优先使用 macrotask。感兴趣可自行去末尾的参考地址进行查阅。</p> <hr> <p>参考:</p> <p><a href="https://cn.vuejs.org/v2/api/#Vue-nextTick" rel="nofollow noreferrer" target="_blank">API—Vue.js</a><br /><a href="https://html.spec.whatwg.org/multipage/webappapis.html#event-loops" rel="nofollow noreferrer" target="_blank">事件循环规范</a><br /><a href="https://github.com/vuejs/vue/blob/v2.6.7/src/core/util/next-tick.js" rel="nofollow noreferrer" target="_blank">Vue nextTick v2.6.7 源码</a><br /><a href="https://github.com/vuejs/vue/blob/v2.5.22/src/core/util/next-tick.js" rel="nofollow noreferrer" target="_blank">Vue nextTick v2.5.22 源码</a><br /><a href="https://github.com/vuejs/vue/blob/v2.4.4/src/core/util/env.js" rel="nofollow noreferrer" target="_blank">Vue nextTick v2.4.4 源码</a><br /><a href="https://juejin.im/entry/59082301a22b9d0065f1a186" rel="nofollow noreferrer" target="_blank">从 event loop 规范探究 javaScript 异步及浏览器更新渲染时机</a></p>

本文固定链接: http://www.js-code.com/vue-js/vue-js_17248.html