最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Vue.js原理分析之nextTick實(shí)現(xiàn)詳解

 更新時間:2020年09月07日 09:04:50   作者:minijun2333  
這篇文章主要給大家介紹了關(guān)于Vue.js原理分析之nextTick實(shí)現(xiàn)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

前言

tips:第一次發(fā)技術(shù)文章,篇幅比較簡短,主要采取文字和關(guān)鍵代碼表現(xiàn)的形式,希望幫助到大家。(若有不正確還請多多指正)

nextTick作用和用法

用法:nextTick接收一個回調(diào)函數(shù)作為參數(shù),它的作用是將回調(diào)延遲到下一次DOM更新之后執(zhí)行,如果沒有提供回調(diào)函數(shù)參數(shù)且在支持Promise的環(huán)境中,nextTick將返回一個Promise。
適用場景:開發(fā)過程中,開發(fā)者需要在更新完數(shù)據(jù)之后,需要對新DOM做一些操作,其實(shí)我們當(dāng)時無法對新DOM進(jìn)行操作,因?yàn)檫@時候還沒有重新渲染,這時候nextTick就派上了用場。

nextTick實(shí)現(xiàn)原理

下面我們介紹下nextTick工作原理:

首先我們應(yīng)該了解到更新完數(shù)據(jù)(狀態(tài))之后,DOM更新這個動作并不是同步進(jìn)行的,而是異步的。Vue.js中有一個隊(duì)列,每當(dāng)需要渲染時,會將Watcher推送到這個隊(duì)列中,等下一次事件循環(huán)中再讓W(xué)atcher觸發(fā)渲染流程。這里我們可能會有兩個疑問: 

**1.為什么更新DOM是異步的?**

我們知道從Vue2.0開始使用虛擬DOM進(jìn)行渲染,變化偵測只發(fā)送到組件級別,組件內(nèi)部則通過虛擬DOM的diff(比對)而進(jìn)行局部渲染,而在同一次事件循環(huán)中組件假如收到兩份通知,組件是否會進(jìn)行兩次渲染呢?事實(shí)上一次事件循環(huán)組件會在所有狀態(tài)修改完畢之后只進(jìn)行一次渲染操作。

**2.什么是事件循環(huán)?**

javascript是單線程腳本語言,它具有非阻塞特性,之所以非阻塞是由于在處理異步代碼時,主線程會掛起這個任務(wù),當(dāng)異步任務(wù)處理完畢之后會根據(jù)一定的規(guī)則去執(zhí)行異步任務(wù)的回調(diào),異步任務(wù)分宏任務(wù)(macrotast)和微任務(wù)(microtast),它們會被分配到不同的隊(duì)列中,當(dāng)執(zhí)行棧所有任務(wù)執(zhí)行完畢之后,會先檢查微任務(wù)隊(duì)列中是否有事件存在,優(yōu)先執(zhí)行微任務(wù)隊(duì)列事件對應(yīng)的回調(diào),直至為空。然后再執(zhí)行宏任務(wù)隊(duì)列中事件的回調(diào)。無限重復(fù)這個過程,形成一個無限循環(huán)就叫做事件循環(huán)。

常見微任務(wù)包括:Promise 、MutationObserver、Object.observer、process.nextTick等

常見宏任務(wù)包括:setTimeout、setInterval、setImmediate、MessageChannel、requestAnimation、UI交互事件等

微任務(wù)如何注冊?

nextTick會將回調(diào)添加到異步任務(wù)隊(duì)列中延遲執(zhí)行,在執(zhí)行回調(diào)前,反復(fù)調(diào)用nextTick,Vue并不會反復(fù)添加到任務(wù)隊(duì)列中,只會向任務(wù)隊(duì)列添加一個任務(wù),多次使用nextTick只會將回調(diào)添加到回調(diào)列表緩存起來,當(dāng)任務(wù)觸發(fā)時,會清空回調(diào)列表并依次執(zhí)行所有回調(diào) ,具體代碼如下: 

const callbacks = []
let pending = false

function flushCallbacks(){ //執(zhí)行回調(diào)
  pending = false
  const copies = callbacks.slice(0)
  callbacks.length = 0 //清空回調(diào)隊(duì)列
  for(let i = 0; i < copies.length; i++) {
    copies[i]()
  }
}
let microTimerFunc
const p = Promise.resolve()
microTimerFunc = () => { //注冊微任務(wù)
  p.then(flushCallbacks)
}

export function nextTick(cb,ctx){
  callbacks.push(()=>{
    if(cb){
      cb.call(ctx)
    }
  })
  if(!pending){
    pending = true //將pending設(shè)置為true,保證任務(wù)在依次事件循環(huán)中不會重復(fù)添加
    microTimerFunc()
  }
}

由于微任務(wù)優(yōu)先級太高,可能在某些場景下需要使用到宏任務(wù),所以Vue提供了可以強(qiáng)制使用宏任務(wù)的方法withMacroTask。具體實(shí)現(xiàn)如下:

