国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 語言 > JavaScript > 正文

深入理解Vue nextTick 機制

2024-05-06 15:34:46
字體:
供稿:網(wǎng)友

我們先來看一段Vue的執(zhí)行代碼:

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

這段腳本執(zhí)行我們猜測1000m后會依次打印:1、2、3。但是實際效果中,只會輸出一次:3。為什么會出現(xiàn)這樣的情況?我們來一探究竟。

queueWatcher

我們定義 watch 監(jiān)聽 msg ,實際上會被Vue這樣調(diào)用 vm.$watch(keyOrFn, handler, options) 。 $watch 是我們初始化的時候,為 vm 綁定的一個函數(shù),用于創(chuàng)建 Watcher 對象。那么我們看看 Watcher 中是如何處理 handler 的:

this.deep = this.user = this.lazy = this.sync = false... update () {  if (this.lazy) {   this.dirty = true  } else if (this.sync) {   this.run()  } else {   queueWatcher(this)  } }...

初始設(shè)定 this.deep = this.user = this.lazy = this.sync = false ,也就是當觸發(fā) update 更新的時候,會去執(zhí)行 queueWatcher 方法:

const queue: Array<Watcher> = []let has: { [key: number]: ?true } = {}let waiting = falselet flushing = false...export function queueWatcher (watcher: Watcher) { const id = watcher.id if (has[id] == null) {  has[id] = true  if (!flushing) {   queue.push(watcher)  } else {   // if already flushing, splice the watcher based on its id   // if already past its id, it will be run next immediately.   let i = queue.length - 1   while (i > index && queue[i].id > watcher.id) {    i--   }   queue.splice(i + 1, 0, watcher)  }  // queue the flush  if (!waiting) {   waiting = true   nextTick(flushSchedulerQueue)  } }}

這里面的 nextTick(flushSchedulerQueue) 中的 flushSchedulerQueue 函數(shù)其實就是 watcher 的視圖更新:

function flushSchedulerQueue () { flushing = true let watcher, id ... for (index = 0; index < queue.length; index++) {  watcher = queue[index]  id = watcher.id  has[id] = null  watcher.run()  ... }}

另外,關(guān)于 waiting 變量,這是很重要的一個標志位,它保證 flushSchedulerQueue 回調(diào)只允許被置入 callbacks 一次。 接下來我們來看看 nextTick 函數(shù),在說 nexTick 之前,需要你對 Event Loop 、 microTask 、 macroTask 有一定的了解,Vue nextTick 也是主要用到了這些基礎(chǔ)原理。如果你還不了解,可以參考我的這篇文章 Event Loop 簡介 好了,下面我們來看一下他的實現(xiàn):

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]()  } } // An asynchronous deferring mechanism. // In pre 2.4, we used to use microtasks (Promise/MutationObserver) // but microtasks actually has too high a priority and fires in between // supposedly sequential events (e.g. #4521, #6690) or even between // bubbling of the same event (#6566). Technically setImmediate should be // the ideal choice, but it's not available everywhere; and the only polyfill // that consistently queues the callback after all DOM events triggered in the // same loop is by using MessageChannel. /* istanbul ignore if */ if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {  timerFunc = () => {   setImmediate(nextTickHandler)  } } else if (typeof MessageChannel !== 'undefined' && (  isNative(MessageChannel) ||  // PhantomJS  MessageChannel.toString() === '[object MessageChannelConstructor]' )) {  const channel = new MessageChannel()  const port = channel.port2  channel.port1.onmessage = nextTickHandler  timerFunc = () => {   port.postMessage(1)  } } else /* istanbul ignore next */ if (typeof Promise !== 'undefined' && isNative(Promise)) {  // use microtask in non-DOM environments, e.g. Weex  const p = Promise.resolve()  timerFunc = () => {   p.then(nextTickHandler)  } } else {  // fallback to setTimeout  timerFunc = () => {   setTimeout(nextTickHandler, 0)  } } return function queueNextTick (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()  }  // $flow-disable-line  if (!cb && typeof Promise !== 'undefined') {   return new Promise((resolve, reject) => {    _resolve = resolve   })  } }})()            
發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表

圖片精選

主站蜘蛛池模板: 中山市| 乡城县| 钟山县| 元江| 永顺县| 磴口县| 宜城市| 富宁县| 怀仁县| 柳林县| 西宁市| 二连浩特市| 巧家县| 紫云| 丽江市| 临沂市| 湛江市| 湟中县| 武山县| 阿克陶县| 麻栗坡县| 蒙自县| 霍林郭勒市| 卢氏县| 舒城县| 长宁县| 哈巴河县| 棋牌| 林芝县| 西藏| 成武县| 郎溪县| 肇东市| 安溪县| 古蔺县| 格尔木市| 博兴县| 常德市| 肃宁县| 赤城县| 河西区|