effectScope 介绍(初中水平)

🎯 什么是 effectScope?

effectScopeVue 3 的一个工具,用来管理和清理响应式副作用

📖 核心概念

1. 什么是”副作用”?

在 Vue 中,副作用就是那些监听数据变化并执行操作的代码,比如:

  • watch() - 监听数据变化
  • computed() - 计算属性
  • effect() - 响应式效果

2. 为什么要管理?

如果不管理,当组件销毁时,这些监听器可能还在运行,导致:

  • 内存泄漏
  • 性能下降
  • 奇怪的 bug

🔧 基本用法

创建和使用

// 1. 创建一个作用域
const scope = effectScope()

// 2. 在作用域内运行代码
scope.run(() => {
  // 这里的所有响应式代码都会被管理
  const count = ref(0)

  // 监听 count 变化
  watch(count, (newValue) => {
    console.log('count 变成:', newValue)
  })

  // 计算 double
  const double = computed(() => count.value * 2)
})

// 3. 清理所有响应式代码
scope.stop()

📊 实际场景

场景1:组件中使用

// 在 Vue 组件中
export default {
  setup() {
    // 创建作用域
    const scope = effectScope()

    // 定义响应式数据
    const data = ref(null)
    const loading = ref(false)

    // 在作用域内设置监听
    scope.run(() => {
      // 监听数据变化
      watch(
        () => data.value,
        (newData) => {
          console.log('数据更新:', newData)
        }
      )

      // 监听加载状态
      watch(
        () => loading.value,
        (isLoading) => {
          if (isLoading) {
            console.log('开始加载...')
          } else {
            console.log('加载完成')
          }
        }
      )
    })

    // 组件销毁时清理
    onUnmounted(() => {
      scope.stop()  // 一键清理所有监听
    })

    return { data, loading }
  }
}

场景2:自定义 Hook

// 创建一个可重用的数据获取 Hook
function useFetch(url) {
  const data = ref(null)
  const error = ref(null)
  const loading = ref(false)

  // 创建作用域
  const scope = effectScope()

  scope.run(() => {
    // 当 url 变化时自动获取数据
    watch(
      () => url,
      async (newUrl) => {
        try {
          loading.value = true
          const response = await fetch(newUrl)
          data.value = await response.json()
        } catch (err) {
          error.value = err
        } finally {
          loading.value = false
        }
      },
      { immediate: true }  // 立即执行一次
    )
  })

  // 返回清理函数
  const stop = () => scope.stop()

  return { data, error, loading, stop }
}

🆚 对比:有 vs 没有 effectScope

没有 effectScope(旧方法)

// 需要手动管理每个副作用
const stopWatch1 = watch(data, () => { ... })
const stopWatch2 = watch(loading, () => { ... })
const stopComputed = effect(() => { ... })

// 清理时要一个一个清理
onUnmounted(() => {
  stopWatch1()
  stopWatch2()
  stopComputed()
  // 容易忘记清理某些
})

有 effectScope(新方法)

// 创建一个作用域
const scope = effectScope()

// 所有副作用放在一起
scope.run(() => {
  watch(data, () => { ... })
  watch(loading, () => { ... })
  effect(() => { ... })
})

// 一键清理所有
onUnmounted(() => {
  scope.stop()  // 简单、不会遗漏
})

💡 高级功能

1. 嵌套作用域

// 可以创建嵌套的作用域
const parentScope = effectScope()
const childScope = effectScope()

parentScope.run(() => {
  console.log('父作用域')

  childScope.run(() => {
    console.log('子作用域')
  })
})

// 停止父作用域时,子作用域也会被停止
parentScope.stop()

2. 独立作用域

// 创建独立的作用域(不会自动停止)
const detachedScope = effectScope(true)  // 传入 true 表示独立

detachedScope.run(() => {
  // 这个作用域需要手动停止
  // 适合全局状态管理
})

// 需要时手动停止
detachedScope.stop()

为什么需要 effectScope?

1. 防止内存泄漏

// 如果没有清理,监听器会一直存在
// 即使组件销毁了,它还在监听
// 导致内存占用越来越多

2. 代码更清晰

// 把所有相关代码放在一起
// 逻辑更清晰,易于维护

3. 避免遗漏清理

// 以前可能忘记清理某些监听器
// 现在只需要调用一次 stop()

📈 在项目中的使用

企业级项目示例

// 在大型项目中,effectScope 非常有用
export function useComplexDataFlow() {
  const scope = effectScope()

  const state = reactive({
    data: null,
    loading: false,
    error: null
  })

  // 复杂的数据流逻辑
  scope.run(() => {
    // 多个 watch 监听
    watch(() => state.data, handleDataChange)
    watch(() => state.loading, handleLoadingChange)
    watch(() => state.error, handleError)

    // 计算属性
    const dataSummary = computed(() => {
      return state.data ? `数据量: ${state.data.length}` : '无数据'
    })

    // 自动执行的副作用
    effect(() => {
      if (state.loading) {
        showLoading()
      } else {
        hideLoading()
      }
    })

    return { ...toRefs(state), dataSummary }
  })

  // 提供清理方法
  const cleanup = () => scope.stop()

  return { ...toRefs(state), cleanup }
}

🎓 关键点总结

  1. 创建effectScope() 创建一个作用域
  2. 运行scope.run() 在作用域内执行代码
  3. 收集:作用域会收集所有响应式副作用
  4. 清理scope.stop() 清理所有副作用
  5. 嵌套:支持嵌套作用域
  6. 独立:可以创建独立的作用域

📋 一句话总结

effectScope 是 Vue 3 的”副作用管理器”,它能集中管理和清理所有响应式代码,让应用更加健壮和高效。