Vue.js 的 Watch 为什么不需要手动回收?

如下面一个简单的例子

<script setup lang="ts">
import { watch, ref } from "vue";
const count = ref(0);
watch(count, (newCount) => {
  console.log("new count", newCount);
});
</script>

你有没有想过,为什么 watch 不需要像 React Effect hook 一样 return cleanup 函数?
你有没有注意过 Vue 的 watch 其实是 return 了 cleanup (但是却不用你手动调用的)?
好神奇,怎么做到的?

查阅源码,关键代码如下,非常的简单:

function doWatch() {

  ...

  const effect = new ReactiveEffect(getter, NOOP, scheduler)

  const scope = getCurrentScope()
  const unwatch = () => {
    effect.stop()
    if (scope) {
      remove(scope.effects, effect)
    }
  }

  ...

  return unwatch
}

魔法在于 const scope = getCurrentScope()

getCurrentScope 取到的是 Vue setup 函数调用时的 EffectScope。

所以像下面这段代码,肯定是不能够按预期执行的:

onMounted(async () => {
  await delay(300); // 延迟300ms
  watch(domRef, () => {
    // balabala
  });
});