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

詳解Vue的異步更新實(shí)現(xiàn)原理

 更新時(shí)間:2020年12月22日 09:03:05   作者:Liqiuyue  
這篇文章主要介紹了Vue的異步更新實(shí)現(xiàn)原理,幫助大家更好的理解和使用vue,感興趣的朋友可以了解下

最近面試總是會(huì)被問(wèn)到這么一個(gè)問(wèn)題:在使用vue的時(shí)候,將for循環(huán)中聲明的變量i從1增加到100,然后將i展示到頁(yè)面上,頁(yè)面上的i是從1跳到100,還是會(huì)怎樣?答案當(dāng)然是只會(huì)顯示100,并不會(huì)有跳轉(zhuǎn)的過(guò)程。

怎么可以讓頁(yè)面上有從1到100顯示的過(guò)程呢,就是用setTimeout或者Promise.then等方法去模擬。

講道理,如果不在vue里,單獨(dú)運(yùn)行這段程序的話,輸出一定是從1到100,但是為什么在vue中就不一樣了呢?

for(let i=1; i<=100; i++){
	console.log(i);
}

這就涉及到Vue底層的異步更新原理,也要說(shuō)一說(shuō)nextTick的實(shí)現(xiàn)。不過(guò)在說(shuō)nextTick之前,有必要先介紹一下JS的事件運(yùn)行機(jī)制。

JS運(yùn)行機(jī)制

眾所周知,JS是基于事件循環(huán)的單線程的語(yǔ)言。 執(zhí)行的步驟大致是:

  1. 當(dāng)代碼執(zhí)行時(shí),所有同步的任務(wù)都在主線程上執(zhí)行,形成一個(gè)執(zhí)行棧;
  2. 在主線程之外還有一個(gè)任務(wù)隊(duì)列(task queue),只要異步任務(wù)有了運(yùn)行結(jié)果就在任務(wù)隊(duì)列中放置一個(gè)事件;
  3. 一旦執(zhí)行棧中所有同步任務(wù)執(zhí)行完畢(主線程代碼執(zhí)行完畢),此時(shí)主線程不會(huì)空閑而是去讀取任務(wù)隊(duì)列。此時(shí),異步的任務(wù)就結(jié)束等待的狀態(tài)被執(zhí)行。
  4. 主線程不斷重復(fù)以上的步驟。

我們把主線程執(zhí)行一次的過(guò)程叫一個(gè)tick,所以nextTick就是下一個(gè)tick的意思,也就是說(shuō)用nextTick的場(chǎng)景就是我們想在下一個(gè)tick做一些事的時(shí)候。

所有的異步任務(wù)結(jié)果都是通過(guò)任務(wù)隊(duì)列來(lái)調(diào)度的。而任務(wù)分為兩類:宏任務(wù)(macro task)和微任務(wù)(micro task)。它們之間的執(zhí)行規(guī)則就是每個(gè)宏任務(wù)結(jié)束后都要將所有微任務(wù)清空。 常見(jiàn)的宏任務(wù)有setTimeout/MessageChannel/postMessage/setImmediate,微任務(wù)有MutationObsever/Promise.then。

想要透徹學(xué)習(xí)事件循環(huán),推薦Jake在JavaScript全球開(kāi)發(fā)者大會(huì)的演講,保證講懂!

nextTick原理

派發(fā)更新

大家都知道vue的響應(yīng)式的靠依賴收集和派發(fā)更新來(lái)實(shí)現(xiàn)的。在修改數(shù)組之后的派發(fā)更新過(guò)程,會(huì)觸發(fā)setter的邏輯,執(zhí)行dep.notify():

// src/core/observer/watcher.js
class Dep {
	notify() {
  	//subs是Watcher的實(shí)例數(shù)組
  	const subs = this.subs.slice()
    for(let i=0, l=subs.length; i<l; i++){
    	subs[i].update()
    }
  }
}

遍歷subs里每一個(gè)Watcher實(shí)例,然后調(diào)用實(shí)例的update方法,下面我們來(lái)看看update是怎么去更新的:

class Watcher {
	update() {
  	...
  	//各種情況判斷之后
    else{
    	queueWatcher(this)
    }
  }
}

