Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

挖一挖 Vue nextTick "黑历史" #1

Open
pengVc opened this issue Jul 16, 2019 · 0 comments
Open

挖一挖 Vue nextTick "黑历史" #1

pengVc opened this issue Jul 16, 2019 · 0 comments

Comments

@pengVc
Copy link
Owner

pengVc commented Jul 16, 2019

写在前面的话, 或许本文的标题有过, 但并不是意在抹黑、贬低之意, 谁没有点过去呢, vue 也在实现 nextTick 走过的"弯"路.
本文重在陈述 nextTick 每个阶段重要实现的阶段.

Preface

  • 求真之路
  • nextTick 的迭代阶段
    • v2.4 专一: Microtask 全盛时期
    • v2.5 革命: Macrotask vs Microtask
    • v2.6 统一: Microtask 复辟时期
  • 总结

求真之旅

在查阅求真 Vue nextTick 背后机制相关博文资料之时, 总会出现一些相佐的意见:

  • Vue 还在使用 Microtask 吗?
  • Vue 为何放弃使用 MutationObserver ?
  • Vue 为何采用 Marcotask ?
  • setImmediate 是最佳的队列控制吗, MessageChannel 在 Event Loop 处于什么阶段?

如果再不去了解源码改动历史, 是无法跳出这个疑问阶层.


nextTick 的迭代阶段

v2.4 专一

在 Vue v2.4 及之前很长一段时期里, nextTick 背后实现机制是 Microtask .

  • Microtask 全盛时期
  • 优雅降级方案: Promise -> MutationObserver -> setTimeout
  • 读v2.4.4 源码

v2.5 "两次"革命

  • 第一次革命: 2.5.0 ~ 2.5.1 开头两个版本
    • MacroTask 崛起时期

    • 使用全新的降级方案: setImmediate -> MessageChannel -> Promise -> setTimeout

    • 革命原因: Vue 源码说明非常透彻, 在之前的 2.4, 使用 Microtask 机制, 但是 Microtask 优先级过高, 它可能触发在: 顺序发生的事件、甚至是同一事件源冒泡监听回调之间

    • 至于 Vue 当时为什么选择 setImmediate 为优先, 或许可以从 Node.js 官网中 Event Loop 介绍中 得到一些佐证.

      • 笔记: 异步 I/O 事件循环
      • setImmediate 被执行的阶段, 是在一次 Event Loop 靠后的阶段 ( pollcheck Phases ).
    • 读v2.5.1 源码


  • 第二次革命: 2.5.2 ~ 2.5.final
    • Macrotask/Microtask 并举期( MacroTask 补丁时期 )

    • 两类降级方案:

      • setImmediate -> MessageChannel > setTimeout
      • Promise -> macroTimerFunc
    • 内部实现的特征

      • 出现 microTimerFuncmacroTimerFunc , 默认使用 microTimerFunc
      • 申明 useMacroTask 作为标记, 用于判断该使用何种任务
      • 暴露 withMacroTask API, 用于在一些特别场景下强制使用 macroTask
      • 还有一个变化: nextTick 的实现, 由 core/util/env 拆出为 next-tick 独立模块实现
      • 革命原因: Macrotask 全盘接手的控制会导致一些难以察觉( subtle )的问题
      • 读v2.5.2 源码

v2.6 统一

  • Microtask 复辟时期
  • 降级方案: Promise -> MutationObserver -> setImmediate -> setTimeout
  • 在权衡了 Marcotask 与 Microtask 都不是银弹方式, 做出了取舍:
    - 忍受 microtasks 高优先级带来某些场景下优先执行
    - 这些场景可在业务中用一些 变通方式处理
  • 内部实现的特征
    • 暴露一个只读变量 isUsingMicrotask
  • 读v2.6.0 源码

总结

  • 没有任何一种方案是银弹
  • Vue 在 v2.4 及之前 以及 2.6 及之后, 确实使用的是 Microtask 队列控制机制.
  • v2.5 采用 Macrotask 的原因: Microtask 在某些场景下存在较高执行优先级.
  • v2.6 重拾 Microtask 的原因: 做出了取舍

further:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant