欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

淺析vue中$nextTick的作用與原理

 更新時(shí)間:2023年10月20日 14:05:24   作者:前端技術(shù)棧  
這篇文章主要為大家詳細(xì)介紹一下Vue中$nextTick的作用于原理,這也是面試中常常考到的問(wèn)題,文中的示例代碼講解詳細(xì),對(duì)我們深入了解Vue有一定的幫助,需要的小伙伴可以參考一下

一、為什么使用nextTick

因?yàn)?vue 采用的異步更新策略,當(dāng)監(jiān)聽(tīng)到數(shù)據(jù)發(fā)生變化的時(shí)候不會(huì)立即去更新DOM,

而是開(kāi)啟一個(gè)任務(wù)隊(duì)列,并緩存在同一事件循環(huán)中發(fā)生的所有數(shù)據(jù)變更;

這種做法帶來(lái)的好處就是可以將多次數(shù)據(jù)更新合并成一次,減少操作DOM的次數(shù),

如果不采用這種方法,假設(shè)數(shù)據(jù)改變100次就要去更新100次DOM,而頻繁的DOM更新是很耗性能的。

二、nextTick 作用

nextTick 接收一個(gè)回調(diào)函數(shù)作為參數(shù),并將這個(gè)回調(diào)函數(shù)延遲到DOM更新后才執(zhí)行;

使用場(chǎng)景:想要操作 基于最新數(shù)據(jù)生成的DOM 時(shí),就將這個(gè)操作放在 nextTick 的回調(diào)中;

下面是一個(gè)簡(jiǎn)單示例,展示了如何使用nextTick:

new Vue({
  data() {
    return {
      message: 'Hello, Vue!'
    }
  },
  mounted() {
    this.message = 'Modified message'
    this.$nextTick(() => {
      // 在DOM更新之后執(zhí)行的操作
      console.log(this.$el.textContent) // 輸出:"Modified message"
    })
  }
})
 

代碼解析:

第一次 console.log 的時(shí)候,獲取的到的是舊值,這是因?yàn)?value 數(shù)據(jù)發(fā)生變化的時(shí)候,Vue 沒(méi)有立刻去更新 DOM ,而是將修改數(shù)據(jù)的操作放在了一個(gè)異步操作隊(duì)列中,如果一直修改相同數(shù)據(jù),異步操作隊(duì)列還會(huì)進(jìn)行去重,等待同一事件循環(huán)中的所有數(shù)據(jù)變化完成之后,會(huì)將隊(duì)列中的事件拿來(lái)進(jìn)行處理,進(jìn)行 DOM 的更新
第二次的 console.log 是放到 this.$nextTick 回調(diào)函數(shù)中的,此時(shí)獲取到的是新值,是因?yàn)?nextTick 的回調(diào)函數(shù)是在 DOM 更新之后觸發(fā)的

三、nextTick 原理

將傳入的回調(diào)函數(shù)包裝成異步任務(wù),異步任務(wù)又分微任務(wù)和宏任務(wù),為了盡快執(zhí)行所以優(yōu)先選擇微任務(wù);

nextTick 提供了四種異步方法 Promise.then、MutationObserver、setImmediate、setTimeout(fn,0)

源碼解讀

源碼位置 core/util/next-tick

源碼并不復(fù)雜,三個(gè)函數(shù),60幾行代碼,沉下心去看!

Tips:為了便于理解我調(diào)整了源碼中 nextTick、timerFunc、flushCallbacks 三個(gè)函數(shù)的書(shū)寫(xiě)順序

import { noop } from 'shared/util'
import { handleError } from './error'
import { isIE, isIOS, isNative } from './env'
 
//  上面三行與核心代碼關(guān)系不大,了解即可
//  noop 表示一個(gè)無(wú)操作空函數(shù),用作函數(shù)默認(rèn)值,防止傳入 undefined 導(dǎo)致報(bào)錯(cuò)
//  handleError 錯(cuò)誤處理函數(shù)
//  isIE, isIOS, isNative 環(huán)境判斷函數(shù),
//  isNative 判斷某個(gè)屬性或方法是否原生支持,如果不支持或通過(guò)第三方實(shí)現(xiàn)支持都會(huì)返回 false
 
 
export let isUsingMicroTask = false     // 標(biāo)記 nextTick 最終是否以微任務(wù)執(zhí)行
 
const callbacks = []     // 存放調(diào)用 nextTick 時(shí)傳入的回調(diào)函數(shù)
let pending = false     // 標(biāo)記是否已經(jīng)向任務(wù)隊(duì)列中添加了一個(gè)任務(wù),如果已經(jīng)添加了就不能再添加了
    // 當(dāng)向任務(wù)隊(duì)列中添加了任務(wù)時(shí),將 pending 置為 true,當(dāng)任務(wù)被執(zhí)行時(shí)將 pending 置為 false
    // 
 
 
