成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

vue源碼解析:nextTick

PrototypeZ / 2141人閱讀

摘要:對屬性值進(jìn)行修改操作時,如,實(shí)際上會觸發(fā)。下面看源碼,為方便越讀,源碼有刪減??偨Y(jié)為了保證性能,會把修改添加到異步任務(wù),所有同步代碼執(zhí)行完成后再統(tǒng)一修改,一次事件循環(huán)中的多次數(shù)據(jù)修改只會觸發(fā)一次。

1 nextTick的使用

vue中dom的更像并不是實(shí)時的,當(dāng)數(shù)據(jù)改變后,vue會把渲染watcher添加到異步隊(duì)列,異步執(zhí)行,同步代碼執(zhí)行完成后再統(tǒng)一修改dom,我們看下面的代碼。



export default {
  name: "index",
  data () {
    return {
      msg: "hello"
    }
  },
  mounted () {
    this.msg = "world"
    let box = document.getElementsByClassName("box")[0]
    console.log(box.innerHTML) // hello
  }
}

可以看到,修改數(shù)據(jù)后并不會立即更新dom ,dom的更新是異步的,無法通過同步代碼獲取,需要使用nextTick,在下一次事件循環(huán)中獲取。

this.msg = "world"
let box = document.getElementsByClassName("box")[0]
this.$nextTick(() => {
  console.log(box.innerHTML) // world
})

如果我們需要獲取數(shù)據(jù)更新后的dom信息,比如動態(tài)獲取寬高、位置信息等,需要使用nextTick。

2 數(shù)據(jù)變化dom更新與nextTick的原理分析 2.1 數(shù)據(jù)變化

vue雙向數(shù)據(jù)綁定依賴于ES5的Object.defineProperty,在數(shù)據(jù)初始化的時候,通過Object.defineProperty為每一個屬性創(chuàng)建gettersetter,把數(shù)據(jù)變成響應(yīng)式數(shù)據(jù)。對屬性值進(jìn)行修改操作時,如this.msg = world,實(shí)際上會觸發(fā)setter。下面看源碼,為方便越讀,源碼有刪減。

數(shù)據(jù)改變觸發(fā)set函數(shù)

Object.defineProperty(obj, key, {
  enumerable: true,
  configurable: true,
  // 數(shù)據(jù)修改后觸發(fā)set函數(shù) 經(jīng)過一系列操作 完成dom更新
  set: function reactiveSetter (newVal) {
    const value = getter ? getter.call(obj) : val
    if (getter && !setter) return
    if (setter) {
      setter.call(obj, newVal)
    } else {
      val = newVal
    }
    childOb = !shallow && observe(newVal)
    dep.notify() // 執(zhí)行dep notify方法
  }
})

執(zhí)行dep.notify方法

export default class Dep {
  constructor () {
    this.id = uid++
    this.subs = []
  }
  notify () {
    const subs = this.subs.slice()
    for (let i = 0, l = subs.length; i < l; i++) {
      // 實(shí)際上遍歷執(zhí)行了subs數(shù)組中元素的update方法
      subs[i].update()
    }
  }
}

當(dāng)數(shù)據(jù)被引用時,如

{{msg}}
,會執(zhí)行g(shù)et方法,并向subs數(shù)組中添加渲染Watcher,當(dāng)數(shù)據(jù)被改變時執(zhí)行Watcher的update方法執(zhí)行數(shù)據(jù)更新。

update () {
  /* istanbul ignore else */
  if (this.lazy) {
    this.dirty = true
  } else if (this.sync) {
    this.run()
  } else {
    queueWatcher(this) //執(zhí)行queueWatcher
  }
}

update 方法最終執(zhí)行queueWatcher

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 保證nextTick只執(zhí)行一次
      waiting = true
      // 最終queueWatcher 方法會把flushSchedulerQueue 傳入到nextTick中執(zhí)行
      nextTick(flushSchedulerQueue)
    }
  }
}

執(zhí)行flushSchedulerQueue方法

function flushSchedulerQueue () {
  currentFlushTimestamp = getNow()
  flushing = true
  let watcher, id
  ...
  for (index = 0; index < queue.length; index++) {
    watcher = queue[index]
    if (watcher.before) {
      watcher.before()
    }
    id = watcher.id
    has[id] = null
    // 遍歷執(zhí)行渲染watcher的run方法 完成視圖更新
    watcher.run()
  }
  // 重置waiting變量 
  resetSchedulerState()
  ...
}

也就是說當(dāng)數(shù)據(jù)變化最終會把flushSchedulerQueue傳入到nextTick中執(zhí)行flushSchedulerQueue函數(shù)會遍歷執(zhí)行watcher.run()方法,watcher.run()方法最終會完成視圖更新,接下來我們看關(guān)鍵的nextTick方法到底是啥

2.2 nextTick

nextTick方法會被傳進(jìn)來的回調(diào)push進(jìn)callbacks數(shù)組,然后執(zhí)行timerFunc方法

export function nextTick (cb?: Function, ctx?: Object) {
  let _resolve
  // push進(jìn)callbacks數(shù)組
  callbacks.push(() => {
     cb.call(ctx)
  })
  if (!pending) {
    pending = true
    // 執(zhí)行timerFunc方法
    timerFunc()
  }
}

timerFunc