const callbacks = []
let pending = false

function flushCallbacks(){ //執(zhí)行回調(diào)
  pending = false
  const copies = callbacks.slice(0)
  callbacks.length = 0 //清空回調(diào)隊(duì)列
  for(let i = 0; i < copies.length; i++) {
    copies[i]()
  }
}
let microTimerFunc
//新增代碼
let macroTimerFunc = function(){
  ...
}

let useMacroTask = false
const p = Promise.resolve()
microTimerFunc = () => { //注冊微任務(wù)
  p.then(flushCallbacks)
}

//新增代碼
export function withMacroTask(fn){
  return fn._withTask || fn._withTask = function()=>{
    useMacroTask = true
    const res = fn.apply(null,arguments)
    useMacroTask = false
    return res
  }
}

export function nextTick(cb,ctx){
  callbacks.push(()=>{
    if(cb){
      cb.call(ctx)
    }
  })
  if(!pending){
    pending = true //將pending設(shè)置為true,保證任務(wù)在依次事件循環(huán)中不會重復(fù)添加
    //修改代碼
    if(useMacroTask){
      macroTimerFunc()
    }else{
      microTimerFunc()
    }
  }
}

上面提供了一個withMacroTask方法強(qiáng)制使用宏任務(wù),通過useMacroTask變量進(jìn)行控制是否使用注冊宏任務(wù)執(zhí)行,withMacroTask實(shí)現(xiàn)很簡單,先將useMacroTask變量設(shè)置為true,然后執(zhí)行回調(diào),回調(diào)執(zhí)行之后再改回false。

宏任務(wù)是如何注冊?

注冊宏任務(wù)優(yōu)先使用setImmediate,但是存在兼容性問題,只能在IE中使用,所以使用MessageChannel作為備選方案,若以上都不支持則最后會使用setTimeout。具體實(shí)現(xiàn)如下:

if(typeof setImmediate !== 'undefined' && isNative(setImmediate)){
  macroTimerFunc = ()=>{
    setImmediate(flushCallbacks)
  }
} else if(
  typeof MessageChannel !== 'undefined' && 
  (isNative(MessageChannel) || MessageChannel.toString() === '[Object MessageChannelConstructor]')
){
  const channel = new MessageChannel()
  const port = channel.port2
  channel.port1.onmessage = flushCallbacks
  macroTimerFunc = ()=>{
    port.postMessage(1)
  }
} else {
  macroTimerFunc = ()=>{
    setTimout(flushCallbacks,0)
  }
}

microTimerFunc的實(shí)現(xiàn)方法是通過Promise.then,但是并不是所有瀏覽器都支持Promise,當(dāng)不支持的時候采取降級為宏任務(wù)方式

if(typeof Promise !== 'undefined' && isNative(Promise)){
  const p = Promise.resolve()
  microTimerFunc = ()=>{
    p.then(flushCallbacks)
  }
} else {
  microTimerFunc = macroTimerFunc
}

若未提供回調(diào)且環(huán)境支持Promise情況下,nextTick會返回一個Promise,具體實(shí)現(xiàn)如下:

export function nextTick(cb, ctx) {
  let _resolve
  callbacks.push(()=>{
    if(cb){
      cb.call(ctx)
    }else{
      _resolve(ctx)
    }
  })

  if(!pending){
    pending = true
    if(useMacroTask){
      macroTimerFunc()
    }else{
      microTimerFunc()
    }
  }

  if(typeof Promise !== 'undefined' && isNative(Promise)){
    return new Promise(resolve=>{
      _resolve = resolve
    })
  }
}

以上是nextTick運(yùn)行原理的設(shè)計(jì),完整代碼如下:

const callbacks = []
let pending = false

function flushCallbacks(){ //執(zhí)行回調(diào)
  pending = false
  const copies = callbacks.slice(0)
  callbacks.length = 0 //清空回調(diào)隊(duì)列
  for(let i = 0; i < copies.length; i++) {
    copies[i]()
  }
}
let microTimerFunc
let macroTimerFunc 
let useMacroTask = false

//注冊宏任務(wù)
if(typeof setImmediate !== 'undefined' && isNative(setImmediate)){
  macroTimerFunc = ()=>{
    setImmediate(flushCallbacks)
  }
} else if(
  typeof MessageChannel !== 'undefined' && 
  (isNative(MessageChannel) || MessageChannel.toString() === '[Object MessageChannelConstructor]')
){
  const channel = new MessageChannel()
  const port = channel.port2
  channel.port1.onmessage = flushCallbacks
  macroTimerFunc = ()=>{
    port.postMessage(1)
  }
} else {
  macroTimerFunc = ()=>{
    setTimout(flushCallbacks,0)
  }
}

//微任務(wù)注冊
if(typeof Promise !== 'undefined' && isNative(Promise)){
  const p = Promise.resolve()
  microTimerFunc = ()=>{
    p.then(flushCallbacks)
  }
} else {//降級處理
  microTimerFunc = macroTimerFunc
}

