亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

淺談Vue.nextTick 的實現方法

 更新時間:2017年10月25日 09:35:55   作者:jirengu_楊然  
本篇文章主要介紹了Vue.nextTick 的實現方法,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

這是一篇繼event loop和MicroTask 后的vue.nextTick API實現的源碼解析。

預熱,寫一個sleep函數

function sleep (ms) {
 return new Promise(resolve => setTimeout(resolve, ms)
}
async function oneTick (ms) {
 console.log('start')
 await sleep(ms)
 console.log('end')
}
oneTick(3000)

解釋下sleep函數

async 函數進行await PromiseFn()時函數執(zhí)行是暫停的,我們也知道現在這個PromiseFn是在microTask內執(zhí)行。當microTask沒執(zhí)行完畢時,后面的macroTask是不會執(zhí)行的,我們也就通過microTask在event loop的特性實現了一個sleep函數,阻止了console.log的執(zhí)行

流程

1執(zhí)行console.log('start')
2執(zhí)行await 執(zhí)行暫停,等待await函數后的PromiseFn在microTask執(zhí)行完畢
3在sleep函數內,延遲ms返回
4返回resolve后執(zhí)行console.log('end')

nextTick API

vue中nextTick的使用方法

vue.nextTick(() => {
 // todo...
})

了解用法后看一下源碼

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]() // 逐個執(zhí)行
  }
 }

 if (typeof Promise !== 'undefined' && isNative(Promise)) {
  var p = Promise.resolve()
  var logError = err => { console.error(err) }
  timerFunc = () => {
   p.then(nextTickHandler).catch(logError) // 重點
  }
 } else if ('!isIE MutationObserver') {
  var counter = 1
  var observer = new MutationObserver(nextTickHandler) // 重點
  var textNode = document.createTextNode(string(conter))

  observer.observe(textNode, {
   characterData: true
  })
  timerFunc = () => {
   counter = (counter + 1) % 2
   textNode.data = String(counter)
  }
 } else {
  timerFunc = () => {
   setTimeout(nextTickHandler, 0) // 重點
  }
 }


 return function queueNextTick (cb, ctx) { // api的使用方式
  let _resolve
  callbacks.push(() => {
   if (cb) {
    try {
     cb.call(ctx)
    } catch (e) {
     err
    }
   } else if (_resolve) {
    _resolve(ctx)
   }
  })
  if (!pending) {
   pending = true
   timerFunc()
  }
  if (!cb && typeof Promise !== 'undefined') {
   return new Promise((resolve, reject) => {
    _resolve =resolve
   })
  }
 }
})() // 自執(zhí)行函數

大致看一下源碼可以了解到nextTick api是一個自執(zhí)行函數

既然是自執(zhí)行函數,直接看它的return類型,return function queueNextTick (cb, ctx) {...}

return function queueNextTick (cb, ctx) { // api的使用方式
  let _resolve
  callbacks.push(() => {
   if (cb) {
    try {
     cb.call(ctx)
    } catch (e) {
     err
    }
   } else if (_resolve) {
    _resolve(ctx)
   }
  })
  if (!pending) {
   pending = true
   timerFunc()
  }
  if (!cb && typeof Promise !== 'undefined') {
   return new Promise((resolve, reject) => {
    _resolve =resolve
   })
  }
 }

