返回

Vue3 watchEffect 源码学习

前端

前言

在看 reactive 源码的时候发现单独调用 reactive 函数并没有收集依赖,那么什么时候才会收集依赖呢,我把之前的 demo 改了改,加入了 watchEffect 之后发现 watchEffect 在调用的时候会触发 get 的时候把当前的 effect 函数收集起来,而当数据被修改的时候会遍历这个 effect 数组,分别执行这些 effect,这样就能实现响应式的功能了,具体实现原理我来带大家看一下源码吧。

watchEffect 源码分析

1. 准备工作

首先,我们需要创建一个 watchEffect 函数,这个函数将被用来跟踪依赖项并更新视图。

function watchEffect(callback) {
  // 创建一个 effect 实例
  const effect = createEffect(callback)

  // 立即执行 effect
  effect()

  // 返回一个 cleanup 函数,用于取消 effect
  return effect.stop
}

2. createEffect 函数

createEffect 函数是 Vue3 中用于创建 effect 的函数。它接受一个 callback 函数作为参数,该函数将在每次数据发生变化时被调用。

function createEffect(callback) {
  // 创建一个 effect 对象
  const effect = {
    // effect 函数
    callback,

    // 是否正在运行
    active: true,

    // 收集的依赖项
    deps: [],

    // 停止 effect 的函数
    stop() {
      // 将 active 设为 false,以停止 effect
      effect.active = false

      // 从所有依赖项中移除 effect
      for (const dep of effect.deps) {
        dep.delete(effect)
      }
    }
  }

  // 将 effect 添加到全局 effect 数组中
  globalEffects.add(effect)

  // 返回 effect
  return effect
}

3. get 函数

get 函数是 Vue3 中用于获取响应式数据的函数。它接受一个对象和一个属性名称作为参数,并返回该属性的值。

function get(target, key) {
  // 获取对象的原始值
  const value = Reflect.get(target, key)

  // 如果对象的属性是响应式的,则收集依赖项
  if (isReactive(value)) {
    // 从 effect 数组中取出最后一个 effect
    const effect = globalEffects.last()

    // 将 effect 添加到属性的依赖项数组中
    value.deps.add(effect)

    // 将属性添加到 effect 的依赖项数组中
    effect.deps.add(value)
  }

  // 返回对象的原始值
  return value
}

4. set 函数

set 函数是 Vue3 中用于设置响应式数据的函数。它接受一个对象、一个属性名称和一个新值作为参数,并设置该属性的新值。

function set(target, key, value) {
  // 设置对象的原始值
  const oldValue = Reflect.get(target, key)
  Reflect.set(target, key, value)

  // 如果对象的属性是响应式的,则触发依赖项
  if (isReactive(oldValue)) {
    // 从 effect 数组中取出最后一个 effect
    const effect = globalEffects.last()

    // 触发 effect 的依赖项
    effect.deps.forEach(dep => dep.run())
  }
}

5. 使用 watchEffect

现在,我们已经了解了 watchEffect 的源码,就可以开始使用它了。

const app = Vue.createApp({
  data() {
    return {
      count: 0
    }
  },

  watch: {
    count(newValue, oldValue) {
      console.log(`count changed from ${oldValue} to ${newValue}`)
    }
  }
})

app.mount('#app')

这段代码创建一个 Vue 实例,并使用 watchEffect 来跟踪 count 属性的变化。每次 count 属性发生变化时,都会调用 watchEffect 的 callback 函数,并打印出 count 属性的新值和旧值。

总结

以上就是 Vue3 watchEffect 源码的实现原理。通过这篇文章,我们了解了 watchEffect 是如何收集和追踪依赖,以及如何实现响应式更新的。同时,我们还学习了一些有关如何使用 watchEffect 的建议,希望这些建议能够帮助您在开发中更好地利用 watchEffect。