// 聲明 nextTick 函數(shù),接收一個(gè)回調(diào)函數(shù)和一個(gè)執(zhí)行上下文作為參數(shù)
// 回調(diào)的 this 自動(dòng)綁定到調(diào)用它的實(shí)例上
export function nextTick(cb?: Function, ctx?: Object) {
    let _resolve
    // 將傳入的回調(diào)函數(shù)存放到數(shù)組中,后面會(huì)遍歷執(zhí)行其中的回調(diào)
    callbacks.push(() => {
        if (cb) {   // 對(duì)傳入的回調(diào)進(jìn)行 try catch 錯(cuò)誤捕獲
            try {
                cb.call(ctx)
            } catch (e) {    // 進(jìn)行統(tǒng)一的錯(cuò)誤處理
                handleError(e, ctx, 'nextTick')
            }
        } else if (_resolve) {
            _resolve(ctx)
        }
    })
    
    // 如果當(dāng)前沒(méi)有在 pending 的回調(diào),
    // 就執(zhí)行 timeFunc 函數(shù)選擇當(dāng)前環(huán)境優(yōu)先支持的異步方法
    if (!pending) {
        pending = true
        timerFunc()
    }
    
    // 如果沒(méi)有傳入回調(diào),并且當(dāng)前環(huán)境支持 promise,就返回一個(gè) promise
    // 在返回的這個(gè) promise.then 中 DOM 已經(jīng)更新好了,
    if (!cb && typeof Promise !== 'undefined') {
        return new Promise(resolve => {
            _resolve = resolve
        })
    }
}
 
 
// 判斷當(dāng)前環(huán)境優(yōu)先支持的異步方法,優(yōu)先選擇微任務(wù)
// 優(yōu)先級(jí):Promise---> MutationObserver---> setImmediate---> setTimeout
// setTimeout 可能產(chǎn)生一個(gè) 4ms 的延遲,而 setImmediate 會(huì)在主線程執(zhí)行完后立刻執(zhí)行
// setImmediate 在 IE10 和 node 中支持
 
// 當(dāng)在同一輪事件循環(huán)中多次調(diào)用 nextTick 時(shí) ,timerFunc 只會(huì)執(zhí)行一次
 
let timerFunc   
// 判斷當(dāng)前環(huán)境是否原生支持 promise
if (typeof Promise !== 'undefined' && isNative(Promise)) {  // 支持 promise
    const p = Promise.resolve()
    timerFunc = () => {
       // 用 promise.then 把 flushCallbacks 函數(shù)包裹成一個(gè)異步微任務(wù)
        p.then(flushCallbacks)
        if (isIOS) setTimeout(noop)
        // 這里的 setTimeout 是用來(lái)強(qiáng)制刷新微任務(wù)隊(duì)列的
        // 因?yàn)樵?ios 下 promise.then 后面沒(méi)有宏任務(wù)的話,微任務(wù)隊(duì)列不會(huì)刷新
    }
    // 標(biāo)記當(dāng)前 nextTick 使用的微任務(wù)
    isUsingMicroTask = true
    
    
    // 如果不支持 promise,就判斷是否支持 MutationObserver
    // 不是IE環(huán)境,并且原生支持 MutationObserver,那也是一個(gè)微任務(wù)
} else if (!isIE && typeof MutationObserver !== 'undefined' && (
    isNative(MutationObserver) ||
    MutationObserver.toString() === '[object MutationObserverConstructor]'
)) {
    let counter = 1
    // new 一個(gè) MutationObserver 類
    const observer = new MutationObserver(flushCallbacks) 
    // 創(chuàng)建一個(gè)文本節(jié)點(diǎn)
    const textNode = document.createTextNode(String(counter))   
    // 監(jiān)聽(tīng)這個(gè)文本節(jié)點(diǎn),當(dāng)數(shù)據(jù)發(fā)生變化就執(zhí)行 flushCallbacks 
    observer.observe(textNode, { characterData: true })
    timerFunc = () => {
        counter = (counter + 1) % 2
        textNode.data = String(counter)  // 數(shù)據(jù)更新
    }
    isUsingMicroTask = true    // 標(biāo)記當(dāng)前 nextTick 使用的微任務(wù)
    
    
    // 判斷當(dāng)前環(huán)境是否原生支持 setImmediate
} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
    timerFunc = () => { setImmediate(flushCallbacks)  }
} else {
 
    // 以上三種都不支持就選擇 setTimeout
    timerFunc = () => { setTimeout(flushCallbacks, 0) }
}
 
 
// 如果多次調(diào)用 nextTick,會(huì)依次執(zhí)行上面的方法,將 nextTick 的回調(diào)放在 callbacks 數(shù)組中
// 最后通過(guò) flushCallbacks 函數(shù)遍歷 callbacks 數(shù)組的拷貝并執(zhí)行其中的回調(diào)
function flushCallbacks() {
    pending = false    
    const copies = callbacks.slice(0)    // 拷貝一份 callbacks
    callbacks.length = 0    // 清空 callbacks
    for (let i = 0; i < copies.length; i++) {    // 遍歷執(zhí)行傳入的回調(diào)
        copies[i]()
    }
}
 
