手动停止响应的方法
手动停止响应的方法
Vue3的响应式系统基于Proxy实现,依赖收集和触发更新的过程自动化程度高。但在某些场景下需要手动控制响应式行为,比如性能优化或特殊逻辑处理。
停止响应式的基本原理
每个响应式对象内部维护一个依赖映射表(targetMap
),通过effect
函数创建的副作用会被收集到该表中。手动停止响应本质上是清除这些依赖关系:
const state = reactive({ count: 0 })
// 创建effect
const stopHandle = effect(() => {
console.log(state.count)
})
// 停止响应
stopHandle.effect.stop()
通过effect返回值停止
effect
函数返回一个Runner
对象,该对象的effect
属性包含stop
方法:
const runner = effect(() => {
// 副作用逻辑
})
// 停止该effect的所有响应
runner.effect.stop()
停止后,该副作用不再响应依赖的变化:
const obj = reactive({ a: 1 })
let dummy
const runner = effect(() => {
dummy = obj.a
})
obj.a = 2 // 触发更新
runner.effect.stop()
obj.a = 3 // 不再触发更新
console.log(dummy) // 仍输出2
自定义stop逻辑
可以在effect中添加onStop
回调实现自定义清理逻辑:
const runner = effect(() => {
/* ... */
}, {
onStop() {
console.log('effect已停止')
// 执行资源释放等操作
}
})
组件级别的停止响应
在setup函数中使用stop
函数停止整个组件的响应式:
import { reactive, stop } from 'vue'
export default {
setup() {
const state = reactive({ /* ... */ })
onUnmounted(() => {
stop(state) // 组件卸载时停止响应
})
return { state }
}
}
深度停止响应式
对于嵌套对象,需要递归停止响应:
function deepStop(reactiveObj) {
if (!reactiveObj || !reactiveObj.__v_reactive) return
stop(reactiveObj)
for (const key in reactiveObj) {
const val = reactiveObj[key]
if (val && val.__v_reactive) {
deepStop(val)
}
}
}
const nestedObj = reactive({
a: 1,
b: { c: 2 }
})
deepStop(nestedObj) // 停止整个嵌套结构的响应
响应式标记与停止
Vue3内部使用__v_skip
标记跳过响应式处理:
const obj = {}
Object.defineProperty(obj, '__v_skip', {
get() { return true }
})
const proxy = reactive(obj) // 不会创建响应式代理
与watch的结合使用
手动停止watch
监听:
const stopHandle = watch(
() => state.count,
(newVal) => {
console.log('count changed:', newVal)
}
)
// 停止监听
stopHandle()
响应式系统的内部实现
在源码层面,停止响应主要涉及ReactiveEffect
类的实现:
class ReactiveEffect {
active = true
deps: Dep[] = []
constructor(
public fn: () => T,
public scheduler?: () => void,
public onStop?: () => void
) {}
stop() {
if (this.active) {
cleanupEffect(this)
this.onStop?.()
this.active = false
}
}
}
function cleanupEffect(effect: ReactiveEffect) {
effect.deps.forEach(dep => {
dep.delete(effect)
})
effect.deps.length = 0
}
性能优化场景
在大型列表渲染时,可以临时停止非可见区域的响应:
const list = reactive([/* 大量数据 */])
let activeRange = [0, 50]
effect(() => {
// 只响应可见区域数据变化
const visibleData = list.slice(...activeRange)
renderList(visibleData)
})
// 滚动时更新可见区域
function handleScroll() {
const newRange = calcVisibleRange()
if (newRange[0] !== activeRange[0] || newRange[1] !== activeRange[1]) {
// 先停止旧effect
runner.effect.stop()
// 更新范围后重新创建effect
activeRange = newRange
runner = effect(/* 新effect */)
}
}
与toRefs的特殊情况
toRefs
转换后的ref需要单独停止:
const state = reactive({ x: 1, y: 2 })
const refs = toRefs(state)
// 停止原始响应式对象
stop(state)
// refs仍然保持响应
refs.x.value = 3 // 仍然有效
// 需要单独停止每个ref
Object.values(refs).forEach(ref => {
if (ref.effect) ref.effect.stop()
})
响应式系统的恢复机制
已停止的响应式对象可以通过重新创建effect恢复:
const obj = reactive({ data: null })
let runner
function startObserving() {
runner = effect(() => {
console.log('Data changed:', obj.data)
})
}
function stopObserving() {
runner.effect.stop()
}
// 可以重复停止和启动
startObserving()
obj.data = 1 // 输出日志
stopObserving()
obj.data = 2 // 无输出
startObserving()
obj.data = 3 // 再次输出日志
源码中的stop相关API
Vue3暴露的完整停止API包括:
effect
返回的runner的stop
方法- 全局
stop
函数 watch
/watchEffect
返回的停止函数computed
返回的计算属性的effect
属性
// 全局stop函数声明
declare function stop<T>(effect: ReactiveEffect<T>): void
响应式停止的边界情况
处理边缘案例时的注意事项:
- 已停止的effect再次调用stop不会报错
- 停止后runner仍然可以手动执行
- 停止操作不会清除已有值
const obj = reactive({ val: 'initial' })
const runner = effect(() => obj.val)
runner.effect.stop()
obj.val = 'changed' // 不触发effect
// runner仍可手动执行
runner() // 输出'changed'
// 重复停止无副作用
runner.effect.stop()
runner.effect.stop()
响应式停止与内存管理
长时间运行的应用需要注意内存泄漏问题:
const globalState = reactive({ /* ... */ })
// 错误的做法:未清理的effect会持续占用内存
effect(() => {
localStorage.setItem('cache', JSON.stringify(globalState))
})
// 正确的做法:提供清理接口
let cacheEffect
function enableCache() {
cacheEffect = effect(/* ... */)
}
function disableCache() {
cacheEffect?.effect.stop()
}
与SSR的特殊处理
服务端渲染时需要特别注意响应式停止:
let serverSideEffect
if (!import.meta.env.SSR) {
// 仅在客户端执行
serverSideEffect = effect(() => {
// 浏览器特定逻辑
})
} else {
// 服务端模拟停止状态
const mockEffect = { stop: () => {} }
serverSideEffect = () => mockEffect
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:响应式系统的性能优化手段
下一篇:响应式系统边界情况处理