副作用与作用域
Vitarx 的响应式系统围绕副作用(Effect)和作用域(EffectScope)构建。副作用是响应式数据变化时自动执行的逻辑,作用域则用于批量管理副作用的生命周期。理解这两个概念,有助于你更好地控制响应式系统的行为。
Effect 基类
Effect 是所有副作用的抽象基类,定义了三种状态:
| 状态 | 说明 |
|---|---|
active | 活跃——正常工作 |
paused | 暂停——不响应数据变化 |
disposed | 已销毁——永久停止 |
每个 Effect 实例提供以下方法:
dispose()— 销毁副作用,释放资源pause()— 暂停副作用resume()— 恢复副作用
以及对应的属性和钩子:
state— 当前状态isActive/isPaused/isDisposed— 状态判断
watch() 返回的 Watcher、computed() 返回的 Computed 都是 Effect 的子类,都拥有上述能力。
EffectScope 作用域
EffectScope 是副作用作用域,用于批量管理一组 Effect 的生命周期。当作用域被销毁时,其内部的所有副作用也会一起被销毁。
createScope()
createScope() 创建一个新的作用域实例:
import { createScope, ref, watchEffect } from 'vitarx'
const scope = createScope({ name: 'my-scope' })
// 在作用域上下文中执行代码
scope.run(() => {
const count = ref(0)
// 此副作用会自动加入当前作用域
watchEffect(() => {
console.log('count:', count.value)
})
})scope.run(fn)
run() 在作用域上下文中执行函数。函数内创建的副作用(如 watchEffect、computed)会自动加入该作用域:
import { createScope, ref, watchEffect, computed } from 'vitarx'
const scope = createScope()
scope.run(() => {
const count = ref(0)
// 这些副作用会自动加入 scope
watchEffect(() => {
console.log('count:', count.value)
})
const double = computed(() => count.value * 2)
})scope.dispose()
dispose() 销毁作用域及其内部所有副作用:
import { createScope, ref, watchEffect } from 'vitarx'
const scope = createScope()
scope.run(() => {
const count = ref(0)
watchEffect(() => {
console.log('count:', count.value)
})
})
// 销毁作用域——内部所有副作用一起停止
scope.dispose()scope.pause() 和 scope.resume()
pause() 暂停作用域内所有副作用,resume() 恢复:
import { createScope, ref, watchEffect } from 'vitarx'
const scope = createScope()
scope.run(() => {
const count = ref(0)
watchEffect(() => {
console.log('count:', count.value)
})
})
// 暂停——所有副作用停止响应
scope.pause()
// 恢复——所有副作用重新工作
scope.resume()生命周期回调
EffectScope 提供了三个生命周期回调注册方法:
onScopeDispose()
作用域被销毁时执行回调:
import { createScope, onScopeDispose } from 'vitarx'
const scope = createScope()
scope.run(() => {
onScopeDispose(() => {
console.log('作用域被销毁了')
})
})
scope.dispose() // 输出: "作用域被销毁了"onScopePause()
作用域被暂停时执行回调:
import { createScope, onScopePause } from 'vitarx'
const scope = createScope()
scope.run(() => {
onScopePause(() => {
console.log('作用域被暂停了')
})
})
scope.pause() // 输出: "作用域被暂停了"onScopeResume()
作用域被恢复时执行回调:
import { createScope, onScopeResume } from 'vitarx'
const scope = createScope()
scope.run(() => {
onScopeResume(() => {
console.log('作用域被恢复了')
})
})
scope.pause()
scope.resume() // 输出: "作用域被恢复了"WARNING
onScopeDispose、onScopePause、onScopeResume 必须在作用域上下文中调用(即在 scope.run() 内部)。如果在没有活跃作用域时调用,会在控制台输出警告。可以传入第二个参数 true 来静默失败。
getActiveScope()
getActiveScope() 获取当前活跃的作用域:
import { createScope, getActiveScope } from 'vitarx'
const scope = createScope()
scope.run(() => {
const active = getActiveScope()
console.log(active === scope) // true
})
// 在作用域外部调用——返回 undefined
console.log(getActiveScope()) // undefinedviewEffect()
viewEffect() 是视图级副作用函数,主要用于框架内部的视图运行时(如 DOM 更新、指令副作用等)。它的特点是支持暂停和恢复——当组件不可见时暂停,可见时恢复。
import { ref, viewEffect } from 'vitarx'
const count = ref(0)
// 创建视图级副作用
const effect = viewEffect(() => {
console.log('DOM 更新相关逻辑, count:', count.value)
})
if (effect) {
// 暂停——组件不可见时
effect.pause()
// 恢复——组件可见时
effect.resume()
// 销毁——组件卸载时
effect.dispose()
}WARNING
viewEffect() 主要服务于框架内部的视图运行时。业务代码通常应该使用 watchEffect() 代替。使用 viewEffect() 时需要在合适的时机主动调用 dispose(),否则可能造成内存泄漏。
viewEffect 的返回值
- 如果副作用函数内有信号依赖,返回一个包含
pause()、resume()、dispose()方法的控制对象 - 如果没有信号依赖,返回
null
作用域与组件
在 Vitarx 中,每个组件实例都会创建一个 EffectScope。组件内的 watchEffect、computed 等副作用会自动加入组件的作用域。当组件卸载时,作用域被销毁,所有副作用自动清理。
这意味着你通常不需要手动管理作用域——框架已经帮你处理好了。但在以下场景中,手动使用作用域会很有用:
- 在组件外部创建响应式逻辑(如全局状态管理)
- 需要批量暂停/恢复一组副作用
- 需要在特定时机统一释放资源
完整示例
下面是一个综合使用 EffectScope 相关 API 的示例:
import {
createScope,
onScopeDispose,
onScopePause,
onScopeResume,
ref,
watchEffect,
computed,
createApp
} from 'vitarx'
function App() {
// 创建独立作用域
const scope = createScope({ name: 'counter-scope' })
const count = ref(0)
const double = computed(() => count.value * 2)
// 在作用域中执行
scope.run(() => {
// 注册生命周期回调
onScopeDispose(() => {
console.log('计数器作用域已销毁')
})
onScopePause(() => {
console.log('计数器作用域已暂停')
})
onScopeResume(() => {
console.log('计数器作用域已恢复')
})
// 此副作用自动加入作用域
watchEffect(() => {
console.log('count 变化了:', count.value)
})
})
const increment = () => count.value++
const handlePause = () => scope.pause()
const handleResume = () => scope.resume()
const handleDispose = () => scope.dispose()
return (
<div style={{ padding: '20px' }}>
<h2>副作用与作用域示例</h2>
<div style={{ marginTop: '16px' }}>
<p>计数: {count}</p>
<p>双倍: {double}</p>
<p>作用域状态: {scope.state}</p>
<p>副作用数量: {scope.count}</p>
</div>
<div style={{ marginTop: '16px', display: 'flex', gap: '8px' }}>
<button onClick={increment}>+1</button>
<button onClick={handlePause}>暂停作用域</button>
<button onClick={handleResume}>恢复作用域</button>
<button onClick={handleDispose}>销毁作用域</button>
</div>
</div>
)
}
createApp(App).mount('#app')