内射老阿姨1区2区3区4区_久久精品人人做人人爽电影蜜月_久久国产精品亚洲77777_99精品又大又爽又粗少妇毛片

如何從Javascript事件循環(huán)看出Vue.nextTick的原理和執(zhí)行機(jī)制

今天就跟大家聊聊有關(guān)如何從Javascript事件循環(huán)看出Vue.nextTick的原理和執(zhí)行機(jī)制,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結(jié)了以下內(nèi)容,希望大家根據(jù)這篇文章可以有所收獲。

創(chuàng)新互聯(lián)公司成立以來(lái)不斷整合自身及行業(yè)資源、不斷突破觀念以使企業(yè)策略得到完善和成熟,建立了一套“以技術(shù)為基點(diǎn),以客戶(hù)需求中心、市場(chǎng)為導(dǎo)向”的快速反應(yīng)體系。對(duì)公司的主營(yíng)項(xiàng)目,如中高端企業(yè)網(wǎng)站企劃 / 設(shè)計(jì)、行業(yè) / 企業(yè)門(mén)戶(hù)設(shè)計(jì)推廣、行業(yè)門(mén)戶(hù)平臺(tái)運(yùn)營(yíng)、重慶APP開(kāi)發(fā)公司、手機(jī)網(wǎng)站開(kāi)發(fā)、微信網(wǎng)站制作、軟件開(kāi)發(fā)、成都服務(wù)器托管等實(shí)行標(biāo)準(zhǔn)化操作,讓客戶(hù)可以直觀的預(yù)知到從創(chuàng)新互聯(lián)公司可以獲得的服務(wù)效果。

拋磚引玉

Vue 的特點(diǎn)之一就是響應(yīng)式,但是有些時(shí)候數(shù)據(jù)更新了,我們看到頁(yè)面上的 DOM 并沒(méi)有立刻更新。如果我們需要在 DOM 更新之后再執(zhí)行一段代碼時(shí),可以借助  nextTick 實(shí)現(xiàn)。

我們先來(lái)看一個(gè)例子

export default {   data() {     return {       msg: 0     }   },   mounted() {     this.msg = 1     this.msg = 2     this.msg = 3   },   watch: {     msg() {       console.log(this.msg)     }   } }

這里的結(jié)果是只輸出一個(gè) 3,而非依次輸出 1,2,3。這是為什么呢?

vue 的官方文檔是這樣解釋的:

Vue 異步執(zhí)行 DOM 更新。只要觀察到數(shù)據(jù)變化,Vue 將開(kāi)啟一個(gè)隊(duì)列,并緩沖在同一事件循環(huán)中發(fā)生的所有數(shù)據(jù)改變。如果同一個(gè)watcher  被多次觸發(fā),只會(huì)被推入到隊(duì)列中一次。這種在緩沖時(shí)去除重復(fù)數(shù)據(jù)對(duì)于避免不必要的計(jì)算和 DOM 操作上非常重要。然后,在下一個(gè)的事件循環(huán)“tick”中,Vue  刷新隊(duì)列并執(zhí)行實(shí)際 (已去重的) 工作。Vue 在內(nèi)部嘗試對(duì)異步隊(duì)列使用原生的Promise.then和  MessageChannel,如果執(zhí)行環(huán)境不支持,會(huì)采用setTimeout(fn, 0)代替。

假如有這樣一種情況,mounted鉤子函數(shù)下一個(gè)變量 a 的值會(huì)被++循環(huán)執(zhí)行 1000  次。每次++時(shí),都會(huì)根據(jù)響應(yīng)式觸發(fā)setter->Dep->Watcher->update->run。如果這時(shí)候沒(méi)有異步更新視圖,那么每次++都會(huì)直接操作  DOM 一次,這是非常消耗性能的。所以 Vue 實(shí)現(xiàn)了一個(gè)queue隊(duì)列,在下一個(gè) Tick(或者是當(dāng)前 Tick  的微任務(wù)階段)的時(shí)候會(huì)統(tǒng)一執(zhí)行queue中Watcher的run。同時(shí),擁有相同 id 的Watcher不會(huì)被重復(fù)加入到該queue中去,所以不會(huì)執(zhí)行 1000  次Watcher的run。最終的結(jié)果是直接把 a 的值從 1 變成 1000,大大提升了性能。

在 vue 中,數(shù)據(jù)監(jiān)測(cè)都是通過(guò)Object.defineProperty來(lái)重寫(xiě)里面的 set 和 get 方法實(shí)現(xiàn)的,vue 更新 DOM  是異步的,每當(dāng)觀察到數(shù)據(jù)變化時(shí),vue 就開(kāi)始一個(gè)隊(duì)列,將同一事件循環(huán)內(nèi)所有的數(shù)據(jù)變化緩存起來(lái),等到下一次 eventLoop,將會(huì)把隊(duì)列清空,進(jìn)行 DOM  更新。

想要了解 vue.nextTick 的執(zhí)行機(jī)制,我們先來(lái)了解一下 javascript 的事件循環(huán)。

