Maison >interface Web >js tutoriel >Explication détaillée de la méthode d'implémentation de Vue.nextTick
Cet article présente principalement la méthode d'implémentation de Vue.nextTick. L'éditeur pense qu'elle est plutôt bonne, je vais donc la partager avec vous maintenant et la donner comme référence. Suivons l'éditeur et jetons un œil. J'espère que cela pourra aider tout le monde.
Il s'agit d'une analyse du code source de l'implémentation de l'API vue.nextTick suite à la boucle d'événements et à MicroTask.
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)
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 suspendue, en attendant que le PromiseFn après que la fonction wait soit exécutée 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... })
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 !== '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 }) } } })() // 自执行函数
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 !== 'undefined') { return new Promise((resolve, reject) => { _resolve =resolve }) } }
Concentrez-vous uniquement sur la fonction queueNextTick du processus principal et appuyez sur le () que nous avons passé => ... } dans les rappels
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) // 重点 } }
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]() // 逐个执行 } }
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, 'nextTick') })
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, 'nextTick') })Écrivez-le directement comme ceci.
const simpleNextTick = function queueNextTick (cb) { timerFunc = () => { return Promise.resolve().then(() => { cb() }) } timerFunc() } simpleNextTick(() => { setTimeout(console.log, 3000, 'nextTick') })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, 'nextTick') })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: '#example', data: { message: '123' } }) vm.message = 'new message' // 更改数据 vm.$el.textContent === 'new message' // false Vue.nextTick(function () { vm.$el.textContent === 'new message' // true })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.
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)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 }Cela peut également obtenir le même résultat que nextTick ? Le rôle deEnfin, 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 microTaskRecommandations associées :
Explication détaillée de Vue + Vuex à l'aide de l'instance vm.$nextTick
Explication détaillée du code source de la fonction nextTick dans Vue
Exemples d'utilisation de process.nextTick dans 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!