微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

浅谈Vue.nextTick 的实现方法

这是一篇继event loop和MicroTask 后的vue.nextTick API实现的源码解析。

预热,写一个sleep函数

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的使用方法

{ // todo... })

了解用法后看一下源码

rush:js;"> 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) {...}

{ _resolve =resolve }) } }

只关注主流程queueNextTick函数把我们传入的() => { // todo... } 推入了callbacks内

{ 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,0) // 重点
}
}

这一段我们可以看到标注的三个点表明在不同浏览器环境下使用Promise,MutationObserver或setTimeout(fn,0) 来执行nextTickHandler

rush:js;"> 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

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中执行,现在我们还可以继续简化

{ return Promise.resolve().then(() => { cb() }) } timerFunc() } })()

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

直接写成这样。

{ return Promise.resolve().then(() => { cb() }) } timerFunc() }

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

这次我们把自执行函数也简化掉

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

现在我们直接简化到最后,现在发现nextTick最核心的内容就是Promise,一个microtask。

现在我们回到vue的nextTick API官方示例

rush:js;">
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更新是要在下一个事件循环后执行的。 nextTick的使用原则主要就是解决单一事件更新数据后立即操作dom的场景。

既然我们知道了nextTick核心是利用microTasks,那么我们把简化过的nextTick和开头的sleep函数对照一下。

simpleNextTick(() => {
setTimeout(console.log,'nextTick') // 也可以换成ajax请求
})

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的库

rush:js;"> async function getData () { const data = await axios.get(url) // 操作data的数据来改变dom return data }

这样也可以达到同nextTick同样的作用

最后我们也可以从源码中看出,当浏览器环境不支持Promise时可以使用MutationObserver或setTimeout(cb,0) 来达到同样的效果。但最终的核心是microTask

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程之家。

原文地址:https://www.jb51.cc/vue/35604.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