js 事件循環(huán)

js 的任務(wù)隊(duì)列分為同步任務(wù)和異步任務(wù),所有的同步任務(wù)都是在主線(xiàn)程里執(zhí)行的。異步任務(wù)可能會(huì)在 macrotask 或者 microtask  里面,異步任務(wù)進(jìn)入 Event Table 并注冊(cè)函數(shù)。當(dāng)指定的事情完成時(shí),Event Table 會(huì)將這個(gè)函數(shù)移入 Event  Queue。主線(xiàn)程內(nèi)的任務(wù)執(zhí)行完畢為空,會(huì)去 Event Queue 讀取對(duì)應(yīng)的函數(shù),進(jìn)入主線(xiàn)程執(zhí)行。上述過(guò)程會(huì)不斷重復(fù),也就是常說(shuō)的 Event  Loop(事件循環(huán))。

1. macro-task(宏任務(wù)):

每次執(zhí)行棧執(zhí)行的代碼就是一個(gè)宏任務(wù)(包括每次從事件隊(duì)列中獲取一個(gè)事件回調(diào)并放到執(zhí)行棧中執(zhí)行)。瀏覽器為了能夠使得 js 內(nèi)部(macro)task與  DOM 任務(wù)能夠有序執(zhí)行,會(huì)在一個(gè)(macro)task執(zhí)行結(jié)束后,在下一個(gè)(macro)task執(zhí)行開(kāi)始前,對(duì)頁(yè)面進(jìn)行重新渲染。宏任務(wù)主要包含:

  • script(整體代碼)

  • setTimeout / setInterval

  • setImmediate(Node.js 環(huán)境)

  • I/O

  • UI render

  • postMessage

  • MessageChannel

2. micro-task(微任務(wù)):

可以理解是在當(dāng)前 task 執(zhí)行結(jié)束后立即執(zhí)行的任務(wù)。也就是說(shuō),在當(dāng)前 task 任務(wù)后,下一個(gè) task 之前,在渲染之前。所以它的響應(yīng)速度相比  setTimeout(setTimeout 是 task)會(huì)更快,因?yàn)闊o(wú)需等渲染。也就是說(shuō),在某一個(gè) macrotask 執(zhí)行完后,就會(huì)將在它執(zhí)行期間產(chǎn)生的所有  microtask 都執(zhí)行完畢(在渲染前)。microtask 主要包含:

  • process.nextTick(Node.js 環(huán)境)

  • Promise

  • Async/Await

  • MutationObserver(html5 新特性)

3. 小結(jié)

  • 先執(zhí)行主線(xiàn)程

  • 遇到宏隊(duì)列(macrotask)放到宏隊(duì)列(macrotask)

  • 遇到微隊(duì)列(microtask)放到微隊(duì)列(microtask)

  • 主線(xiàn)程執(zhí)行完畢

  • 執(zhí)行微隊(duì)列(microtask),微隊(duì)列(microtask)執(zhí)行完畢

  • 執(zhí)行一次宏隊(duì)列(macrotask)中的一個(gè)任務(wù),執(zhí)行完畢

  • 執(zhí)行微隊(duì)列(microtask),執(zhí)行完畢

  • 依次循環(huán)。。。

Vue.nextTick 源碼

vue 是采用雙向數(shù)據(jù)綁定的方法驅(qū)動(dòng)數(shù)據(jù)更新的,雖然這樣能避免直接操作 DOM,提高了性能,但有時(shí)我們也不可避免需要操作 DOM,這時(shí)就該  Vue.nextTick(callback)出場(chǎng)了,它接受一個(gè)回調(diào)函數(shù),在 DOM 更新完成后,這個(gè)回調(diào)函數(shù)就會(huì)被調(diào)用。不管是 vue.nextTick  還是vue.prototype.\$nextTick 都是直接用的nextTick這個(gè)閉包函數(shù)。

export const nextTick = (function () {   const callbacks = []   let pending = false   let timerFunc    function nextTickHandler () {     pending = false     const copies = callbacks.slice(0)     callbacks.length = 0     for (let i = 0; i < copies.length; i++) {       copies[i]()     }   }  ... })()

使用數(shù)組callbacks保存回調(diào)函數(shù),pending表示當(dāng)前狀態(tài),使用函數(shù)nextTickHandler  來(lái)執(zhí)行回調(diào)隊(duì)列。在該方法內(nèi),先通過(guò)slice(0)保存了回調(diào)隊(duì)列的一個(gè)副本,通過(guò)設(shè)置 callbacks.length =  0清空回調(diào)隊(duì)列,最后使用循環(huán)執(zhí)行在副本里的所有函數(shù)。

