这篇文章主要介绍“Vue中的Vue.nextTick的异步怎么实现”,在日常操作中,相信很多人在Vue中的Vue.nextTick的异步怎么实现问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Vue中的Vue.nextTick的异步怎么实现”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!
创新互联专注为客户提供全方位的互联网综合服务,包含不限于成都网站设计、做网站、范县网络推广、小程序开发、范县网络营销、范县企业策划、范县品牌公关、搜索引擎seo、人物专访、企业宣传片、企业代运营等,从售前售中售后,我们都将竭诚为您服务,您的肯定,是我们最大的嘉奖;创新互联为所有大学生创业者提供范县建站搭建服务,24小时服务热线:18980820575,官方网址:www.cdcxhl.com

你真的了解nextTick吗?来,直接上题~
{{ name }}
请问上述代码中,当点击按钮“修改name”时,'nextTick1','sync log','nextTick2'对应的this.$refs.name.innerText分别会输出什么?注意,这里打印的是DOM的innerText~(文章结尾处会贴出答案)
如果此时的你有非常坚定的答案,那你可以不用继续往下看了~但如果你对自己的答案有所顾虑,那不如跟着我,接着往下看。相信你看完,不需要看到答案都能有个肯定的答案了~!
源码位于core/util/next-tick中。可以将其分为4个部分来看,直接上代码
callbacks队列、pending状态
const callbacks = [] // 存放cb的队列 let pending = false // 是否马上遍历队列,执行cb的标志
flushCallbacks遍历callbacks执行每个cb
function flushCallbacks () {
pending = false // 注意这里,一旦执行,pending马上被重置为false
const copies = callbacks.slice(0)
callbacks.length = 0
for (let i = 0; i < copies.length; i++) {
copies[i]() // 执行每个cb
}
}nextTick的异步实现根据执行环境的支持程度采用不同的异步实现策略
let timerFunc // nextTick异步实现fn
if (typeof Promise !== 'undefined' && isNative(Promise)) {
// Promise方案
const p = Promise.resolve()
timerFunc = () => {
p.then(flushCallbacks) // 将flushCallbacks包装进Promise.then中
}
isUsingMicroTask = true
} else if (!isIE && typeof MutationObserver !== 'undefined' && (
isNative(MutationObserver) ||
MutationObserver.toString() === '[object MutationObserverConstructor]'
)) {
// MutationObserver方案
let counter = 1
const observer = new MutationObserver(flushCallbacks) // 将flushCallbacks作为观测变化的cb
const textNode = document.createTextNode(String(counter)) // 创建文本节点
// 观测文本节点变化
observer.observe(textNode, {
characterData: true
})
// timerFunc改变文本节点的data,以触发观测的回调flushCallbacks
timerFunc = () => {
counter = (counter + 1) % 2
textNode.data = String(counter)
}
isUsingMicroTask = true
} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
// setImmediate方案
timerFunc = () => {
setImmediate(flushCallbacks)
}
} else {
// 最终降级方案setTimeout
timerFunc = () => {
setTimeout(flushCallbacks, 0)
}
}这里用个真实案例加深对MutationObserver的理解。毕竟比起其他三种异步方案,这个应该是大家最陌生的
const observer = new MutationObserver(() => console.log('观测到文本节点变化'))
const textNode = document.createTextNode(String(1))
observer.observe(textNode, {
characterData: true
})
console.log('script start')
setTimeout(() => console.log('timeout1'))
textNode.data = String(2) // 这里对文本节点进行值的修改
console.log('script end')知道对应的输出会是怎么样的吗?
script start、script end会在第一轮宏任务中执行,这点没问题
setTimeout会被放入下一轮宏任务执行
MutationObserver是微任务,所以会在本轮宏任务后执行,所以先于setTimeout
结果如下图:
nextTick方法实现cb、Promise方式
export function nextTick (cb?: Function, ctx?: Object) {
let _resolve
// 往全局的callbacks队列中添加cb
callbacks.push(() => {
if (cb) {
try {
cb.call(ctx)
} catch (e) {
handleError(e, ctx, 'nextTick')
}
} else if (_resolve) {
// 这里是支持Promise的写法
_resolve(ctx)
}
})
if (!pending) {
pending = true
// 执行timerFunc,在下一个Tick中执行callbacks中的所有cb
timerFunc()
}
// 对Promise的实现,这也是我们使用时可以写成nextTick.then的原因
if (!cb && typeof Promise !== 'undefined') {
return new Promise(resolve => {
_resolve = resolve
})
}
}深入细节,理解pending有什么用,如何运作?
案例1,同一轮Tick中执行2次$nextTick,timerFunc只会被执行一次
this.$nextTick(() => console.log('nextTick1'))
this.$nextTick(() => console.log('nextTick2'))用图看看更直观?

