Vue.nextTick用法和实现

3/21/2023 vue

# 1. 用法

引用我认为最官方的官方说法 (opens new window)

Vue.nextTick(callback?: () => void): Promise<void>

当你在 Vue 中更改响应式状态时,最终的 DOM 更新并不是同步生效的,而是由 Vue 将它们缓存在一个队列中,直到下一个“tick”才一起执行。这样是为了确保每个组件无论发生多少状态改变,都仅执行一次更新。 nextTick()  可以在状态改变后立即使用,以等待 DOM 更新完成。你可以传递一个回调函数作为参数,或者 await 返回的 Promise。

使用示例

<template>
	<button id="counter" @click="increment">{{ count }}</button>
</template>
<script>
	export default {
	  data: () => ({ count: 0 })
	  methods: {
	    async increment() {
	      this.count++
	      console.log(document.getElementById('counter').textContent) // DOM还未更新 0
	      await nextTick(()=>console.log('DOM此时已挂载,在数据更新前作一些操作'))
	      console.log(document.getElementById('counter').textContent) // DOM已更新 1
	    }
	  }
	}
</script>

# 2. 实现

“ Talk is cheap, show me the code ”

Vue.nextTick = (() => {
	function createTimerFunc() {
		// 在页面重排重绘之后执行
		// Promise.resolve.then -> MutationObserver -> setImmediate -> setTimeout
		// 这些都是可以异步回调,异步队列里的任务是在同步任务执行完毕->浏览器重排重绘 之后才执行
		if (Promise) return () => Promise.resolve.then(flushCallbacks) // 满足绝大多数浏览器

		// 以下是兼容处理
		if (MutationObserver) {
			// 在监听的DOM改变后会执行回调
			let counter = 1
			let observer = new MutationObserver(flushCallbacks)
			let textNode = document.createTextNode(String(counter)) // 创建监听的文本节点
			observer.observe(textNode, { characterData: true })
			return () => {
				counter ^= 1
				textNode.data = String(counter)
			}
		}

		if (setImmediate) return () => setImmediate(flushCallbacks)
		if (setTimeout) return () => setTimeout(flushCallbacks)
	}
	let timerFunc = createTimerFunc()

	let callbacks = []
	let pending = false
	function flushCallbacks() {
		// 从回调队列里弹出回调函数,逐个执行
		pending = false
		while (callbacks.length) callbacks.shift()()
	}

	// 主程序
	return (cb) => {
		// 原理:在注册的异步回调函数被执行之前,往回调队列callbacks里push回调函数
		callbacks.push(cb)
		if (pending) return
		pending = true
		timerFunc()
	}
})()
    一定要爱着点什么,
    恰似草木对光阴的钟情。
    红莲华
    x
    loading...