nextTick的使用背景
在vue项目中,经常会使用到nextTick这个api,一直在猜想其是怎么实现的,今天有幸研读了下,虽然源码又些许问题,但仍值得借鉴
核心源码解析
判断当前环境使用最合适的API并保存函数
promise
判断是否支持promise,如果支持就使用Promise对象的then方法包裹要执行的 flushCallbacks函数
MutationObserver
判断是否支持MutationObserver,如果支持就创建一个MutationObserver 用于监听dom改动之后执行 flushCallbacks 函数 并赋值给 observer
setImmediate
判断是否支持setImmediate,如果支持就使用setImmediate包裹 flushCallbacks函数
setTimeout
如果以上三种都不支持使用setTimeout包裹 flushCallbacks函数
export let isUsingMicroTask = false
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] ( )
}
}
let timerFunc
if ( typeof Promise !== 'undefined' && isNative ( 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]'
) ) {
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) ) {
timerFunc = ( ) => {
setImmediate ( flushCallbacks)
}
} else {
timerFunc = ( ) => {
setTimeout ( flushCallbacks, 0 )
}
}
调用异步函数执行回调对列
入参分析
nextTick(cb?: Function, ctx?: Object) {
}
cb是 传入的回调函数 ctx是函数执行的上下文 而两者又都是可选参数, 有所困惑 下文有解析
函数执行逻辑
将调用nextTick是传入的执行函数添加到 callbacks中 可使用call和传入的ctx修改传入的执行函数的this指向
export function nextTick ( cb? : Function, ctx? : Object ) {
let _resolve
callbacks. push ( ( ) => {
if ( cb) {
try {
cb . call ( ctx)
} catch ( e) {
handleError ( e, ctx, 'nextTick' )
}
console. log ( "00000" )
} else if ( _resolve) {
console. log ( 'ctx' )
_resolve ( ctx)
}
} )
console. log ( "9999" )
if ( ! pending) {
pending = true
timerFunc ( )
}
if ( ! cb && typeof Promise !== 'undefined' ) {
console. log ( "111" )
return new Promise ( resolve => {
_resolve = resolve
} )
}
}
这个代码有删减,因为其余代码执行 有所困惑 下文有解析
代码分析
nexttick的参数中cb不能为可选参数,如果cb参数不传将没有回调函数,nextTick将没有意义,并且ctx将成为第一个参数,由于是形参,ctx将顶替cb,此时ctx相当于没有了,resolve出去的将是一个undefined
代码实测
this . $nextTick ( )
结果很意外,得到的是当前组件的实例 实在没看明白,这个ctx 组件的实例 是怎么出现在这个代码中的
依次执行nextTick
function flushCallbacks ( ) {
pending = false
const copies = callbacks. slice ( 0 )
callbacks. length = 0
for ( let i = 0 ; i < copies. length; i++ ) {
copies[ i] ( )
}
}
源码
import { noop } from 'shared/util'
import { handleError } from './error'
import { isIE, isIOS, isNative } from './env'
export let isUsingMicroTask = false
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] ( )
}
}
let timerFunc
if ( typeof Promise !== 'undefined' && isNative ( 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]'
) ) {
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) ) {
timerFunc = ( ) => {
setImmediate ( flushCallbacks)
}
} else {
timerFunc = ( ) => {
setTimeout ( flushCallbacks, 0 )
}
}
export function nextTick ( cb? : Function, ctx? : Object ) {
let _resolve
callbacks. push ( ( ) => {
if ( cb) {
try {
cb . call ( ctx)
} catch ( e) {
handleError ( e, ctx, 'nextTick' )
}
console. log ( "00000" )
} else if ( _resolve) {
console. log ( 'ctx' )
_resolve ( ctx)
}
} )
console. log ( "9999" )
if ( ! pending) {
pending = true
timerFunc ( )
}
if ( ! cb && typeof Promise !== 'undefined' ) {
console. log ( "111" )
return new Promise ( resolve => {
_resolve = resolve
} )
}
}
个人困惑
本人实在是不太理解在没有传入参数时,ctx是怎么拿到的,并且返回resolve有何作用
_resolve ( ctx)
致谢
感谢您百忙之中抽时间阅读我写的博客,谢谢你的肯定,也希望对您能有所帮助 如果您有更好的见解请在评论区留言或者私聊我,期待与您的交流