0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
电子发烧友
开通电子发烧友VIP会员 尊享10大特权
海量资料免费下载
精品直播免费看
优质内容免费畅学
课程9折专享价
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

解析$nextTick魔力,為啥大家都愛它?

京東云 ? 來源:京東保險 卓雅倩 ? 作者:京東保險 卓雅倩 ? 2024-12-17 10:02 ? 次閱讀

作者:京東保險 卓雅倩

1.為什么需要使用$nextTick?

首先我們來看看官方對于$nextTick的定義:

在下次 DOM 更新循環(huán)結(jié)束之后執(zhí)行延遲回調(diào)。在修改數(shù)據(jù)之后立即使用這個方法,獲取更新后的 DOM。

由于vue的試圖渲染是異步的,生命周期的created()鉤子函數(shù)進行的DOM操作一定要放在Vue.nextTick()的回調(diào)函數(shù)中,原因是在created()鉤子函數(shù)執(zhí)行的時候DOM其實并未進行渲染,而此時進行DOM操作是徒勞的,所以一定要將DOM操作的js代碼放到Vue.nextTick()的回調(diào)函數(shù)中。除了在created()鉤子函數(shù)中使用之外咱們還會遇到很多種需要使用到Vue.nextTick()的場景,如下所示:

咱們?nèi)粘I钪谐3錾仙鲜鰣鼍?,會有很多問題,就比如當(dāng)我們點擊按鈕更新數(shù)據(jù)時候,如下代碼示例:

點擊顯示輸入框,并且獲取輸入框焦點

