Maison > interface Web > js tutoriel > Une brève analyse de la méthode d'implémentation de Vue.nextTick

Une brève analyse de la méthode d'implémentation de Vue.nextTick

小云云
Libérer: 2018-01-03 11:17:00
original
1476 Les gens l'ont consulté

Cet article présente principalement la méthode d'implémentation de Vue.nextTick. Il s'agit d'une analyse du code source de l'implémentation de l'API vue.nextTick après la boucle d'événements et MicroTask. L’éditeur le trouve plutôt bien, alors j’aimerais le partager avec vous maintenant et le donner comme référence pour tout le monde. Suivons l'éditeur pour y jeter un œil, j'espère que cela pourra aider tout le monde.

Préchauffer et écrire une fonction veille


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)
Copier après la connexion

Expliquez la fonction veille

Quand la fonction asynchrone exécute wait PromiseFn(), l'exécution de la fonction est suspendue. Nous savons également que PromiseFn est désormais exécutée au sein de la microTask. Lorsque la microTask n'a pas été exécutée, la macroTask suivante ne sera pas exécutée. Nous avons également implémenté une fonction de veille via la fonction de boucle d'événements de microTask, empêchant l'exécution de console.log

Process
.

1 Exécuter console.log('start')
2 Exécuter wait, l'exécution est en pause, attendre que le PromiseFn après la fonction wait soit exécuté dans microTask
3 Dans la fonction sleep, retarder ms pour revenir
4 Après être revenu pour résoudre, exécutez console.log('end')

API nextTick

Comment utiliser nextTick dans vue


vue.nextTick(() => {
 // todo...
})
Copier après la connexion

Après avoir compris l'utilisation, jetez un œil au code source


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 !== &#39;undefined&#39; && isNative(Promise)) {
  var p = Promise.resolve()
  var logError = err => { console.error(err) }
  timerFunc = () => {
   p.then(nextTickHandler).catch(logError) // 重点
  }
 } else if (&#39;!isIE MutationObserver&#39;) {
  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 !== &#39;undefined&#39;) {
   return new Promise((resolve, reject) => {
    _resolve =resolve
   })
  }
 }
})() // 自执行函数
Copier après la connexion

En regardant brièvement le code source, vous pouvez comprendre que l'API nextTick est une fonction auto-exécutable

Puisqu'il s'agit d'une fonction auto-exécutable, regardez directement son type de retour, retournez la fonction 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 !== &#39;undefined&#39;) {
   return new Promise((resolve, reject) => {
    _resolve =resolve
   })
  }
 }
Copier après la connexion

Concentrez-vous uniquement sur la fonction queueNextTick du processus principal et appuyez sur le () que nous avons passé => ... } dans les rappels


 if (typeof Promise !== &#39;undefined&#39; && isNative(Promise)) {
  var p = Promise.resolve()
  var logError = err => { console.error(err) }
  timerFunc = () => {
   p.then(nextTickHandler).catch(logError) // 重点
  }
 } else if (&#39;!isIE MutationObserver&#39;) {
  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) // 重点
  }
 }
Copier après la connexion

Dans ce paragraphe on peut voir les trois points marqués indiquant que Promise, MutationObserver ou setTimeout(fn, 0) sont utilisés pour exécuter nextTickHandler dans différents environnements de navigateur


function nextTickHandler () {
  pending = false
  const copies = callbacks.slice(0) // 复制
  callbacks.length = 0 // 清空
  for (let i = 0; i < copies.length; i++) {
   copies[i]() // 逐个执行
  }
 }
Copier après la connexion

nextTickHandler consiste à exécuter le () que nous avons mis dans les rappels avant => { // todo... } dans les tâches en cours.

Écrivez un simple nextTick

Le code source peut être compliqué, nous écrivons donc nous-mêmes un simple 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, &#39;nextTick&#39;)
})
Copier après la connexion

Nous pouvons voir d'ici que le principe de nextTick est de renvoyer une promesse, et notre code todo est exécuté dans cette promesse. Nous pouvons maintenant continuer à simplifier


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

simpleNextTick(() => {
 setTimeout(console.log, 3000, &#39;nextTick&#39;)
})
Copier après la connexion
Écrivez-le directement comme ceci.


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

simpleNextTick(() => {
 setTimeout(console.log, 3000, &#39;nextTick&#39;)
})
Copier après la connexion
Cette fois, nous simplifions également la fonction auto-exécutable


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

simpleNextTick(() => {
 setTimeout(console.log, 3000, &#39;nextTick&#39;)
})
Copier après la connexion
Maintenant, nous la simplifions En fin de compte, j'ai découvert que le contenu principal de nextTick est Promise, une microtâche.

Nous revenons maintenant à l'exemple officiel de l'API nextTick de vue


<p id="example">{{message}}</p>
var vm = new Vue({
 el: &#39;#example&#39;,
 data: {
  message: &#39;123&#39;
 }
})
vm.message = &#39;new message&#39; // 更改数据
vm.$el.textContent === &#39;new message&#39; // false
Vue.nextTick(function () {
 vm.$el.textContent === &#39;new message&#39; // true
})
Copier après la connexion
Il s'avère qu'une fois les données dans vue mises à jour, la mise à jour dom doit être effectué dans la prochaine boucle d'événements exécutée plus tard.

Le principe d'utilisation de nextTick est principalement de résoudre le scénario d'exploitation du DOM immédiatement après la mise à jour des données en un seul événement.

Maintenant que nous savons que le cœur de nextTick utilise des microTasks, comparons le nextTick simplifié avec la fonction sleep du début.


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

simpleNextTick(() => {
 setTimeout(console.log, 3000, &#39;nextTick&#39;) // 也可以换成ajax请求
})
Copier après la connexion


function sleep (ms) {
 return new Promise(resolve => setTimeout(resolve, ms) // 也可以换成ajax请求
}
async function oneTick (ms) {
 console.log(&#39;start&#39;)
 await sleep(ms)
 console.log(&#39;end&#39;)
}
oneTick(3000)
Copier après la connexion
Nous voyons que les résultats d'exécution de nextTick et oneTick que nous avons écrits sont tellement similaires. La seule différence est que nextTick enveloppe le rappel avec une promesse, le renvoie et l'exécute, tandis que oneTick utilise wait pour exécuter une fonction Promise, et cette promesse a sa propre fonction webapi encapsulée.

Lors d'une requête ajax, pouvons-nous utiliser directement axios pour renvoyer la bibliothèque Promise


async function getData () {
  const data = await axios.get(url)
  // 操作data的数据来改变dom
  return data
}
Copier après la connexion
Cela peut également obtenir le même résultat que nextTick ? Le rôle de

Enfin, nous pouvons également voir dans le code source que lorsque l'environnement du navigateur ne prend pas en charge Promise, vous pouvez utiliser MutationObserver ou setTimeout(cb, 0) pour obtenir le même effet. Mais le noyau final est microTask

Recommandations associées :


Exemples d'utilisation de process.nextTick dans Node.js

node Analyse de la différence entre timer nextTick() et setImmediate() dans .js_node.js

Exemple d'utilisation de process.nextTick dans Node.js_node.js

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal