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

Vue中之nextTick函數(shù)源碼分析詳解

 更新時間:2017年10月17日 08:24:00   作者:涂根華  
這篇文章主要介紹了Vue中之nextTick函數(shù)源碼分析,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

1. 什么是Vue.nextTick()?

官方文檔解釋如下:

在下次DOM更新循環(huán)結(jié)束之后執(zhí)行的延遲回調(diào)。在修改數(shù)據(jù)之后立即使用這個方法,獲取更新后的DOM。

2. 為什么要使用nextTick?

<!DOCTYPE html>
<html>
 <head>
 <title>演示Vue</title>
 <script src="https://tugenhua0707.github.io/vue/vue1/vue.js"></script>
 </head>
 <body>
 <div id="app">
  <template>
  <div ref="list">
   {{name}}
  </div>
  </template>
 </div>
 <script>
  new Vue({
  el: '#app',
  data: {
   name: 'aa'
  },
  mounted() {
   this.updateData();
  },
  methods: {
   updateData() {
   var self = this;
   this.name = 'bb';
   console.log(this.$el.textContent); // aa
   this.$nextTick(function(){
    console.log(self.$el.textContent); // bb
   });
   }
  }
  });
 </script>
 </body>
</html>

如上代碼 在頁面視圖上顯示bb,但是當(dāng)我在控制臺打印的時候,獲取的文本內(nèi)容還是 aa,但是使用 nextTick后,獲取的文本內(nèi)容就是最新的內(nèi)容bb了,因此在這種情況下,我們可以使用nextTick函數(shù)了。

上面的代碼為什么改變this.name = 'bb';后,再使用console.log(this.$el.textContent);打印的值還是aa呢?那是因為設(shè)置name的值后,DOM還沒有更新到,所以獲取值還是之前的值,但是我們放到nextTick函數(shù)里面的時候,代碼會在DOM更新后執(zhí)行,因此DOM更新后,再去獲取元素的值就可以獲取到最新值了。

理解DOM更新:在VUE中,當(dāng)我們修改了data中的某一個值后,并不會立即反應(yīng)到該el中,vue將對更改的數(shù)據(jù)放到watcher的一個異步隊列中,只有在當(dāng)前任務(wù)空閑時才會執(zhí)行watcher隊列任務(wù),這就有一個延遲時間,因此放到 nextTick函數(shù)后就可以獲取該el的最新值了。如果我們把上面的nextTick改成setTimeout也是可以的。

3. Vue源碼詳解之nextTick(源碼在 vue/src/core/util/env.js)

在理解nextTick源碼之前,我們先來理解下 html5中新增的 MutationObserver的API,它的作用是用來監(jiān)聽DOM變動的接口,它能監(jiān)聽一個dom對象發(fā)生的子節(jié)點刪除,屬性修改,文本內(nèi)容修改等等。

nextTick源碼如下:

export const nextTick = (function () {
 const callbacks = []
 let pending = false
 let timerFunc

 function nextTickHandler () {
 pending = false;
 /*
  之所以要slice復(fù)制一份出來是因為有的cb執(zhí)行過程中又會往callbacks中加入內(nèi)容,比如$nextTick的回調(diào)函數(shù)里又有$nextTick,
  那么這些應(yīng)該放入到下一個輪次的nextTick去執(zhí)行,所以拷貝一份,遍歷完成即可,防止一直循環(huán)下去。
  */
 const copies = callbacks.slice(0)
 callbacks.length = 0
 for (let i = 0; i < copies.length; i++) {
  copies[i]()
 }
 }

 // the nextTick behavior leverages the microtask queue, which can be accessed
 // via either native Promise.then or MutationObserver.
 // MutationObserver has wider support, however it is seriously bugged in
 // UIWebView in iOS >= 9.3.3 when triggered in touch event handlers. It
 // completely stops working after triggering a few times... so, if native
 // Promise is available, we will use it:
 /* istanbul ignore if */
 /*
 nextTick行為利用了microtask隊列, 先使用 Promise.resolve().then(nextTickHandler)來將異步回調(diào)
 放入到microtask中,Promise 和 MutationObserver都可以使用,但是 MutationObserver 在IOS9.3以上的
 WebView中有bug,因此如果滿足第一項的話就可以執(zhí)行,如果沒有原生Promise就用 MutationObserver。
 */
 if (typeof Promise !== 'undefined' && isNative(Promise)) {
 var p = Promise.resolve()
 var logError = err => { console.error(err) }
 timerFunc = () => {
  p.then(nextTickHandler).catch(logError)
  // in problematic UIWebViews, Promise.then doesn't completely break, but
  // it can get stuck in a weird state where callbacks are pushed into the
  // microtask queue but the queue isn't being flushed, until the browser
  // needs to do some other work, e.g. handle a timer. Therefore we can
  // "force" the microtask queue to be flushed by adding an empty timer.
  if (isIOS) setTimeout(noop)
 }
 } else if (typeof MutationObserver !== 'undefined' && (
 isNative(MutationObserver) ||
 // PhantomJS and iOS 7.x
 MutationObserver.toString() === '[object MutationObserverConstructor]'
 )) {
 // use MutationObserver where native Promise is not available,
 // e.g. PhantomJS IE11, iOS7, Android 4.4
 /*
  創(chuàng)建一個MutationObserver,observe監(jiān)聽到DOM改動之后執(zhí)行的回調(diào) nextTickHandler 
  */
 var counter = 1
 var observer = new MutationObserver(nextTickHandler)
 var textNode = document.createTextNode(String(counter));
 // 使用MutationObserver的接口,監(jiān)聽文本節(jié)點的字符內(nèi)容
 observer.observe(textNode, {
  characterData: true
 });
 /*
  每次執(zhí)行timerFunc函數(shù)都會讓文本節(jié)點的內(nèi)容在0/1之間切換,切換之后將新賦值到那個我們MutationObserver監(jiān)聽的文本節(jié)點上去。
  */
 timerFunc = () => {
  counter = (counter + 1) % 2
  textNode.data = String(counter)
 }
 } else {
 // fallback to setTimeout
 /*
  如果上面的兩種都不支持的話,我們就使用setTimeout來執(zhí)行
  */
 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)
  }
 });
 /* 如果pending為true,表明本輪事件循環(huán)中已經(jīng)執(zhí)行過 timerFunc(nextTickHandler, 0) */
 if (!pending) {
  pending = true
  timerFunc()
 }
 if (!cb && typeof Promise !== 'undefined') {
  return new Promise((resolve, reject) => {
  _resolve = resolve
  })
 }
 }
})()