export default { data() { return { isShow: false } }, methods : { handleClick () { this.isShow = true this.$refs.input.focus() //控制欄會報錯,因為還沒有這個dom } } }

點擊控制欄顯示效果:控制欄報錯,提示沒有獲取到dom元素;

wKgZPGdg27GAAbCXAAH_Vfnf58U128.png

所以現(xiàn)在Vue.nextTick()派上了用場,Vue.nextTick() 方法的作用正是等待上一次事件循環(huán)執(zhí)行完畢,并在下一次事件循環(huán)開始時再執(zhí)行回調(diào)函數(shù)。這樣可以保證回調(diào)函數(shù)中的 DOM 操作已經(jīng)被 Vue.js 進行過更新,從而避免了一些潛在的問題,如下代碼所示:

點擊顯示輸入框,并且獲取輸入框焦點

export default { data() { return { isShow: false } }, methods : { handleClick () { this.isShow = true this.$nextTick(()=>{ this.$refs.input.focus() }) } } }

加上this.$nextTick后就能夠使得輸入框獲取到焦點;

總而言之Vue.nextTick()就是下次 DOM 更新渲染后執(zhí)行延遲回調(diào)函數(shù)。在日常開發(fā)中,我們在修改數(shù)據(jù)之后使用這個方法,就可以獲取更新后的 DOM的同時進行在對DOM進行相對應(yīng)操作的 js代碼;

2.$nextTick如何實現(xiàn)的?

JS是單線程執(zhí)行的,所有的同步任務(wù)都是在主線程上執(zhí)行的,形成了一個執(zhí)行棧,從上到下依次執(zhí)行,異步代碼會放在任務(wù)隊列里面。

?同步任務(wù)

在主線程里執(zhí)行,當(dāng)瀏覽器第一遍過濾html文件的時候可以執(zhí)行完;(在當(dāng)前作用域直接執(zhí)行的所有內(nèi)容,包括執(zhí)行的方法、new出來的對象)

?異步任務(wù)

耗費時間較長或者性能較差的,瀏覽器執(zhí)行到這些的時候會將其丟到異步任務(wù)隊列中,不會立即執(zhí)行

同時異步任務(wù)分為宏任務(wù)(如setTimeout、setInterval、postMessage、setImmediate等)和微任務(wù)(Promise、process.nextTick等),瀏覽器執(zhí)行這兩種任務(wù)的優(yōu)先級不同;會優(yōu)先執(zhí)行微任務(wù)隊列的代碼,微任務(wù)隊列清空之后再執(zhí)行宏任務(wù)的隊列,這樣循環(huán)往復(fù);

JS自上向下進行代碼的編譯執(zhí)行,遇到同步代碼壓入JS執(zhí)行棧執(zhí)行后出棧,遇到異步代碼放入任務(wù)隊列,當(dāng)JS執(zhí)行棧清空,去執(zhí)行異步隊列中的回調(diào)函數(shù),先去執(zhí)行微任務(wù)隊列,當(dāng)微任務(wù)隊列清空后,去檢測執(zhí)行宏任務(wù)隊列中的回調(diào)函數(shù),直至所有棧和隊列清空

整體流程如下圖所示:

wKgZO2dg27KAOPNyAASDxoSgV6Q704.png

接下來讓我們看看nextTick的源碼~

vue將nextTick的源碼放在了vue/core/util/next-tick.js中。如下圖所示:

wKgZPGdg27OAKML3AAaeel8WjTw493.png

我們把這個文件拆成三個部分來看:

1.nextTick定義函數(shù)

我們將nextTick函數(shù)單獨拿出來,callbacks是一個回調(diào)隊列,其實調(diào)用nextTick就是往這個數(shù)組里面?zhèn)鲌?zhí)行任務(wù),callbacks新增回調(diào)函數(shù)之后執(zhí)行timerFunc函數(shù),pending是用來限制同一個事件循環(huán)內(nèi)只能執(zhí)行一次的pending鎖;

const callbacks = [] // 回調(diào)隊列 let pending = false // export function nextTick (cb?: Function, ctx?: Object) { let _resolve callbacks.push(() => { // cb 回調(diào)函數(shù)會經(jīng)統(tǒng)一處理壓入 callbacks 數(shù)組 if (cb) { try { cb.call(ctx) } catch (e) { handleError(e, ctx, 'nextTick') } } else if (_resolve) { _resolve(ctx) } }) // 執(zhí)行異步延遲函數(shù) timerFunc if (!pending) { pending = true timerFunc() } // $flow-disable-line // 當(dāng) nextTick 沒有傳入函數(shù)參數(shù)的時候,返回一個 Promise 化的調(diào)用 if (!cb && typeof Promise !== 'undefined') { return new Promise(resolve => { _resolve = resolve }) } }

2.timerFunc函數(shù)做了四個判斷,先后嘗試當(dāng)前環(huán)境是否能夠使用原生的Promise.then、MutationObserver和setImmediate,不斷的降級處理,如果以上三個都不支持,則最后就會直接使用setTimeOut,主要操作就是將flushCallbacks中的函數(shù)放入微任務(wù)或者宏任務(wù),等待下一個事件循環(huán)開始執(zhí)行;宏任務(wù)耗費的時間是大于微任務(wù)的,所以在瀏覽器支持的情況下,優(yōu)先使用微任務(wù)。如果瀏覽器不支持微任務(wù),使用宏任務(wù);但是,各種宏任務(wù)之間也有效率的不同,需要根據(jù)瀏覽器的支持情況,使用不同的宏任務(wù);

export let isUsingMicroTask = false let timerFunc if (typeof Promise !== 'undefined' && isNative(Promise)) { //是否支持Promise const p = Promise.resolve() timerFunc = () => { p.then(flushCallbacks) if (isIOS) setTimeout(noop) } isUsingMicroTask = true } else if (!isIE && typeof MutationObserver !== 'undefined' && ( isNative(MutationObserver) || MutationObserver.toString() === '[object MutationObserverConstructor]' )) { //是否支持MutationObserver let counter = 1 const observer = new MutationObserver(flushCallbacks) const textNode = document.createTextNode(String(counter)) observer.observe(textNode, { characterData: true }) timerFunc = () => { counter = (counter + 1) % 2 textNode.data = String(counter) } isUsingMicroTask = true } else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) { timerFunc = () => { //是否支持setImmediate setImmediate(flushCallbacks) } } else { // Fallback to setTimeout. timerFunc = () => { //上面都不行,直接使用setTimeout setTimeout(flushCallbacks, 0) } }

3.flushCallbacks函數(shù)

flushCallbacks函數(shù)只有幾行,也很好理解,將pending鎖置為false,同時將callbacks數(shù)組復(fù)制一份之后再將callbacks置為空,接下來將復(fù)制出來的callbacks數(shù)組的每個函數(shù)依次進行執(zhí)行,簡單來說它的主要作用就是用來執(zhí)行callbacks中的回調(diào)函數(shù);

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

值得注意的是,$nextTick 并不是一個真正意義上的微任務(wù)microtask,而是利用了事件循環(huán)機制來實現(xiàn)異步更新。因此,它的執(zhí)行時機相對于微任務(wù)可能會有所延遲,但仍能保證在 DOM 更新后盡快執(zhí)行回調(diào)函數(shù)。

總的來說,nextTick就是:

1.將傳入的回調(diào)函數(shù)放入callbacks數(shù)組等待執(zhí)行,定義pending判斷鎖保證一個事件循環(huán)中只能調(diào)用一次timerFunc函數(shù);

2.根據(jù)環(huán)境判斷使用異步方式,調(diào)用timerFunc函數(shù)調(diào)用flushCallbacks函數(shù)依次執(zhí)行callbacks中的回調(diào)函數(shù);

3.個人小結(jié)

nextTick可避免數(shù)據(jù)更新后導(dǎo)致DOM的數(shù)據(jù)不一致的問題,提供了更穩(wěn)定的異步更新機制,解決了created鉤子函數(shù)DOM未渲染會造成的異步數(shù)據(jù)渲染問題,但如果過多的使用nextTick會導(dǎo)致事件循環(huán)中任務(wù)數(shù)量和回調(diào)函數(shù)增多,有可能出現(xiàn)可怕的回調(diào)地獄,導(dǎo)致性能下降,同時過度依賴nextTick也會降低代碼的可讀性,所以大家還是"按需加載"的好~

審核編輯 黃宇

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • DOM
    DOM
    +關(guān)注

    關(guān)注

    0

    文章

    18

    瀏覽量

    9659
  • vue
    vue
    +關(guān)注

    關(guān)注

    0

    文章

    58

    瀏覽量

    8059
收藏 0人收藏

    評論

    相關(guān)推薦

    AMOLED屏有什么魔力?華為、小米都愛

    越來越多的手機廠商在屏幕選擇上偏愛OLED,華為、OPPO、vivo、小米等都加入了AMOLED的陣營,這塊屏幕究竟存在什么樣的魔力,能夠吸引這么多廠商?
    發(fā)表于 08-24 09:45 ?3076次閱讀

    用proteus6.7仿真提示cannot find model file bldcm.mdf這是為啥??

    用proteus6.7仿真提示cannot find model file bldcm.mdf這是為啥,大神們幫我看看的,求解答的,求解決問題。。。。
    發(fā)表于 05-15 12:24

    魔力非凡——隆宇 黑魔力電源凈化器“魔力2500”

    我說他們推出了一款LY-6KW最新改良型,倍頻率高達2500倍,型號大致不變是LY-6KW-1,名字叫做魔力2500,說是音效十分出色,比我原來那臺要好得多,叫我一定要試一試,作個AB比較云云。有
    發(fā)表于 04-02 12:14

    模擬電路有什么魔力

    猜猜看是哪兩家半導(dǎo)體廠商?——的公司名稱以T開頭,過去主要做數(shù)字IC,如今卻認為模擬電子更精彩且更具報酬效益!對于半導(dǎo)體廠商來說,過去曾經(jīng)是只要具備擴展數(shù)字晶體管的能力,即可提供一個看起來前景無限
    發(fā)表于 06-21 04:20

    智能魔鏡顯示屏是什么,究竟魔力何在

    隨著黑科技逐漸滲入到日常生活,人們對科技產(chǎn)品的審視標準日益嚴格,傳統(tǒng)鏡子主要功能是被人們用來整理儀容。然而隨著科學(xué)技術(shù)在各個領(lǐng)域中的普及化,鏡子的單一功能被改寫,曠世智能魔鏡顯示屏,究竟魔力何在
    發(fā)表于 09-02 16:31 ?872次閱讀

    搞嵌入式,為啥要有uboot?

    搞嵌入式的,為啥要有uboot?
    的頭像 發(fā)表于 02-05 12:00 ?3172次閱讀

    日本人都愛買什么手機?國產(chǎn)手機品牌為啥在日本就打不過當(dāng)?shù)仄放颇?/a>

    則是唯一一個亮眼的國際品牌。 所以,風(fēng)靡全球的華為、小米等國產(chǎn)手機品牌,為啥在日本就打不過當(dāng)?shù)仄放颇兀刻O果贏得市場的秘訣又是啥? 日本人都愛買什么手機? 在許多國人的印象中,日本人最喜歡用的就是翻蓋式的功能機,一群穿著校
    的頭像 發(fā)表于 12-11 17:44 ?1.4w次閱讀

    為什么單相電機要用電容啟動?

    為啥單相電機要用電容啟動呢?本文小編就來給大家解析一下。
    的頭像 發(fā)表于 12-14 21:24 ?1690次閱讀

    為啥單相電機要用電容啟動?

    為啥單相電機要用電容啟動呢?本文小編就來給大家解析一下。
    發(fā)表于 03-12 06:06 ?57次下載
    <b class='flag-5'>為啥</b>單相電機要用電容啟動?

    Gartner魔力象限報告中將IBM評為AI領(lǐng)導(dǎo)者

    IBM 在 Gartner 2021年“云 AI 開發(fā)者服務(wù)魔力象限“和“數(shù)據(jù)科學(xué)與機器學(xué)習(xí)平臺魔力象限”報告中均被評為領(lǐng)導(dǎo)者。 精彩提要 中國北京,2021年 3月 12日 —— IBM
    的頭像 發(fā)表于 03-19 09:47 ?2036次閱讀

    to B軟件為啥用戶體驗不好

    to B軟件為啥用戶體驗不好?我今天從機制根源層面給大家說說。否則大家還停留在UI、UE的認知層面上。(1)從甲方視角看to B軟件其實分為:高層決策軟件、中層管理軟件、基層業(yè)務(wù)操作軟件。...
    發(fā)表于 12-28 19:37 ?6次下載
    to B軟件<b class='flag-5'>為啥</b>用戶體驗不好

    從藍光到綠色魔力

    從藍光到綠色魔力
    發(fā)表于 11-02 08:16 ?0次下載
    從藍光到綠色<b class='flag-5'>魔力</b>

    國內(nèi)唯一,華為再次入選2022 Gartner SIEM魔力象限

    2022年10月,業(yè)界知名分析機構(gòu)Gartner公司發(fā)布2022年SIEM(Security Information and Event Management,安全信息和事件管理)魔力象限
    的頭像 發(fā)表于 11-16 19:35 ?819次閱讀

    高速信號為啥要走表層?

    高速信號為啥要走表層?
    的頭像 發(fā)表于 12-05 15:16 ?761次閱讀
    高速信號<b class='flag-5'>為啥</b>要走表層?

    被眾人吐槽的Keil,為啥還能挺到現(xiàn)在?

    這款工具相信大家都不陌生,一直被人吐槽很難用,但它為啥沒有被淘汰呢?一、Keil被吐槽的點Keil被大家吐槽最多的還是那千年不變的UI界面,其他很多IDE的界面都比較“現(xiàn)代化”,而K
    的頭像 發(fā)表于 04-18 08:10 ?1566次閱讀
    被眾人吐槽的Keil,<b class='flag-5'>為啥</b>還能挺到現(xiàn)在?

    電子發(fā)燒友

    中國電子工程師最喜歡的網(wǎng)站

    • 2931785位工程師會員交流學(xué)習(xí)
    • 獲取您個性化的科技前沿技術(shù)信息
    • 參加活動獲取豐厚的禮品