nexttick(如何使用VuenextTick)
本文目录
如何使用VuenextTick
这次给大家带来如何使用Vue nextTick,使用Vue nextTick的注意事项有哪些,下面就是实战案例,一起来看一下。export default { data () { return { msg: 0 } }, mounted () { this.msg = 1 this.msg = 2 this.msg = 3 }, watch: { msg () { console.log(this.msg) } }}这段脚本执行我们猜测1000m后会依次打印:1、2、3。但是实际效果中,只会输出一次:3。为什么会出现这样的情况?我们来一探究竟。queueWatcher我们定义 watch 监听 msg ,实际上会被Vue这样调用 vm.$watch(keyOrFn, handler, options) 。 $watch 是我们初始化的时候,为 vm 绑定的一个函数,用于创建 Watcher 对象。那么我们看看 Watcher 中是如何处理 handler 的:this.deep = this.user = this.lazy = this.sync = false... update () { if (this.lazy) { this.dirty = true } else if (this.sync) { this.run() } else { queueWatcher(this) } }...初始设定 this.deep = this.user = this.lazy = this.sync = false ,也就是当触发 update 更新的时候,会去执行 queueWatcher 方法:const queue: Array《Watcher》 = let has: { : ?true } = {}let waiting = falselet flushing = false...export function queueWatcher (watcher: Watcher) { const id = watcher.id if (has == null) { has = true if (!flushing) { queue.push(watcher) } else { // if already flushing, splice the watcher based on its id // if already past its id, it will be run next immediately. let i = queue.length - 1 while (i 》 index && queue.id 》 watcher.id) { i-- } queue.splice(i + 1, 0, watcher) } // queue the flush if (!waiting) { waiting = true nextTick(flushSchedulerQueue) } }}这里面的 nextTick(flushSchedulerQueue) 中的 flushSchedulerQueue 函数其实就是 watcher 的视图更新:function flushSchedulerQueue () { flushing = true let watcher, id ... for (index = 0; index 《 queue.length; index++) { watcher = queue id = watcher.id has = null watcher.run() ... }}另外,关于 waiting 变量,这是很重要的一个标志位,它保证 flushSchedulerQueue 回调只允许被置入 callbacks 一次。 接下来我们来看看 nextTick 函数,在说 nexTick 之前,需要你对 Event Loop 、 microTask 、 macroTask 有一定的了解,Vue nextTick 也是主要用到了这些基础原理。如果你还不了解,可以参考我的这篇文章 Event Loop 简介 好了,下面我们来看一下他的实现:export 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() } } // An asynchronous deferring mechanism. // In pre 2.4, we used to use microtasks (Promise/MutationObserver) // but microtasks actually has too high a priority and fires in between // supposedly sequential events (e.g. #4521, #6690) or even between // bubbling of the same event (#6566). Technically setImmediate should be // the ideal choice, but it’s not available everywhere; and the only polyfill // that consistently queues the callback after all DOM events triggered in the // same loop is by using MessageChannel. /* istanbul ignore if */ if (typeof setImmediate !== ’undefined’ && isNative(setImmediate)) { timerFunc = () =》 { setImmediate(nextTickHandler) } } else if (typeof MessageChannel !== ’undefined’ && ( isNative(MessageChannel) || // PhantomJS MessageChannel.toString() === ’’ )) { const channel = new MessageChannel() const port = channel.port2 channel.port1.onmessage = nextTickHandler timerFunc = () =》 { port.postMessage(1) } } else /* istanbul ignore next */ if (typeof Promise !== ’undefined’ && isNative(Promise)) { // use microtask in non-DOM environments, e.g. Weex const p = Promise.resolve() timerFunc = () =》 { p.then(nextTickHandler) } } else { // fallback to setTimeout timerFunc = () =》 { setTimeout(nextTickHandler, 0) } } return function queueNextTick (cb?: Function, ctx?: Object) { let _resolve callbacks.push(() =》 { if (cb) { try { cb.call(ctx) } catch (e) { handleError(e, ctx, ’nextTick’) } } else if (_resolve) { _resolve(ctx) } }) if (!pending) { pending = true timerFunc() } // $flow-disable-line if (!cb && typeof Promise !== ’undefined’) { return new Promise((resolve, reject) =》 { _resolve = resolve }) } }})()首先Vue通过 callback 数组来模拟事件队列,事件队里的事件,通过 nextTickHandler 方法来执行调用,而何事进行执行,是由 timerFunc 来决定的。我们来看一下 timeFunc 的定义:if (typeof setImmediate !== ’undefined’ && isNative(setImmediate)) { timerFunc = () =》 { setImmediate(nextTickHandler) } } else if (typeof MessageChannel !== ’undefined’ && ( isNative(MessageChannel) || // PhantomJS MessageChannel.toString() === ’’ )) { const channel = new MessageChannel() const port = channel.port2 channel.port1.onmessage = nextTickHandler timerFunc = () =》 { port.postMessage(1) } } else /* istanbul ignore next */ if (typeof Promise !== ’undefined’ && isNative(Promise)) { // use microtask in non-DOM environments, e.g. Weex const p = Promise.resolve() timerFunc = () =》 { p.then(nextTickHandler) } } else { // fallback to setTimeout timerFunc = () =》 { setTimeout(nextTickHandler, 0) } }可以看出 timerFunc 的定义优先顺序 macroTask --》 microTask ,在没有 Dom 的环境中,使用 microTask ,比如weexsetImmediate、MessageChannel VS setTimeout我们是优先定义 setImmediate 、 MessageChannel 为什么要优先用他们创建macroTask而不是setTimeout? HTML5中规定setTimeout的最小时间延迟是4ms,也就是说理想环境下异步回调最快也是4ms才能触发。Vue使用这么多函数来模拟异步任务,其目的只有一个,就是让回调异步且尽早调用。而MessageChannel 和 setImmediate 的延迟明显是小于setTimeout的。解决问题有了这些基础,我们再看一遍上面提到的问题。因为 Vue 的事件机制是通过事件队列来调度执行,会等主进程执行空闲后进行调度,所以先回去等待所有的进程执行完成之后再去一次更新。这样的性能优势很明显,比如:现在有这样的一种情况,mounted的时候test的值会被++循环执行1000次。 每次++时,都会根据响应式触发 setter-》Dep-》Watcher-》update-》run 。 如果这时候没有异步更新视图,那么每次++都会直接操作DOM更新视图,这是非常消耗性能的。 所以Vue实现了一个 queue 队列,在下一个Tick(或者是当前Tick的微任务阶段)的时候会统一执行 queue 中 Watcher 的run。同时,拥有相同id的Watcher不会被重复加入到该queue中去,所以不会执行1000次Watcher的run。最终更新视图只会直接将test对应的DOM的0变成1000。 保证更新视图操作DOM的动作是在当前栈执行完以后下一个Tick(或者是当前Tick的微任务阶段)的时候调用,大大优化了性能。有趣的问题var vm = new Vue({ el: ’#example’, data: { msg: ’begin’, }, mounted () { this.msg = ’end’ console.log(’1’) setTimeout(() =》 { // macroTask console.log(’3’) }, 0) Promise.resolve().then(function () { //microTask console.log(’promise!’) }) this.$nextTick(function () { console.log(’2’) }) }})这个的执行顺序想必大家都知道先后打印:1、promise、2、3。因为首先触发了 this.msg = ’end’ ,导致触发了 watcher 的 update ,从而将更新操作callback push进入vue的事件队列。this.$nextTick 也为事件队列push进入了新的一个callback函数,他们都是通过 setImmediate --》 MessageChannel --》 Promise --》 setTimeout 来定义 timeFunc 。而 Promise.resolve().then 则是microTask,所以会先去打印promise。在支持 MessageChannel 和 setImmediate 的情况下,他们的执行顺序是优先于 setTimeout 的(在IE11/Edge中,setImmediate延迟可以在1ms以内,而setTimeout有最低4ms的延迟,所以setImmediate比setTimeout(0)更早执行回调函数。其次因为事件队列里,优先收入callback数组)所以会打印2,接着打印3但是在不支持 MessageChannel 和 setImmediate 的情况下,又会通过 Promise 定义 timeFunc ,也是老版本Vue 2.4 之前的版本会优先执行 promise 。这种情况会导致顺序成为了:1、2、promise、3。因为this.msg必定先会触发dom更新函数,dom更新函数会先被callback收纳进入异步时间队列,其次才定义 Promise.resolve().then(function () { console.log(’promise!’)}) 这样的microTask,接着定义 $nextTick 又会被callback收纳。我们知道队列满足先进先出的原则,所以优先去执行callback收纳的对象。相信看了本文案例你已经掌握了方法,更多精彩请关注Gxl网其它相关文章!推荐阅读:如何操作JS实现透明度渐变动画怎样操作JS实现简单折叠展开动画
this.$nextTick()怎么使用
应用场景 : this.$nextTick() 方法主要是用在 随数据改变而改变的dom应用场景中 ,vue中数据和dom渲染由于是异步的,所以,要让dom结构随数据改变这样的操作都应该放进this. $nextTick() 的回调函数中。 created()中使用的方法时,dom还没有渲染,如果此时在该钩子函数中进行dom赋值数据(或者其它dom操作)时无异于徒劳,所以, created()钩子函数进行的DOM操作一定要放在Vue.nextTick()的回调函数中 ,而与created()对应的是mounted()的钩子函数则是在dom完全渲染后才开始渲染数据,所以在mounted()中操作dom基本不会存在渲染问题。
简单的理解,vue.js中this.$nextTick()就是起到了一个等待数据的作用,也就是说,将一些回调延迟,等到DOM更新之后再开始执行。简单点说,相当于setTimeout()的作用。
例如: 1.你改变了dom元素数据,然后你又想输出dom,那你只能等到dom更新完成之后才会实现. 2.通过事件改变data数据,然后输出dom,在方法里直接打印的话, 由于dom元素还没有更新, 因此打印出来的还是未改变之前的值,而通过this.$nextTick()获取到的值为dom更新之后的值.
更多文章:

联想yoga13论坛(联想yoga-13 开机黑屏,只有鼠标,怎么办)
2024年12月25日 13:00

oppo reno4手机图片(OPPO Reno4价格是多少,值得吗)
2024年9月25日 20:40

canon相机拍照按什么键(canon单反相机eos拍摄键是那个)
2024年6月8日 16:30

联想g470奔腾(联想G470 CPU是奔腾B940的 做PS CDR 会不会卡或者说慢)
2024年12月3日 18:40

乐视被谁收购了(贾跃亭同意60.41亿出售乐视网1.71亿股无限售流通股了吗)
2024年12月21日 18:10

老式三星数码相机型号价格(三星i8数码相机现在要多少人民币)
2024年12月14日 21:30

mx940相当于什么显卡(笔记本电脑独显940MX是什么级别的显卡)
2024年10月23日 23:30

二手努比亚z17多少钱(努比亚Z17价格多少钱 努比亚Z17什么时候上市)
2024年4月7日 05:10

iphone7发热已解决(iPhone发烫是什么原因 怎样解决)
2025年2月5日 11:30

齐心考勤机mt620修改时间(齐心牌mt620t打卡机怎么调整日期)
2024年12月30日 11:40