代码
vue 的nextTick的主要代码如下:
// 触发回调数组里面所有的回调function flushCallbacks () { pending = false const copies = callbacks.slice(0) callbacks.length = 0 for (let i = 0; i < copies.length; i++) { copies[i]() }}// 延迟触发macroTimerFunc = () => { setImmediate(flushCallbacks)}// 第一个参数回调函数,第二个参数是执行函数上下文export function nextTick (cb?: Function, ctx?: Object) { let _resolve // 将回调push到回调数组中 在下一次nexttick的时候统一触发 callbacks.push(() => { if (cb) { try { cb.call(ctx) } catch (e) { handleError(e, ctx, 'nextTick') } } else if (_resolve) { _resolve(ctx) } }) if (!pending) { pending = true if (useMacroTask) { macroTimerFunc() } else { microTimerFunc() } } // 如果没有回调函数则返回一个promise对象 作为回调函数 if (!cb && typeof Promise !== 'undefined') { return new Promise(resolve => { _resolve = resolve }) }}// 设置$nextTick 方法Vue.prototype.$nextTick = function (fn: Function) { return nextTick(fn, this)}复制代码
今天做一个播放webrtc视频功能的时候碰到了一个小问题,很奇怪所以稍微研究了下vue的nextTick。
问题
问题描述
在设置video标签的srcObject属性的时候,当我使用
this.$nextTick(() => { if (this.$refs.videoNode) { this.$refs.videoNode.srcObject = this.videoStream; }});复制代码
去设置的时候我发现视频是空白的,经过尝试视频流和video标签都是是没有问题的。那就只有调用的时机的问题了。 然后查看nextTick的代码之后选择第二种方式
this.$nextTick().then(() => { if (this.$refs.videoNode) { this.$refs.videoNode.srcObject = this.videoStream; }})复制代码
发现视频是正常播放的。 然后想起来我video标签是放在弹出层dialog中的,dialog会去修改里面元素的位置代码如下:
this.$nextTick(() => { let moveNodeRef = this.$refs.moveNodeRef; if (moveNodeRef) { if (this.parentNode) { this.parentNode.appendChild(moveNodeRef); } else if (this.appendToBody) { document.body.appendChild(moveNodeRef); } } });复制代码
在nextTick的回调函数的中去把元素插入到其他的元素中。于是就尝试了下在设置完video的srcObject后再去移动video元素发现视频卡在了移动时候的那一帧画面。
原因
nextTick会在一次事件循环之后一起调用。给video的srcObject的赋值在dialog的移动dom之前,所以导致video不能播放视频,而第二种写法是promise,promise是异步的,会在本次事件循环结束之后才会调用,因此会在dom移动之后才会触发,因此没有问题。
结论
看似两种写法没有多大的区别,但是调用的先后顺序还是有很大的区别的!