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

首頁 > 編程 > JavaScript > 正文

淺談Vue.nextTick 的實現方法

2019-11-19 15:05:20
字體:
來源:轉載
供稿:網友

這是一篇繼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()時函數執行是暫停的,我們也知道現在這個PromiseFn是在microTask內執行。當microTask沒執行完畢時,后面的macroTask是不會執行的,我們也就通過microTask在event loop的特性實現了一個sleep函數,阻止了console.log的執行

流程

1執行console.log('start')
2執行await 執行暫停,等待await函數后的PromiseFn在microTask執行完畢
3在sleep函數內,延遲ms返回
4返回resolve后執行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]() // 逐個執行  } } 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   })  } }})() // 自執行函數

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

既然是自執行函數,直接看它的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) // 重點  } }

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

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

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

寫一個簡單的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() // 執行timerFunc,返回到是一個Promise }})()simpleNextTick(() => { setTimeout(console.log, 3000, 'nextTick')})

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

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')})

這次我們把自執行函數也簡化掉

const simpleNextTick = function queueNextTick (cb) {   return Promise.resolve().then(cb) }simpleNextTick(() => { setTimeout(console.log, 3000, 'nextTick')})

現在我們直接簡化到最后,現在發現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' // falseVue.nextTick(function () { vm.$el.textContent === 'new message' // true})

原來在vue內數據的更新后dom更新是要在下一個事件循環后執行的。
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的執行結果是那么的相似。區別只在于nextTick是把callback包裹一個Promise返回并執行,而oneTick是用await執行一個Promise函數,而這個Promise有自己包裹的webapi函數。

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

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

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

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

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

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 天津市| 河北省| 北碚区| 麻栗坡县| 嵊州市| 大余县| 鹿泉市| 化州市| 辛集市| 柏乡县| 鄂托克前旗| 吉木萨尔县| 桂平市| 兴安县| 新安县| 明光市| 彝良县| 大冶市| 易门县| 凌海市| 许昌县| 阿拉善左旗| 丁青县| 岳阳市| 赤峰市| 鄄城县| 丹棱县| 霍城县| 临漳县| 德化县| 海门市| 安吉县| 柏乡县| 安仁县| 汝阳县| 沁源县| 周宁县| 新化县| 重庆市| 洛宁县| 卓资县|