$nextTick原理
Vue官网对数据操作是这么描述的
能你还没有注意到,Vue 在更新 DOM 时是异步执行的。只要侦听到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。如果同一个 watcher 被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作是非常重要的。然后,在下一个的事件循环“tick”中,Vue 刷新队列并执行实际 (已去重的) 工作。Vue 在内部对异步队列尝试使用原生的 Promise.then、MutationObserver 和 setImmediate,如果执行环境不支持,则会采用 setTimeout(fn, 0) 代替。
this.$nextTick,在我的感觉中,就相当于一个延迟处理吧.可以用来获取到数据更新之后的DOM
// Promise.resolve().then(() => { // console.log(this.$refs.searchInput) // this.$refs.searchInput.focus() // }) // setTimeout(() => { // console.log(this.$refs.searchInput) // this.$refs.searchInput.focus() // }, 0) this.$nextTick(() => { console.log(this.$refs.searchInput) this.$refs.searchInput.focus() })
源码分析
当我们调用了.this.nextTick这个函数之后,传入回调函数,这个回调函数
export function nextTick (cb?: Function, ctx?: Object) { let _resolve callbacks.push(() => { if (cb) { try { cb.call(ctx) 把$nextTick的回调函数(cb)放到了一个叫callbacks的一个数组里面,这个数组是在最上面定义的, 如果你调用了多个this.$nextTick 数组里面就有了多个待执行的回调函数 const callbacks = [] let pending = false function flushCallbacks () { pending = false const copies = callbacks.slice(0) callbacks.length = 0 for (let i = 0; i < copies.length; i++) { copies[i]() } } flushCallbacks这个函数.如果调用他 它里面就会取出nextTick的回调函数来进行执行
而这个flushCallbacks 是写了一个判断.浏览器的降级处理.如果浏览器支持promise 就把这些会回调函数放到了promise.then里面 执行了否则调用后面的.做了四层判断
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) } }
可以看出这边代码其实是做了四个判断,对当前环境进行不断的降级处理,尝试使用原生的Promise.then、MutationObserver和setImmediate,上述三个都不支持最后使用setTimeout;降级处理的目的都是将flushCallbacks函数放入微任务(判断1和判断2)或者宏任务(判断3和判断4),等待下一次事件循环时来执行。
总结
到这里,整体nextTick的代码都分析完毕了,总结一下它的流程就是:
- 把回调函数放入callbacks等待执行
- 将执行函数放到微任务或者宏任务中
- 事件循环到了微任务或者宏任务,执行函数依次执行callbacks中的回调
myself总结:
实际上我有去看过这个$nextTick的源码,它里面的话是把这些.回调函数放到一个数组里面去,然后对浏览器做了一个兼容性的判断.好像一共四层判断吧.当浏览器支持promise的时候,这个回调函数就在promise的then(微任务)里面执行了.
否则会去判断MutationObserver和setImmediate 最后实在不行了才调用了SetTimeout(,0)(宏任务)来进行执行回调
鉴于以上的回答,可能牵连的面试:
1.事件循环机制
2.$nextTick是微任务还是宏任务
 
                    
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号