这是我参与8月更文挑战的第3天,活动详情查看:8月更文挑战 html
nextTick 本质就是执行延迟回调的钩子,接受一个回调函数做为参数,在下次 DOM 更新循环结束以后执行延迟回调。在修改数据以后当即使用这个方法,获取更新后的 DOM。Vue 2.1.0 开始,若是没有提供回调函数,且在支持 Promise 的环境中,则返回一个 Promise 。注意 Vue 自己是不自带 polyfill 的,若是环境不支持 Promise ,则须要本身提供 polyfill。vue
提及 nextTick ,也不得不说说 Vue 的异步更新,Vue 在更新 DOM 时是异步执行的。只要侦听到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的全部数据变动。若是同一个 watcher 被屡次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免没必要要的计算和 DOM 操做是很是重要的。而后,在下一个的事件循环“tick”中,Vue 刷新队列并执行实际 (已去重的) 工做。Vue(2.6.x) 在内部对异步队列尝试使用原生的 Promise.then
、MutationObserver
和 setImmediate
,若是执行环境不支持,则会采用 setTimeout(fn, 0)
代替。git
例如,当你设置 vm.someData = 'new value'
,该组件不会当即从新渲染。当刷新队列时,组件会在下一个事件循环“tick”中更新。多数状况咱们不须要关心这个过程,可是若是你想基于更新后的 DOM 状态来作点什么,这就可能会有些棘手。虽然 Vue.js 一般鼓励开发人员使用“数据驱动”的方式思考,避免直接接触 DOM,可是有时咱们必需要这么作。为了在数据变化以后等待 Vue 完成更新 DOM,能够在数据变化以后当即使用 Vue.nextTick(callback)
。这样回调函数将在 DOM 更新完成后被调用。github
nextTick 除了让咱们能够在 DOM 更新以后执行延迟回调,还有一个做用就是 Vue 内部 使用nextTick,把渲染 Dom 操做这个操做 放入到 callbacks 中。web
从字面意思理解,next 下一个,tick 滴答(钟表)来源于定时器的周期性中断(输出脉冲),一次中断表示一个 tick,也被称作一个“时钟滴答”,nextTick 顾名思义就是下一个时钟滴答,下一个任务。下一个任务,在 Event Loop 中在熟悉不过了经过一个例子简单回忆一下 Event Loop。面试
console.log('同步代码1');
setTimeout(() => {
console.log('setTimeout')
}, 0)
new Promise((resolve) => {
console.log('同步代码2')
resolve()
}).then(() => {
console.log('promise.then')
})
console.log('同步代码3');
// 最终输出"同步代码1"、"同步代码2"、"同步代码3"、"promise.then"、"setTimeout"
复制代码
了解了浏览器的事件循环机制以后,咱们回头来看 Vue nextTick。 nextTick 是下次DOM更新循环结束后执行延迟回调。segmentfault
nextTick 的原理,用一句话总结就是『利用 Event loop 事件线程去异步操做』。本质上就是注册异步任务来对任务进行处理。不一样的是,在Vue 的不一样版本对这个异步任务的优雅降级不太同样。api
<div id="example">
<span>{{test}}</span>
<button @click="handleClick">change</button>
</div>
复制代码
var vm = new Vue({
el: '#example',
data: {
test: 'begin',
},
methods: {
handleClick: function() {
this.test = 1;
console.log('script')
this.$nextTick(function () {
console.log('nextTick')
});
Promise.resolve().then(function () {
console.log('promise')
})
}
}
});
复制代码
Vue 2.4 输出 script、nextTick、promise。nextTick 执行顺序的。测试源码:连接。数组
Vue 2.5+ 中,这段代码的输出顺序是 script、promise、nextTick。测试源码:连接。promise
Vue 2.6+ 中,输出 script、nextTick、promise。虽然这里和 Vue2.4 输出一致,可是内部实现不太同样,后面会讲到。
注意:这里输出的顺序并非惟一的,还和 API 兼容性有关系。
nextTick 的源码很简单(示例截图:2.6.11,2.x 版本这里差异不大,差异在于 timerFunc 的包装,后面会讲到),就只有几行代码。大体步骤以下:
经过数组 callbacks 来存储用户注册的回调。声明了变量 pending 来标记是否正在执行任务。这里使用一个异步锁,等待任务队列执行完毕以后,在执行下一个任务。当前任务队列正常进行时,将 pending 设置为 true,每当任务被执行完成时将 pending 设置为 false,这样就能够经过 pending 的值来判断当前的任务队列是否在执行,新来的任务是否须要放到下一次的任务队列中。在当前的队列中,执行函数 flushCallbacks。当这个函数被触发时,会将 callbacks 中的全部函数依次执行,而后清空 callbacks,并将 pending 设置为 false。即一轮事件循环中,flushCallbacks 只会执行一次。这里须要注意,执行 flushCallbacks 函数时备份回调函数队列。由于,会出现这么一种状况 nextTick 的回调函数中还使用 nextTick。若是 flushCallbacks 不作特殊处理,直接循环执行回调函数,会致使里面nextTick 中的回调函数会直接进入回调队列。
从 Vue 的 git 上拉取了2.0版本以后关于 timerFunc 的包装,发如今 2.4 版本以前包装都是同样的。优雅降级方案为:
可是这样的方案,在后续的版本中已经代表是有必定的问题,问题在于因为 microTask 的执行优先级很是高,在某些场景之下它甚至要比事件冒泡还要快,就会致使一些诡异的问题。 例如:
issues:link
针对 2.5 版本以前的问题,进行了一个 timerFunc 的从新包装:
在 2.5 版本中,将 microTask 混合 macroTask 进行优雅降级,可是这个方案也存在一些问题:
issues:link
因为截图可能问题不太明显,codepen.io/ericcirone/…,你们有兴趣能够看源测试代码,问题点在于当页面在1000px(大于1000,小于1000)来回切换时,列表显示影藏存在 1s 的闪烁。本质上缘由是优先使用 macroTask,对一些有重绘和动画的场景会有性能的影响,形成了闪烁。
为了解决以前版本的一些历史问题,最终 nextTick 采起的策略是默认走 microTask ,对于一些 DOM 的交互事件,如 v-on 绑定的事件回调处理函数的处理,会强制走 macroTask。在源码层面上也存在一个优雅的降级,以下:
nextTick 在面试中会常常出现,面试官通常经过 nextTick 考验候选人的 Event Loop,或者经过 Event Loop 衍生 nextTick。文章从几个方面浅析了 Vue 的 nextTick 的原理,也从Vue 升级版原本窥视了 nextTick 的前世此生,但愿对正在阅读的你有所帮助。
以上就是本文的所有内容。谢谢观看,若是你还以为不错,帮忙点个赞,谢谢。