Vue.nextTick 用法原理详解

20,910次阅读
没有评论

共计 2109 个字符,预计需要花费 6 分钟才能阅读完成。

用法:

在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。

疑惑:

怎么实现的延迟回调

原理:

  1. JavaScript 语言的一大特点就是单线程,同一个时间只能做一件事
  2. JavaScript 任务可以分为两种,一种是同步任务,一种是异步任务
  3. 异步任务大致分为,宏任务,和微任务
  4. 所有同步任务都在主线程上执行,形成一个执行栈
  5. 主线程之外,还存在一个”任务队列”(task queue)。只要异步任务有了运行结果,就在”任务队列”之中放置一个事件。
  6. 一旦”执行栈”中的所有同步任务执行完毕,系统就会读取”任务队列”中的微任务,其次是宏任务,看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
  7. 主线程不断重复上面的第 6 步。

vue 实现:

vue 大多数情况下优先使用微任务,在添加事件中使用宏任务 意思是在事件中使用 Vue.nextTick 使用宏任务,其他情况下使用微任务

vue nextTick 宏任务实现

  • 优先检测 setImmediate
if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
  macroTimerFunc = () => {
    setImmediate(flushCallbacks)
  }
}

setImmediate 浏览器支持情况 Vue.nextTick 用法原理详解

  • 其次检测 MessageChannel 支持情况
else if (typeof MessageChannel !== 'undefined' && (isNative(MessageChannel) ||
  // PhantomJS
  MessageChannel.toString() === '[object MessageChannelConstructor]'
)) {const channel = new MessageChannel()
  const port = channel.port2
  channel.port1.onmessage = flushCallbacks
  macroTimerFunc = () => {port.postMessage(1)
  }
} 

MessageChannel 浏览器支持情况 Vue.nextTick 用法原理详解

  • 上面都不支持就使用最原始的 setTimeout
 else {
  /* istanbul ignore next */
  macroTimerFunc = () => {setTimeout(flushCallbacks, 0)
  }
}

vue nextTick 微任务实现

if (typeof Promise !== 'undefined' && isNative(Promise)) {
  const p = Promise.resolve()
  microTimerFunc = () => {
    p.then(flushCallbacks)
    // in problematic UIWebViews, Promise.then doesn't completely break, but
    // it can get stuck in a weird state where callbacks are pushed into the
    // microtask queue but the queue isn't being flushed, until the browser
    // needs to do some other work, e.g. handle a timer. Therefore we can
    // "force" the microtask queue to be flushed by adding an empty timer.
    if (isIOS) setTimeout(noop)
  }
}

Promise 浏览器支持情况 Vue.nextTick 用法原理详解

  • 如果不支持 Promise, 还是使用宏任务
else {
  // fallback to macro
  microTimerFunc = macroTimerFunc
}

vue 中什么地方用宏任务,什么地方用微任务?

从源码中可以看出,在 DOM 事件中使用 Vue.nextTick 默认使用宏任务,其他地方使用 Vue.nextTick 默认使用微任务。

其实从源码中注释中可以看出 Vue 最开始都是使用微任务方式,后面出现了 bug,才引入了宏任务方式

// Here we have async deferring wrappers using both microtasks and (macro) tasks.
// In 
// microtasks have too high a priority and fire in between supposedly
// sequential events (e.g. #4521, #6690) or even between bubbling of the same
// event (#6566). However, using (macro) tasks everywhere also has subtle problems
// when state is changed right before repaint (e.g. #6813, out-in transitions).
// Here we use microtask by default, but expose a way to force (macro) task when
// needed (e.g. in event handlers attached by v-on).

    正文完
     0
    Yojack
    版权声明:本篇文章由 Yojack 于1970-01-01发表,共计2109字。
    转载说明:
    1 本网站名称:优杰开发笔记
    2 本站永久网址:https://yojack.cn
    3 本网站的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,请联系站长进行删除处理。
    4 本站一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
    5 本站所有内容均可转载及分享, 但请注明出处
    6 我们始终尊重原创作者的版权,所有文章在发布时,均尽可能注明出处与作者。
    7 站长邮箱:laylwenl@gmail.com
    评论(没有评论)