export function withMacroTask(fn){
  return fn._withTask || fn._withTask = function()=>{
    useMacroTask = true
    const res = fn.apply(null,arguments)
    useMacroTask = false
    return res
  }
}

export function nextTick(cb,ctx){
  let _resolve
  callbacks.push(()=>{
    if(cb){
      cb.call(ctx)
    }else{
      _resolve(ctx)
    }
  })
  if(!pending){
    pending = true //將pending設(shè)置為true,保證任務(wù)在依次事件循環(huán)中不會重復(fù)添加
    //修改代碼
    if(useMacroTask){
      macroTimerFunc()
    }else{
      microTimerFunc()
    }
  }

  if(typeof Promise !== 'undefined' && isNative(Promise)){
    return new Promise(resolve=>{
      _resolve = resolve
    })
  }
}

以上便是對nextTick的實(shí)現(xiàn)原理的全部介紹。

參考資料

Vue.js深入淺出

總結(jié)

到此這篇關(guān)于Vue.js原理分析之nextTick實(shí)現(xiàn)詳解的文章就介紹到這了,更多相關(guān)Vue.js原理之nextTick實(shí)現(xiàn)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • vue中組件間多種傳值方式案例詳解

    vue中組件間多種傳值方式案例詳解

    vue中每個組件都是隔離的,包括父組件和子組件,各組件之間需要數(shù)據(jù)通信,就涉及到了組件傳值,本文給大家介紹vue中組件間多種傳值方式案例,感興趣的朋友跟隨小編一起看看吧
    2024-03-03
  • vue3封裝自己的分頁組件

    vue3封裝自己的分頁組件

    這篇文章主要為大家詳細(xì)介紹了vue3封裝自己的分頁組件,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • vue實(shí)現(xiàn)循環(huán)滾動列表

    vue實(shí)現(xiàn)循環(huán)滾動列表

    這篇文章主要為大家詳細(xì)介紹了vue實(shí)現(xiàn)循環(huán)滾動列表,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-06-06
  • vue中使用geobuf的示例詳解

    vue中使用geobuf的示例詳解

    這篇文章主要介紹了vue中使用geobuf的示例詳細(xì),本文結(jié)合示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-04-04
  • Vue實(shí)現(xiàn)省市區(qū)級聯(lián)下拉選擇框

    Vue實(shí)現(xiàn)省市區(qū)級聯(lián)下拉選擇框

    這篇文章主要為大家詳細(xì)介紹了Vue實(shí)現(xiàn)省市區(qū)級聯(lián)下拉選擇框,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • Vue3利用自定義指令進(jìn)行內(nèi)容控制的實(shí)現(xiàn)方法

    Vue3利用自定義指令進(jìn)行內(nèi)容控制的實(shí)現(xiàn)方法

    Vue3作為一個漸進(jìn)式JavaScript框架,提供了強(qiáng)大的自定義指令功能,使得權(quán)限控制變得既簡單又高效,本文將詳細(xì)介紹如何在Vue3中使用自定義指令來判斷內(nèi)容是否顯示,以滿足不同用戶權(quán)限下的界面展示需求,需要的朋友可以參考下
    2024-04-04
  • vue子組件中使用window.onresize()只執(zhí)行一次問題

    vue子組件中使用window.onresize()只執(zhí)行一次問題

    這篇文章主要介紹了vue子組件中使用window.onresize()只執(zhí)行一次問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • vue element 關(guān)閉當(dāng)前tab 跳轉(zhuǎn)到上一路由操作

    vue element 關(guān)閉當(dāng)前tab 跳轉(zhuǎn)到上一路由操作

    這篇文章主要介紹了vue element 關(guān)閉當(dāng)前tab 跳轉(zhuǎn)到上一路由操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-07-07
  • vue如何調(diào)用瀏覽器分享功能詳解

    vue如何調(diào)用瀏覽器分享功能詳解

    這篇文章主要給大家介紹了關(guān)于vue如何調(diào)用瀏覽器分享的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03
  • vant的Loading加載動畫組件的使用(通過接口拿數(shù)據(jù)時顯示加載狀態(tài))

    vant的Loading加載動畫組件的使用(通過接口拿數(shù)據(jù)時顯示加載狀態(tài))

    這篇文章主要介紹了vant的Loading加載動畫組件的使用,通過接口拿數(shù)據(jù)時顯示加載狀態(tài),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2024-01-01

最新評論

车致| 西乡县| 酒泉市| 中西区| 栾城县| 镇沅| 昌黎县| 台南县| 揭东县| 射阳县| 兰溪市| 清原| 桑植县| 天气| 巴东县| 鹤岗市| 自贡市| 乐平市| 志丹县| 舒兰市| 天柱县| 闸北区| 谷城县| 岗巴县| 新干县| 什邡市| 和田市| 敦化市| 清新县| 两当县| 乌鲁木齐县| 冀州市| 泰来县| 高安市| 新密市| 石门县| 德保县| 新兴县| 雷州市| 惠州市| 东阿县|