if (typeof Promise !== 'undefined' && isNative(Promise)) {   var p = Promise.resolve()   var logError = err => {     console.error(err)   }   timerFunc = () => {     p.then(nextTickHandler).catch(logError)     if (isIOS) setTimeout(noop)   } } else if (typeof MutationObserver !== 'undefined' && (isNative(MutationObserver) || MutationObserver.toString() === '[object MutationObserverConstructor]')) {   var counter = 1   var observer = new MutationObserver(nextTickHandler)   var textNode = document.createTextNode(String(counter))   observer.observe(textNode, {     characterData: true   })   timerFunc = () => {     counter = (counter + 1) % 2     textNode.data = String(counter)   } } else {   timeFunc = () => {

隊(duì)列控制的最佳選擇是microtask,而microtask的最佳選擇是Promise。但如果當(dāng)前環(huán)境不支持 Promise,就檢測(cè)到瀏覽器是否支持  MO,是則創(chuàng)建一個(gè)文本節(jié)點(diǎn),監(jiān)聽(tīng)這個(gè)文本節(jié)點(diǎn)的改動(dòng)事件,以此來(lái)觸發(fā)nextTickHandler(也就是 DOM  更新完畢回調(diào))的執(zhí)行。此外因?yàn)榧嫒菪詥?wèn)題,vue 不得不做了microtask向macrotask 的降級(jí)方案。

為讓這個(gè)回調(diào)函數(shù)延遲執(zhí)行,vue 優(yōu)先用promise來(lái)實(shí)現(xiàn),其次是 html5 的  MutationObserver,然后是setTimeout。前兩者屬于microtask,后一個(gè)屬于 macrotask。下面來(lái)看最后一部分。

return function queueNextTick(cb?: Function, ctx?: Object) {   let _resolve   callbacks.push(() => {     if (cb) cb.call(ctx)     if (_resolve) _resolve(ctx)   })   if (!pending) {     pending = true     timerFunc()   }   if (!cb && typeof Promise !== 'undefined') {     return new Promise(resolve => {       _resolve = resolve     })   } }

這就是我們真正調(diào)用的nextTick函數(shù),在一個(gè)event loop內(nèi)它會(huì)將調(diào)用 nextTick的cb 回調(diào)函數(shù)都放入 callbacks  中,pending 用于判斷是否有隊(duì)列正在執(zhí)行回調(diào),例如有可能在 nextTick 中還有一個(gè) nextTick,此時(shí)就應(yīng)該屬于下一個(gè)循環(huán)了。最后幾行代碼是  promise 化,可以將 nextTick 按照 promise 方式去書(shū)寫(xiě)(暫且用的較少)。

應(yīng)用場(chǎng)景

場(chǎng)景一、點(diǎn)擊按鈕顯示原本以 v-show = false 隱藏起來(lái)的輸入框,并獲取焦點(diǎn)。

<input id="keywords" v-if="showit">  showInput(){   this.showit = true   document.getElementById("keywords").focus() }

以上的寫(xiě)法在第一個(gè) tick 里,因?yàn)楂@取不到輸入框,自然也獲取不到焦點(diǎn)。如果我們改成以下的寫(xiě)法,在 DOM 更新后就可以獲取到輸入框焦點(diǎn)了。

showsou(){   this.showit = true   this.$nextTick(function () {     // DOM 更新了     document.getElementById("keywords").focus()   }) }

場(chǎng)景二、獲取元素屬,點(diǎn)擊獲取元素寬度。

<div id="app">   <p ref="myWidth" v-if="showMe">{{ message }}</p>   <button @click="getMyWidth">獲取p元素寬度</button> </div>  getMyWidth() {   this.showMe = true;   thisthis.message = this.$refs.myWidth.offsetWidth;   //報(bào)錯(cuò) TypeError: this.$refs.myWidth is undefined   this.$nextTick(()=>{       //dom元素更新后執(zhí)行,此時(shí)能拿到p元素的屬性     thisthis.message = this.$refs.myWidth.offsetWidth;   }) }

看完上述內(nèi)容,你們對(duì)如何從Javascript事件循環(huán)看出Vue.nextTick的原理和執(zhí)行機(jī)制有進(jìn)一步的了解嗎?如果還想了解更多知識(shí)或者相關(guān)內(nèi)容,請(qǐng)關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝大家的支持。

名稱(chēng)欄目:如何從Javascript事件循環(huán)看出Vue.nextTick的原理和執(zhí)行機(jī)制
文章轉(zhuǎn)載:http://www.rwnh.cn/article32/gspopc.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供電子商務(wù)微信小程序、全網(wǎng)營(yíng)銷(xiāo)推廣、網(wǎng)站建設(shè)、網(wǎng)站制作、定制網(wǎng)站

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話(huà):028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

網(wǎng)站優(yōu)化排名
寿宁县| 英山县| 固阳县| 万荣县| 伊宁县| 伊吾县| 古交市| 西昌市| 普兰县| 上蔡县| 晋城| 三门县| 观塘区| 财经| 富顺县| 定襄县| 台州市| 海伦市| 常德市| 鄂州市| 玉林市| 土默特左旗| 革吉县| 平阴县| 青铜峡市| 布拖县| 清原| 惠水县| 黄浦区| 依兰县| 鄂州市| 汶川县| 安徽省| 新昌县| 昌宁县| 台南市| 福州市| 扶风县| 惠来县| 鲁甸县| 崇阳县|