// 為什么要拷貝一份 callbacks
 
// 用 callbacks.slice(0) 將 callbacks 拷貝出來(lái)一份,
// 是因?yàn)榭紤]到在 nextTick 回調(diào)中可能還會(huì)調(diào)用 nextTick 的情況,
// 如果在 nextTick 回調(diào)中又調(diào)用了一次 nextTick,則又會(huì)向 callbacks 中添加回調(diào),
// 而 nextTick 回調(diào)中的 nextTick 應(yīng)該放在下一輪執(zhí)行,
// 否則就可能出現(xiàn)一直循環(huán)的情況,
// 所以需要將 callbacks 復(fù)制一份出來(lái)然后清空,再遍歷備份列表執(zhí)行回調(diào)

到此這篇關(guān)于淺析vue中$nextTick的作用與原理的文章就介紹到這了,更多相關(guān)vue $nextTick內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 淺談VueUse中useAsyncState的實(shí)現(xiàn)原理

    淺談VueUse中useAsyncState的實(shí)現(xiàn)原理

    useAsyncState?是 VueUse 庫(kù)中提供的一個(gè)實(shí)用工具,它用于處理異步狀態(tài),本文主要介紹了VueUse中useAsyncState的實(shí)現(xiàn)及其原理,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-08-08
  • vue條件渲染列表渲染原理示例詳解

    vue條件渲染列表渲染原理示例詳解

    這篇文章主要為大家介紹了vue條件渲染列表渲染原理示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-07-07
  • 如何在Vue中獲取DOM元素的實(shí)際寬高

    如何在Vue中獲取DOM元素的實(shí)際寬高

    使用vue的時(shí)候,想要獲得一個(gè)指定的元素的高度時(shí),下面這篇文章主要給大家介紹了關(guān)于如何在Vue中獲取DOM元素的實(shí)際寬高的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-09-09
  • web前端vue之vuex單獨(dú)一文件使用方式實(shí)例詳解

    web前端vue之vuex單獨(dú)一文件使用方式實(shí)例詳解

    Vuex 是一個(gè)專為 Vue.js 應(yīng)用程序開(kāi)發(fā)的狀態(tài)管理模式。這篇文章主要介紹了web前端vue:vuex單獨(dú)一文件使用方式,需要的朋友可以參考下
    2018-01-01
  • Vue純前端如何實(shí)現(xiàn)導(dǎo)出簡(jiǎn)單Excel表格的功能

    Vue純前端如何實(shí)現(xiàn)導(dǎo)出簡(jiǎn)單Excel表格的功能

    這篇文章主要介紹了如何在Vue項(xiàng)目中使用vue-json-excel插件實(shí)現(xiàn)Excel表格的導(dǎo)出功能,包括安裝依賴、引入插件、使用組件、設(shè)置表頭和數(shù)據(jù)、處理空數(shù)據(jù)情況、源代碼修改以解決常見(jiàn)問(wèn)題,需要的朋友可以參考下
    2025-01-01
  • vue-element-admin關(guān)閉eslint的校驗(yàn)方式

    vue-element-admin關(guān)閉eslint的校驗(yàn)方式

    這篇文章主要介紹了vue-element-admin關(guān)閉eslint的校驗(yàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • vue 中 命名視圖的用法實(shí)例詳解

    vue 中 命名視圖的用法實(shí)例詳解

    這篇文章主要介紹了vue 中 命名視圖的用法,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2019-08-08
  • vue3中setup-script的應(yīng)用實(shí)例

    vue3中setup-script的應(yīng)用實(shí)例

    script-setup是一個(gè)比較有爭(zhēng)議的新特性,作為 setup 函數(shù)的語(yǔ)法糖,褒貶不一,不過(guò)經(jīng)歷了幾次迭代之后,目前在體驗(yàn)上來(lái)說(shuō),感受還是非常棒的,這篇文章主要給大家介紹了關(guān)于vue3中setup-script應(yīng)用的相關(guān)資料,需要的朋友可以參考下
    2022-01-01
  • 教你使用vue-cli快速構(gòu)建的小說(shuō)閱讀器

    教你使用vue-cli快速構(gòu)建的小說(shuō)閱讀器

    這篇文章主要介紹了vue-cli構(gòu)建的小說(shuō)閱讀器,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-05-05
  • vue實(shí)現(xiàn)3D環(huán)形圖效果

    vue實(shí)現(xiàn)3D環(huán)形圖效果

    這篇文章主要為大家詳細(xì)介紹了vue實(shí)現(xiàn)3D環(huán)形圖效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-03-03

最新評(píng)論