effectScope 介绍(初中水平)
🎯 什么是 effectScope?
effectScope 是 Vue 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 }
}
🎓 关键点总结
- 创建:
effectScope()创建一个作用域 - 运行:
scope.run()在作用域内执行代码 - 收集:作用域会收集所有响应式副作用
- 清理:
scope.stop()清理所有副作用 - 嵌套:支持嵌套作用域
- 独立:可以创建独立的作用域
📋 一句话总结
effectScope 是 Vue 3 的”副作用管理器”,它能集中管理和清理所有响应式代码,让应用更加健壮和高效。
