一. 前言
一直都想在掘金写点东西, 但是总是害怕写得不好, 所以都是写了放在一个 private 的仓库里, 孤芳自赏. 今天早上起来, 花了一两个小时写的这篇算是一个开始(不敢写时间长, 自己看到漏洞或者问题多了, 就又不敢发了, 求饶过), 以后会努力多发一些文章, just do it.
二. 用法
vue 官网是这样介绍的. 地址
在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。 其实这个方法就是可以一个回调函数放到事件循环的任务中去执行.
- 会在当前环境按照
Promise
>MutationObserver
>setImmediate
>setTimeout
的顺序找到支持的任务, 可以看出优先使用微任务Promise
, 如果不支持会一路降级到宏任务setTimeout
- 因为即使是在最快的
Promise
这个微任务中, 当前的同步任务(可以理解当前正在执行的代码是同步任务, 哪怕它是个宏任务)也已经执行完了, dom 就已经是更新了的了. - 所以它的应用场景可以是为了等 dom 更新完成, 也可以是等当前同步代码执行完成.
- 等待 dom 更新完成, 比如想拿到修改了数据之后(此时的 dom 可能会被修改)的 dom 的宽高, 需要注意修改数据和 nextTick 的顺序, 下面会解释
三.写法
vue 官网是这样介绍的. 地址
// 修改数据
vm.msg = 'Hello'
// DOM 还没有更新
Vue.nextTick(function () {
// DOM 更新了
})
// 作为一个 Promise 使用 (2.1.0 起新增,详见接下来的提示)
Vue.nextTick().then(function () {
// DOM 更新了
})
刚开始我还不理解为什么要加一个 Promise 的使用方法, 直到我看到了这种写法就理解了,完整代码
export default {
methods: {
updateMessage: async function () {
this.message = '已更新'
console.log(this.$el.textContent) // => '未更新'
await this.$nextTick()
console.log(this.$el.textContent) // => '已更新'
},
},
}
四.源码解析
完整版自己去 github 上看吧, 家里网络实在是打不开 github🤦🏻♀️
/**
*
* @param cb 要添加的回调函数
* @param ctx 添加回调函数执行时的上下文this
* @returns Promise | void
*/
export function nextTick(cb?: Function, ctx?: Object) {
let _resolve
callbacks.push(() => {
if (cb) {
cb.call(ctx)
} else if (_resolve) {
_resolve(ctx)
}
})
if (!pending) {
pending = true
timerFunc()
}
if (!cb && typeof Promise !== 'undefined') {
return new Promise((resolve) => {
_resolve = resolve
})
}
}
内部使用到的变量
-
callbacks
用来缓存当前一个 tick 添加的回调, 就是在当前同步代码中添加的所有回调都会保存在这个数组中.
-
pending
用来标识是否正在执行回调, 就是有可能在回调里面又添加了回调
如果
pending
为true
时, 表示回调队列正在执行, 此时只是把当前的回调放入了回调队列, 不再执行新的回调队列callbacks
-
timerFunc
这个是回调执行的任务函数(
Promise
>MutationObserver
>setImmediate
>setTimeout
)四个中的某一个, 可以就认为它是Promise.prototype.then
,它会把callbacks
中保存的回调都拿出来, 执行之后, 清空callbacks
执行的时机, 通过
nextTick
添加回调时, 此时没有上一个回调队列正在执行.
五.使用情况
在 vue 源码中, 它在三个地方被使用了
Vue.nextTick
Vue.nextTick = nextTick
$nextTick
Vue.prototype.$nextTick = function (fn: Function) {
return nextTick(fn, this)
}
nextTick(flushSchedulerQueue)
这个是
queueWatcher
中执行的回调, 这个是数据绑定中的点了, 这里就不谈论了, 就是当vue
实例数据更新会触发的回调.
export default {
methods: {
updateMessage: async function () {
this.$nextTick(() => {
console.log(this.$el.textContent) // => '未更新'
})
this.message = '已更新'
this.$nextTick(() => {
console.log(this.$el.textContent) // => '已更新'
})
},
},
}
所以要注意this.$nextTick
书写的位置呀
六.完
不写点什么了, 赶紧点发布......