只關注主流程queueNextTick函數把我們傳入的() => { // todo... } 推入了callbacks內

 if (typeof Promise !== 'undefined' && isNative(Promise)) {
  var p = Promise.resolve()
  var logError = err => { console.error(err) }
  timerFunc = () => {
   p.then(nextTickHandler).catch(logError) // 重點
  }
 } else if ('!isIE MutationObserver') {
  var counter = 1
  var observer = new MutationObserver(nextTickHandler) // 重點
  var textNode = document.createTextNode(string(conter))

  observer.observe(textNode, {
   characterData: true
  })
  timerFunc = () => {
   counter = (counter + 1) % 2
   textNode.data = String(counter)
  }
 } else {
  timerFunc = () => {
   setTimeout(nextTickHandler, 0) // 重點
  }
 }

這一段我們可以看到標注的三個點表明在不同瀏覽器環(huán)境下使用Promise, MutationObserver或setTimeout(fn, 0) 來執(zhí)行nextTickHandler

function nextTickHandler () {
  pending = false
  const copies = callbacks.slice(0) // 復制
  callbacks.length = 0 // 清空
  for (let i = 0; i < copies.length; i++) {
   copies[i]() // 逐個執(zhí)行
  }
 }

nextTickHandler就是把我們之前放入callbacks的 () => { // todo... } 在當前tasks內執(zhí)行。

寫一個簡單的nextTick

源碼可能比較繞,我們自己寫一段簡單的nextTick

const simpleNextTick = (function () {
 let callbacks = []
 let timerFunc

 return function queueNextTick (cb) {
  callbacks.push(() => { // 給callbacks 推入cb()
   cb()
  })

  timerFunc = () => {
   return Promise.resolve().then(() => {
    const fn = callbacks.shift()
    fn()
   })
  }
  timerFunc() // 執(zhí)行timerFunc,返回到是一個Promise
 }
})()

simpleNextTick(() => {
 setTimeout(console.log, 3000, 'nextTick')
})

我們可以從這里看出nextTick的原理就是返回出一個Promise,而我們todo的代碼在這個Promise中執(zhí)行,現在我們還可以繼續(xù)簡化

const simpleNextTick = (function () {
 return function queueNextTick (cb) {
  timerFunc = () => {
   return Promise.resolve().then(() => {
    cb()
   })
  }
  timerFunc()
 }
})()

simpleNextTick(() => {
 setTimeout(console.log, 3000, 'nextTick')
})

直接寫成這樣。

const simpleNextTick = function queueNextTick (cb) {
  timerFunc = () => {
   return Promise.resolve().then(() => {
    cb()
   })
  }
  timerFunc()
 }

simpleNextTick(() => {
 setTimeout(console.log, 3000, 'nextTick')
})

這次我們把自執(zhí)行函數也簡化掉

const simpleNextTick = function queueNextTick (cb) {
   return Promise.resolve().then(cb)
 }

simpleNextTick(() => {
 setTimeout(console.log, 3000, 'nextTick')
})

現在我們直接簡化到最后,現在發(fā)現nextTick最核心的內容就是Promise,一個microtask。

現在我們回到vue的nextTick API官方示例

<div id="example">{{message}}</div>
var vm = new Vue({
 el: '#example',
 data: {
  message: '123'
 }
})
vm.message = 'new message' // 更改數據
vm.$el.textContent === 'new message' // false
Vue.nextTick(function () {
 vm.$el.textContent === 'new message' // true
})

原來在vue內數據的更新后dom更新是要在下一個事件循環(huán)后執(zhí)行的。
nextTick的使用原則主要就是解決單一事件更新數據后立即操作dom的場景。

既然我們知道了nextTick核心是利用microTasks,那么我們把簡化過的nextTick和開頭的sleep函數對照一下。

const simpleNextTick = function queueNextTick (cb) {
   return Promise.resolve().then(cb)
 }

simpleNextTick(() => {
 setTimeout(console.log, 3000, 'nextTick') // 也可以換成ajax請求
})
function sleep (ms) {
 return new Promise(resolve => setTimeout(resolve, ms) // 也可以換成ajax請求
}
async function oneTick (ms) {
 console.log('start')
 await sleep(ms)
 console.log('end')
}
oneTick(3000)

我們看出nextTick和我么寫的oneTick的執(zhí)行結果是那么的相似。區(qū)別只在于nextTick是把callback包裹一個Promise返回并執(zhí)行,而oneTick是用await執(zhí)行一個Promise函數,而這個Promise有自己包裹的webapi函數。

那在用ajax請求的時候我們是不是直接這樣使用axios可以返回Promise的庫

async function getData () {
  const data = await axios.get(url)
  // 操作data的數據來改變dom
  return data
}

這樣也可以達到同nextTick同樣的作用

最后我們也可以從源碼中看出,當瀏覽器環(huán)境不支持Promise時可以使用MutationObserver或setTimeout(cb, 0) 來達到同樣的效果。但最終的核心是microTask

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。

相關文章

  • vue中watch監(jiān)聽不到變化的解決

    vue中watch監(jiān)聽不到變化的解決

    本文主要介紹了vue中watch監(jiān)聽不到變化的解決,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-01-01
  • 公共Hooks封裝useTableData表格數據實例

    公共Hooks封裝useTableData表格數據實例

    這篇文章主要為大家介紹了公共Hooks封裝useTableData表格數據實例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-12-12
  • vue實現文件上傳和下載

    vue實現文件上傳和下載

    這篇文章主要為大家詳細介紹了vue實現文件上傳和下載,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • Vue.extend和VueComponent的關系源碼解析

    Vue.extend和VueComponent的關系源碼解析

    這篇文章主要為大家詳解了Vue.extend和VueComponent的關系源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-02-02
  • 詳解Vue3中setup函數的使用教程

    詳解Vue3中setup函數的使用教程

    在vue3版本中,引入了一個新的函數,叫做setup,引入他的原因總結一下主要原因是:為了使用組合式 API,setup函數是Composition 的入口。本文將詳細介紹一下Vue3中setup函數的使用教程,感興趣的可以了解一下
    2022-07-07
  • Vue中組件的傳值方式詳解

    Vue中組件的傳值方式詳解

    這篇文章主要介紹了Vue中組件的傳值方式詳解,Vue中最常見的組件之間的通信方式有12種,今天我們會詳細講解父傳子props方式和子傳父emit以及非父子組件傳值,需要的朋友可以參考下
    2023-08-08
  • vue實現分頁功能

    vue實現分頁功能

    這篇文章主要為大家詳細介紹了vue實現分頁功能,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • 基于vue封裝一個安全鍵盤組件

    基于vue封裝一個安全鍵盤組件

    大部分中文應用彈出的默認鍵盤是簡體中文輸入法鍵盤,在輸入用戶名和密碼的時候,如果使用簡體中文輸入法鍵盤,用戶的輸入記錄會被緩存下來所以我們需要一個安全鍵盤,本文給大家介紹了如何基于vue封裝一個安全鍵盤組件,需要的朋友可以參考下
    2023-12-12
  • Vue3實現批量異步更新

    Vue3實現批量異步更新

    這篇文章主要為大家詳細介紹了Vue3批量異步更新是如何實現的,文中的示例代碼簡潔易懂,具有一定的借鑒價值,感興趣的小伙伴可以跟隨小編一起學習一下
    2023-08-08
  • 純JS如何實現vue.js下的雙向綁定功能

    純JS如何實現vue.js下的雙向綁定功能

    對于vue下的雙向綁定功能,個人理解為在處理邏輯的過程中緩存了大量的node對象,node對象可以是html標簽、文本內容。既然選擇了緩存這些對象,那么在用的過程中哪里需要改變就把node拿出來,進行標簽屬性的變更或者文本內容的修改。本文主要講了如何實現雙向綁定
    2021-06-06

最新評論