整體思路理解:首先 nextTick 是一個閉包函數(shù),代碼立即執(zhí)行,在理解整體代碼之前,我們先來看個類似的demo,如下代碼:

<!DOCTYPE html>
<html>
 <head>
 <title>演示Vue</title>
 </head>
 <body>
 <div id="app">
  
 </div>
 <script>
  var nextTick = (function(){
  return function queueNextTick(cb, ctx) {
   if (cb) {
   try {
    cb.call(ctx)
   } catch (e) {
    console.log('出錯了');
   }
   }
  }
  })();

  // 方法調(diào)用
  nextTick(function(){
  console.log(2); // 打印2
  })
 </script>
 </body>
</html>

demo代碼和上面的代碼很類似。

我們也可以再來抽離使用nextTick做demo代碼如下:

var nextTick2 = (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]()
 }
 }
 if (typeof Promise !== 'undefined') {
 var p = Promise.resolve()
 var logError = err => { console.error(err) }
 timerFunc = () => {
  p.then(nextTickHandler).catch(logError)
 }
 } else if (typeof MutationObserver !== 'undefined' ||
 // PhantomJS and iOS 7.x
 MutationObserver.toString() === '[object MutationObserverConstructor]'
 ) {
 // use MutationObserver where native Promise is not available,
 // e.g. PhantomJS IE11, iOS7, Android 4.4
 var counter = 1
 var observer = new MutationObserver(nextTickHandler)
 var textNode = document.createTextNode(String(counter))
 observer.observe(textNode, {
  characterData: true
 })
 timerFunc = () => {
  counter = (counter + 1) % 2
  textNode.data = String(counter)
 }
 } else {
 // fallback to setTimeout
 /* istanbul ignore next */
 timerFunc = () => {
  setTimeout(nextTickHandler, 0)
 }
 }
 return function queueNextTick (cb, ctx) {
 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()
 }
 if (!cb && typeof Promise !== 'undefined') {
  return new Promise((resolve, reject) => {
  _resolve = resolve
  })
 }
 }
})();
nextTick2(function(){
 console.log(2222);
});

如上代碼是nextTick源碼的抽離,為了更好的理解nextTick,做了如上的demo。

我們再來理解一下整體的代碼的含義;

先定義數(shù)組 callbacks = [];來存放所有需要執(zhí)行的回調(diào)函數(shù),定義let pending = false;判斷本輪事件是否執(zhí)行過 timerFunc(nextTickHandler, 0)這個函數(shù),為true說明執(zhí)行過 timeFunc函數(shù),接著定義nextTickHandler函數(shù),該函數(shù)的作用是依次遍歷數(shù)組callbacks保存的函數(shù),依次執(zhí)行;

請看源代碼如下:

function nextTickHandler () {
 pending = false
 const copies = callbacks.slice(0)
 callbacks.length = 0
 for (let i = 0; i < copies.length; i++) {
 copies[i]()
 }
}

然后就是三個判斷了,代碼如下:

