nextTick的使用背景
在vue项目中,经常会使用到nextTick这个api,一直在猜想其是怎么实现的,今天有幸研读了下,虽然源码又些许问题,但仍值得借鉴
核心源码解析
判断当前环境使用最合适的API并保存函数
promise
判断是否支持promise,如果支持就使用Promise对象的then方法包裹要执行的 flushCallbacks函数
MutationObserver
判断是否支持MutationObserver,如果支持就创建一个MutationObserver 用于监听dom改动之后执行 flushCallbacks 函数 并赋值给 observer
setImmediate
判断是否支持setImmediate,如果支持就使用setImmediate包裹 flushCallbacks函数
setTimeout
如果以上三种都不支持使用setImmediate包裹 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' )
}
}
} )
if ( ! pending) {
pending = true
timerFunc ( )
}
}
这个代码有删减,因为其余代码不会执行是 伪代码 下文有解析
伪代码分析
nexttick的参数中cb不能为可选参数,如果cb参数不传将没有回调函数,nextTick将没有意义,并且ctx将成为第一个参数,由于是形参,ctx将顶替cb但是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' )
}
} else if ( _resolve) {
console. log ( 'ctx' )
_resolve ( ctx)
}
} )
if ( ! pending) {
pending = true
timerFunc ( )
}
if ( ! cb && typeof Promise !== 'undefined' ) {
return new Promise ( resolve => {
_resolve = resolve
} )
}
}
个人困惑
本人实在是不太理解下列代码的意义是什么,感觉是没用的,但是不太确定,以下是个人分析
分析
首先将执行上下文this抛出是没什么意义的 其次promise的resolve 单独拿出来在此处有什么作用呢
let _resolve
if ( ! cb && typeof Promise !== 'undefined' ) {
return new Promise ( resolve => {
_resolve = resolve
} )
}
_resolve ( ctx)
致谢
感谢您百忙之中抽时间阅读我写的博客,谢谢你的肯定,也希望对您能有所帮助 如果您有更好的见解请在评论区留言或者私聊我,期待与您的交流