这里如果有对Vue组件化、派发更新不是十分了解的朋友,可以先戳这里,看图解Vue响应式原理了解下Vue组件化和派发更新的相关内容再回来看噢~
Vue的异步更新DOM其实也是使用nextTick来实现的,跟我们平时使用的$nextTick其实是同一个~
这里我们回顾一下,当我们改变一个属性值的时候会发生什么?

根据上图派发更新过程,我们从watcher.update开时讲起,以渲染Watcher为例,进入到queueWatcher里
queueWatcher做了什么?// 用来存放Wathcer的队列。注意,不要跟nextTick的callbacks搞混了,都是队列,但用处不同~ const queue: Array= [] function queueWatcher (watcher: Watcher) { const id = watcher.id // 拿到Wathcer的id,这个id每个watcher都有且全局唯一 if (has[id] == null) { // 避免添加重复wathcer,这也是异步渲染的优化做法 has[id] = true if (!flushing) { queue.push(watcher) } if (!waiting) { waiting = true // 这里把flushSchedulerQueue推进nextTick的callbacks队列中 nextTick(flushSchedulerQueue) } } }
flushSchedulerQueue做了什么?function flushSchedulerQueue () {
currentFlushTimestamp = getNow()
flushing = true
let watcher, id
// 排序保证先父后子执行更新,保证userWatcher在渲染Watcher前
queue.sort((a, b) => a.id - b.id)
// 遍历所有的需要派发更新的Watcher执行更新
for (index = 0; index < queue.length; index++) {
watcher = queue[index]
id = watcher.id
has[id] = null
// 真正执行派发更新,render -> update -> patch
watcher.run()
}
}最后,一张图搞懂组件的异步更新过程

相信经过上文对nextTick源码的剖析,我们已经揭开它神秘的面纱了。这时的你一定可以坚定地把答案说出来了~话不多说,我们一起核实下,看看是不是如你所想!
1、如图所示,mounted时候的innerText是“井柏然”的中文

2、接下来是点击按钮后,打印结果如图所示

没错,输出结果如下(意不意外?惊不惊喜?)
sync log 井柏然
nextTick1 井柏然
nextTick2 jngboran
下面简单分析一下每个输出:
this.$nextTick(() => console.log('nextTick1', this.$refs.name.innerText))
this.name = 'jngboran'
console.log('sync log', this.$refs.name.innerText)
this.$nextTick(() => console.log('nextTick2', this.$refs.name.innerText))sync log:这个同步打印没什么好说了,相信大部分童鞋的疑问点都不在这里。如果不清楚的童鞋可以先回顾一下EventLoop,这里不多赘述了~
nextTick1:注意其虽然是放在$nextTick的回调中,在下一个tick执行,但是他的位置是在this.name = 'jngboran'的前。也就是说,他的cb会比App组件的派发更新(flushSchedulerQueue)更先进入队列,当nextTick1打印时,App组件还未派发更新,所以拿到的还是旧的DOM值。
nextTick2就不展开了,大家可以自行分析一下。相信大家对它应该是最肯定的,我们平时不就是这样拿到更新后的DOM吗?
最后来一张图加深理解

到此,关于“Vue中的Vue.nextTick的异步怎么实现”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注创新互联网站,小编会继续努力为大家带来更多实用的文章!
Copyright © 2009-2022 www.fjjierui.cn 青羊区广皓图文设计工作室(个体工商户)达州站 版权所有 蜀ICP备19037934号