if (typeof Promise !== 'undefined' && isNative(Promise)) {
 var p = Promise.resolve();
 var logError = err => { console.error(err) }
 timerFunc = () => {
 p.then(nextTickHandler).catch(logError);
} else if (typeof MutationObserver !== 'undefined' && (
 isNative(MutationObserver) ||
 // PhantomJS and iOS 7.x
 MutationObserver.toString() === '[object MutationObserverConstructor]'
)){
 var counter = 1
 var observer = new MutationObserver(nextTickHandler)
 var textNode = document.createTextNode(String(counter))
 observer.observe(textNode, {
 characterData: true
 })
 timerFunc = () => {
 counter = (counter + 1) % 2
 textNode.data = String(counter)
 }
} else {
 timerFunc = () => {
 setTimeout(nextTickHandler, 0)
 }
}

首先判斷是否支持Promise對象,如果支持的話,定義了timeFunc()函數(shù),為了下一步調(diào)用做準(zhǔn)備,然后繼續(xù)判斷是否支持該對象 MutationObserver,如果支持的話,創(chuàng)建一個文本節(jié)點,監(jiān)聽該節(jié)點數(shù)據(jù)是否發(fā)生改變,如果發(fā)生改變的話,調(diào)用timerFunc函數(shù),counter值會在0/1切換,如果值改變了的話,把該數(shù)據(jù)值賦值到data屬性上面去,那么data屬性發(fā)生改變了,就會重新渲染頁面(因為vue是通過Object.defineProperty來監(jiān)聽屬性值是否發(fā)生改變),如果上面兩種情況都不滿足的話,那么直接使用setTimeout來執(zhí)行nextTickHandler函數(shù)了;

最后nextTick代碼返回一個函數(shù),代碼如下:

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()
 }
 if (!cb && typeof Promise !== 'undefined') {
 return new Promise((resolve, reject) => {
  _resolve = resolve
 })
 }
}

代碼的含義是:傳入的cb是否是函數(shù),ctx參數(shù)是否是一個對象,如果cb是一個函數(shù)的話,使用cb.call(ctx), 如果timerFunc沒有執(zhí)行過的話,那么pending為false,因此執(zhí)行 timerFunc()函數(shù)。基本的思路就是這樣的。

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Vue項目中使用mock.js的完整步驟

    Vue項目中使用mock.js的完整步驟

    這篇文章主要給大家介紹了關(guān)于在Vue項目中使用mock.js的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-01-01
  • 解決vue3使用element-ui的具體操作

    解決vue3使用element-ui的具體操作

    elmentui是一款為vue設(shè)計的桌面組件庫,下面這篇文章主要給大家介紹了關(guān)于解決vue3使用element-ui的具體操作,文中通過實例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-08-08
  • el-table?選中行與復(fù)選框相互聯(lián)動的實現(xiàn)步驟

    el-table?選中行與復(fù)選框相互聯(lián)動的實現(xiàn)步驟

    這篇文章主要介紹了el-table?選中行與復(fù)選框相互聯(lián)動,分為兩步,第一步點擊行時觸發(fā)復(fù)選框的選擇或取消,第二步點擊復(fù)選框時觸發(fā)相應(yīng)行的變化,本文通過實例代碼給大家詳細(xì)講解,需要的朋友可以參考下
    2022-10-10
  • 如何使用vite搭建vue3項目詳解

    如何使用vite搭建vue3項目詳解

    Vite 是一個面向現(xiàn)代瀏覽器的更輕,更快的web應(yīng)用開發(fā)工具,下面這篇文章主要給大家介紹了關(guān)于如何使用vite搭建vue3項目的相關(guān)資料,文中通過實例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-07-07
  • 詳解django模板與vue.js沖突問題

    詳解django模板與vue.js沖突問題

    在本文里小編給各位整理了關(guān)于django模板與vue.js沖突問題以及實例代碼,需要的朋友們參考下。
    2019-07-07
  • vue中axios的封裝問題(簡易版攔截,get,post)

    vue中axios的封裝問題(簡易版攔截,get,post)

    這篇文章主要介紹了vue中axios的封裝問題(簡易版攔截,get,post),需要的朋友可以參考下
    2018-06-06
  • vuex中g(shù)etters的基本用法解讀

    vuex中g(shù)etters的基本用法解讀

    這篇文章主要介紹了vuex中g(shù)etters的基本用法,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-09-09
  • vant如何修改placeholder樣式

    vant如何修改placeholder樣式

    這篇文章主要介紹了vant如何修改placeholder樣式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-05-05
  • 解決v-if 與 v-for 同時使用報錯的問題

    解決v-if 與 v-for 同時使用報錯的問題

    在進(jìn)行項目開發(fā)的時候因為在一個標(biāo)簽上同時使用了v-for和v-if兩個指令導(dǎo)致的報錯,遇到這個問題如何解決呢?下面小編給大家?guī)砹岁P(guān)于v-if 與 v-for 使用報錯問題分析及解決方法,一起看看吧
    2022-03-03
  • vue中實現(xiàn)高德定位功能

    vue中實現(xiàn)高德定位功能

    這篇文章主要介紹了vue中實現(xiàn)高德定位功能,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下
    2019-12-12

最新評論

临西县| 蓝田县| 松阳县| 新疆| 桂平市| 罗甸县| 浪卡子县| 柳河县| 定陶县| 大埔县| 阿拉善左旗| 敖汉旗| 阿合奇县| 齐齐哈尔市| 南木林县| 马鞍山市| 金山区| 太白县| 兴化市| 潢川县| 绩溪县| 宝兴县| 栾川县| 兰坪| 赞皇县| 莒南县| 星座| 开封县| 荆门市| 隆尧县| 长海县| 利津县| 北流市| 余江县| 池州市| 钦州市| 威海市| 大城县| 镇安县| 正安县| 双流县|