let timerFunc
// 判斷是否原生支持Promise
if (typeof Promise !== "undefined" && isNative(Promise)) {
  const p = Promise.resolve()
  timerFunc = () => {
    // 如果原生支持Promise 用Promise執(zhí)行flushCallbacks
    p.then(flushCallbacks)
    if (isIOS) setTimeout(noop)
  }
  isUsingMicroTask = true
// 判斷是否原生支持MutationObserver
} else if (!isIE && typeof MutationObserver !== "undefined" && (
  isNative(MutationObserver) ||
  // PhantomJS and iOS 7.x
  MutationObserver.toString() === "[object MutationObserverConstructor]"
)) {
  let counter = 1
  // 如果原生支持MutationObserver 用MutationObserver執(zhí)行flushCallbacks
  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
// 判斷是否原生支持setImmediate 
} else if (typeof setImmediate !== "undefined" && isNative(setImmediate)) {
  timerFunc = () => {
  // 如果原生支持setImmediate  用setImmediate執(zhí)行flushCallbacks
    setImmediate(flushCallbacks)
  }
// 都不支持的情況下使用setTimeout 0
} else {
  timerFunc = () => {
    // 使用setTimeout執(zhí)行flushCallbacks
    setTimeout(flushCallbacks, 0)
  }
}

// flushCallbacks 最終執(zhí)行nextTick 方法傳進(jìn)來的回調(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會優(yōu)先使用microTask, 其次是macroTask 。

也就是說nextTick中的任務(wù),實(shí)際上會異步執(zhí)行,nextTick(callback)類似于
Promise.resolve().then(callback),或者setTimeout(callback, 0)。

也就是說vue的視圖更新 nextTick(flushSchedulerQueue)等同于setTimeout(flushSchedulerQueue, 0),會異步執(zhí)行flushSchedulerQueue函數(shù),所以我們在this.msg = hello 并不會立即更新dom。

要想在dom更新后讀取dom信息,我們需要在本次異步任務(wù)創(chuàng)建之后創(chuàng)建一個異步任務(wù)。

為了驗(yàn)證這個想法我們不用nextTick,直接用setTimeout實(shí)驗(yàn)一下。如下面代碼,驗(yàn)證了我們的想法。



文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/106696.html

相關(guān)文章

  • Vue.$nextTick()源碼解析

    摘要:源碼在下用法在下次更新循環(huán)結(jié)束之后執(zhí)行延遲回調(diào)。在修改數(shù)據(jù)之后立即使用這個方法,獲取更新后的。 源碼在src/core/util/next-tick.js下 用法 在下次 DOM 更新循環(huán)結(jié)束之后執(zhí)行延遲回調(diào)。在修改數(shù)據(jù)之后立即使用這個方法,獲取更新后的 DOM。 // 修改數(shù)據(jù) vm.msg = Hello // DOM 還沒有更新 Vue.nextTick(function () ...

    Lionad-Morotar 評論0 收藏0
  • vue 源碼解析 --虛擬Dom-render

    摘要:用于延遲執(zhí)行一段代碼,它接受個參數(shù)回調(diào)函數(shù)和執(zhí)行回調(diào)函數(shù)的上下文環(huán)境,如果沒有提供回調(diào)函數(shù),那么將返回對象。 instance/index.js function Vue (options) { if (process.env.NODE_ENV !== production && !(this instanceof Vue) ) { warn(Vue is a ...

    Tony 評論0 收藏0
  • Vue源碼探究一】當(dāng)我們引入Vue,我們引入了什么?

    摘要:源碼版本構(gòu)造器實(shí)例選項(xiàng)讓我們用一段展示一下這三個概念其中的構(gòu)造器實(shí)例實(shí)例名可以任意取,這里我們便于理解保持和文檔一致選項(xiàng)即為傳入構(gòu)造器里的配置選項(xiàng)。其實(shí)構(gòu)造器上也綁了不少好用的方法。 源碼版本:2.0.5 構(gòu)造器、實(shí)例、選項(xiàng) 讓我們用一段demo展示一下這三個概念: //HTML {{ message }} //JS var vm = new Vue({ el: #app,...

    mengbo 評論0 收藏0
  • 前方來報,八月最新資訊--關(guān)于vue2&3的最佳文章推薦

    摘要:哪吒別人的看法都是狗屁,你是誰只有你自己說了才算,這是爹教我的道理。哪吒去他個鳥命我命由我,不由天是魔是仙,我自己決定哪吒白白搭上一條人命,你傻不傻敖丙不傻誰和你做朋友太乙真人人是否能夠改變命運(yùn),我不曉得。我只曉得,不認(rèn)命是哪吒的命。 showImg(https://segmentfault.com/img/bVbwiGL?w=900&h=378); 出處 查看github最新的Vue...

    izhuhaodev 評論0 收藏0
  • Vue源碼Vue中DOM的異步更新策略以及nextTick機(jī)制

    摘要:本篇文章主要是對中的異步更新策略和機(jī)制的解析,需要讀者有一定的使用經(jīng)驗(yàn)并且熟悉掌握事件循環(huán)模型。這個結(jié)果足以說明中的更新并非同步。二是把回調(diào)函數(shù)放入一個隊(duì)列,等待適當(dāng)?shù)臅r機(jī)執(zhí)行。通過的主動來觸發(fā)的事件,進(jìn)而把回調(diào)函數(shù)作為參與事件循環(huán)。 本篇文章主要是對Vue中的DOM異步更新策略和nextTick機(jī)制的解析,需要讀者有一定的Vue使用經(jīng)驗(yàn)并且熟悉掌握J(rèn)avaScript事件循環(huán)模型。 ...

    selfimpr 評論0 收藏0

發(fā)表評論

0條評論

PrototypeZ

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<