update執(zhí)行后又走到了queueWatcher,那就繼續(xù)去看看queueWatcher干啥了(希望不要繼續(xù)套娃了:

//queueWatcher 定義在 src/core/observer/scheduler.js
const queue: Array<Watcher> = []
let has: { [key: number]: ?true } = {}
let waiting = false
let flushing = false
let index = 0

export function queueWatcher(watcher: Watcher) {
	const id = watcher.id
  //根據(jù)id是否重復(fù)做優(yōu)化
  if(has[id] == null){
  	has[id] = true
    if(!flushing){
    	queue.push(watcher)
    }else{
    	let i=queue.length - 1
      while(i > index && queue[i].id > watcher.id){
      	i--
      }
      queue.splice(i + 1, 0, watcher)
    }
    
  	if(!waiting){
  		waiting = true
    	//flushSchedulerQueue函數(shù): Flush both queues and run the watchers
    	nextTick(flushSchedulerQueue)
  	}
  }
}

這里queue在pushwatcher時(shí)是根據(jù)id和flushing做了一些優(yōu)化的,并不會(huì)每次數(shù)據(jù)改變都觸發(fā)watcher的回調(diào),而是把這些watcher先添加到⼀個(gè)隊(duì)列⾥,然后在nextTick后執(zhí)⾏flushSchedulerQueue。

flushSchedulerQueue函數(shù)是保存更新事件的queue的一些加工,讓更新可以滿足Vue更新的生命周期。

這里也解釋了為什么for循環(huán)不能導(dǎo)致頁(yè)面更新,因?yàn)閒or是主線程的代碼,在一開(kāi)始執(zhí)行數(shù)據(jù)改變就會(huì)將它push到queue里,等到for里的代碼執(zhí)行完畢后i的值已經(jīng)變化為100時(shí),這時(shí)vue才走到nextTick(flushSchedulerQueue)這一步。

nextTick源碼

接著打開(kāi)vue2.x的源碼,目錄core/util/next-tick.js,代碼量很小,加上注釋才110行,是比較好理解的。

const callbacks = []
let pending = false

export function nextTick (cb?: Function, ctx?: Object) {
 let _resolve
 callbacks.push(() => {
  if (cb) {
   try {
    cb.call(ctx)
   } catch (e) {
    handleError(e, ctx, 'nextTick')
   }
  } else if (_resolve) {
   _resolve(ctx)
  }
 })
 if (!pending) {
  pending = true
  timerFunc()
 }

首先將傳入的回調(diào)函數(shù)cb(上節(jié)的flushSchedulerQueue)壓入callbacks數(shù)組,最后通過(guò)timerFunc函數(shù)一次性解決。

let timerFunc

if (typeof Promise !== 'undefined' && isNative(Promise)) {
 const p = Promise.resolve()
 timerFunc = () => {
  p.then(flushCallbacks)
  if (isIOS) setTimeout(noop)
  }
 isUsingMicroTask = true
} else if (!isIE && typeof MutationObserver !== 'undefined' && (
 isNative(MutationObserver) ||
 // PhantomJS and iOS 7.x
 MutationObserver.toString() === '[object MutationObserverConstructor]'
)) {
 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(flushCallbacks)
 }
} else {
 timerFunc = () => {
  setTimeout(flushCallbacks, 0)
 }
}

timerFunc下面一大片if else是在判斷不同的設(shè)備和不同情況下選用哪種特性去實(shí)現(xiàn)異步任務(wù):優(yōu)先檢測(cè)是否原生⽀持Promise,不⽀持的話再去檢測(cè)是否⽀持MutationObserver,如果都不行就只能嘗試宏任務(wù)實(shí)現(xiàn),首先是setImmediate,這是⼀個(gè)⾼版本 IE 和 Edge 才⽀持的特性,如果都不⽀持的話最后就會(huì)降級(jí)為 setTimeout 0。

這⾥使⽤callbacks⽽不是直接在nextTick中執(zhí)⾏回調(diào)函數(shù)的原因是保證在同⼀個(gè) tick 內(nèi)多次執(zhí)⾏nextTick,不會(huì)開(kāi)啟多個(gè)異步任務(wù),⽽把這些異步任務(wù)都?jí)撼?#12032;個(gè)同步任務(wù),在下⼀個(gè) tick 執(zhí)⾏完畢。

nextTick使用

nextTick不僅是vue的源碼文件,更是vue的一個(gè)全局API。下面來(lái)看看怎么使用吧。

當(dāng)設(shè)置 vm.someData = 'new value',該組件不會(huì)立即重新渲染。當(dāng)刷新隊(duì)列時(shí),組件會(huì)在下一個(gè)事件循環(huán)tick中更新。多數(shù)情況我們不需要關(guān)心這個(gè)過(guò)程,但是如果你想基于更新后的 DOM 狀態(tài)來(lái)做點(diǎn)什么,這就可能會(huì)有些棘手。雖然 Vue.js 通常鼓勵(lì)開(kāi)發(fā)人員使用數(shù)據(jù)驅(qū)動(dòng)的方式思考,避免直接接觸 DOM,但是有時(shí)我們必須要這么做。為了在數(shù)據(jù)變化之后等待 Vue 完成更新 DOM,可以在數(shù)據(jù)變化之后立即使用Vue.nextTick(callback)。這樣回調(diào)函數(shù)將在 DOM 更新完成后被調(diào)用。

官網(wǎng)用例:

<div id="example">{{message}}</div>
var vm = new Vue({
 el: '#example',
 data: {
  message: '123'
 }
})
vm.message = 'new message' // 更改數(shù)據(jù)

