nextTick
在哪里使用?原理是什么?
标签:nextTick,timerFunc,resolve,函数,异步,源码,Promise,vue2 From: https://www.cnblogs.com/dgqp/p/17347669.html
nextTick
内部采用了异步任务进行包装(多个nextTick
调用会被合并成一次,内部会合并回调)最后在异步任务中批处理。主要应用场景就是异步更新(默认调度的时候就会添加一个·
nextTick
任务)用户为了获取最终的渲染结果需要在内部任务执行之后再执行用户逻辑这时候需要将对应的逻辑放到nextTick
中。
使用场景
如果想要在修改数据后立刻得到更新后的
DOM
结构,可以使用Vue.nextTick()
第一个参数为:回调函数。
第二个参数为:执行函数上下文。
// 修改数据 vm.message = '修改后的值' // DOM 还没有更新 console.log(vm.$el.textContent) // 原始的值 Vue.nextTick(function () { // DOM 更新了 console.log(vm.$el.textContent) // 修改后的值 })
实现原理
- 将回调函数加入队列。
- 执行队列中的回调函数。
callbacks
也就是异步更新操作队列。
callbacks
新增一个回调函数后有执行一个timefunc
函数,pending
使用来标识同一时间只能执行一次export function nextTick(cb?: Function, ctx?: Object) { let _resolve; // cb 回调函数会经统一处理压入 callbacks 数组 callbacks.push(() => { if (cb) { // 给 cb 回调函数执行加上了 try-catch 错误处理 try { cb.call(ctx); } catch (e) { handleError(e, ctx, 'nextTick'); } } else if (_resolve) { _resolve(ctx); } }); // 执行异步延迟函数 timerFunc if (!pending) { pending = true; timerFunc(); } // 当 nextTick 没有传入函数参数的时候,返回一个 Promise 化的调用 if (!cb && typeof Promise !== 'undefined') { return new Promise(resolve => { _resolve = resolve; }); } }
timerFunc
函数定义,这里是根据当前环境支持什么方法则确定调用哪个,分别有:
Promise.then
、MutationObserver
、setImmediate
、setTimeout
export let isUsingMicroTask = false if (typeof Promise !== 'undefined' && isNative(Promise)) { //判断1:是否原生支持Promise const p = Promise.resolve() timerFunc = () => { p.then(flushCallbacks) if (isIOS) setTimeout(noop) } isUsingMicroTask = true } else if (!isIE && typeof MutationObserver !== 'undefined' && ( isNative(MutationObserver) || MutationObserver.toString() === '[object MutationObserverConstructor]' )) { //判断2:是否原生支持MutationObserver let counter = 1 const observer = new MutationObserver(flushCallbacks) const textNode = document.createTextNode(String(counter)) observer.observe(textNode, { characterData: true }) timerFunc = () => { counter = (counter + 1) % 2 textNode.data = String(counter) } isUsingMicroTask = true } else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) { //判断3:是否原生支持setImmediate timerFunc = () => { setImmediate(flushCallbacks) } } else { //判断4:上面都不行,直接用setTimeout timerFunc = () => { setTimeout(flushCallbacks, 0) } }
最后进行使用:即
flushCallbacks
方法function flushCallbacks () { pending = false const copies = callbacks.slice(0) callbacks.length = 0 for (let i = 0; i < copies.length; i++) { copies[i]() } }
总结
总之,
nextTick
的源码实现比较复杂,但其核心思想是利用异步任务队列,在DOM
更新后执行回调函数。Vue
提供了多种不同的异步任务安排方式,根据环境的支持情况选择合适的方式。同时,为了保证代码的稳定性和性能,Vue
实现中还考虑了回调函数异常处理、数组复制等多种细节问题。