vm.$el.textContent === 'new message' // false
Vue.nextTick(function () {
 vm.$el.textContent === 'new message' // true
})

并且因?yàn)?code>$nextTick() 返回一個(gè) Promise 對(duì)象,所以也可以使用async/await 語(yǔ)法去處理事件,非常方便。

以上就是詳解Vue的異步更新實(shí)現(xiàn)原理的詳細(xì)內(nèi)容,更多關(guān)于vue 異步更新的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Vue中實(shí)現(xiàn)v-for循環(huán)遍歷圖片的方法

    Vue中實(shí)現(xiàn)v-for循環(huán)遍歷圖片的方法

    這篇文章主要介紹了Vue中實(shí)現(xiàn)v-for循環(huán)遍歷圖片的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • create?vite?實(shí)例源碼解析

    create?vite?實(shí)例源碼解析

    這篇文章主要為大家介紹了create?vite?實(shí)例源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-11-11
  • Vue自定義指令詳解

    Vue自定義指令詳解

    這篇文章主要為大家介紹了Vue自定義指令,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助
    2021-11-11
  • vue使用vue-video-player插件播放視頻的步驟講解

    vue使用vue-video-player插件播放視頻的步驟講解

    在最近的項(xiàng)目中有一個(gè)視頻播放的功能,在之前的項(xiàng)目中沒(méi)有接觸過(guò)類似的功能,第一次接觸,把具體操作步驟一下,這篇文章主要給大家介紹了關(guān)于vue使用vue-video-player插件播放視頻的相關(guān)資料,需要的朋友可以參考下
    2022-12-12
  • Vue使用Prism實(shí)現(xiàn)頁(yè)面代碼高亮展示示例

    Vue使用Prism實(shí)現(xiàn)頁(yè)面代碼高亮展示示例

    這篇文章主要為大家介紹了Vue使用Prism實(shí)現(xiàn)頁(yè)面代碼高亮展示示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-07-07
  • vue3中reactive和ref的實(shí)現(xiàn)與區(qū)別詳解

    vue3中reactive和ref的實(shí)現(xiàn)與區(qū)別詳解

    reactive和ref都是vue3實(shí)現(xiàn)響應(yīng)式系統(tǒng)的api,他們是如何實(shí)現(xiàn)響應(yīng)式的呢,reactive和ref又有什么區(qū)別呢,下面小編就來(lái)和大家詳細(xì)講講,希望對(duì)大家有所幫助
    2023-10-10
  • vue限制實(shí)現(xiàn)不登錄無(wú)法進(jìn)入其他頁(yè)面

    vue限制實(shí)現(xiàn)不登錄無(wú)法進(jìn)入其他頁(yè)面

    本文主要介紹了vue限制實(shí)現(xiàn)不登錄無(wú)法進(jìn)入其他頁(yè)面,vue限制不登錄,通過(guò)url進(jìn)入其他頁(yè)面強(qiáng)制回到登錄頁(yè)面;已經(jīng)登錄的情況下,不可以再進(jìn)入登錄界面,感興趣的可以了解一下
    2024-01-01
  • Vue?3.0?v-for中的Ref數(shù)組用法小結(jié)

    Vue?3.0?v-for中的Ref數(shù)組用法小結(jié)

    在?Vue?2?中,在?v-for?里使用的?ref?attribute會(huì)用ref?數(shù)組填充相應(yīng)的?$refs?property,本文給大家介紹Vue?3.0?v-for中的Ref數(shù)組的相關(guān)知識(shí),感興趣的朋友一起看看吧
    2023-12-12
  • 利用Vue.js制作一個(gè)拼圖華容道小游戲

    利用Vue.js制作一個(gè)拼圖華容道小游戲

    這篇文章主要為大家詳細(xì)介紹了如何利用Vue.js編寫(xiě)一個(gè)拼圖華容道游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-04-04
  • vue-router實(shí)現(xiàn)webApp切換頁(yè)面動(dòng)畫(huà)效果代碼

    vue-router實(shí)現(xiàn)webApp切換頁(yè)面動(dòng)畫(huà)效果代碼

    本篇文章主要介紹了vue實(shí)現(xiàn)app頁(yè)面切換效果實(shí)例代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-05-05

最新評(píng)論

宿州市| 横峰县| 林芝县| 滨海县| 广汉市| 精河县| 河东区| 宁武县| 普兰店市| 东莞市| 西林县| 年辖:市辖区| 兴业县| 营山县| 台北县| 定结县| 泽普县| 白河县| 乐昌市| 扬中市| 离岛区| 靖远县| 邵东县| 浦东新区| 门源| 年辖:市辖区| 诸暨市| 繁峙县| 琼结县| 定安县| 长岭县| 绍兴县| 罗甸县| 平泉县| 揭阳市| 无锡市| 名山县| 肥乡县| 绵竹市| 巴林左旗| 揭东县|