自定义响应式effect
响应式effect的基本概念
Vue.js的响应式系统核心之一是effect
,它负责追踪依赖并在数据变化时重新执行。effect
本质上是一个副作用函数,当它内部访问的响应式数据发生变化时,该函数会自动重新运行。
import { reactive, effect } from 'vue'
const state = reactive({
count: 0
})
effect(() => {
console.log('count changed:', state.count)
})
state.count++ // 输出: count changed: 1
创建自定义effect
Vue提供了effect
API让我们可以创建自定义的响应式副作用。基础用法是直接传入一个函数:
const stop = effect(() => {
// 副作用逻辑
document.title = `Count: ${state.count}`
})
// 停止effect
stop()
effect函数返回一个停止函数,调用它可以取消该effect的响应式追踪。
effect的调度器
我们可以通过scheduler
选项控制effect的执行时机:
effect(
() => {
console.log('count changed:', state.count)
},
{
scheduler(effect) {
// 在下一次微任务时执行
queueMicrotask(effect)
}
}
)
这在实现批量更新或异步渲染时非常有用。
effect的懒执行
通过lazy
选项可以创建但不立即执行的effect:
const lazyEffect = effect(
() => {
console.log('Lazy effect ran')
},
{ lazy: true }
)
// 手动执行
lazyEffect()
嵌套effect
effect可以嵌套使用,Vue会自动建立正确的依赖关系:
effect(() => {
console.log('Outer effect')
effect(() => {
console.log('Inner effect', state.count)
})
})
自定义依赖收集
我们可以通过track
和trigger
手动控制依赖收集:
import { track, trigger } from 'vue'
const obj = { value: 1 }
effect(() => {
track(obj, 'value') // 手动追踪
console.log(obj.value)
})
// 手动触发
trigger(obj, 'value')
effect作用域
Vue 3.2+引入了effectScope
API,用于管理一组effect:
import { effectScope } from 'vue'
const scope = effectScope()
scope.run(() => {
effect(() => {
console.log(state.count)
})
})
// 停止所有scope内的effect
scope.stop()
自定义响应式effect实践
结合以上特性,我们可以实现一个自定义的响应式logger:
function createLoggerEffect(source, prefix = '') {
const stop = effect(
() => {
console.log(prefix, JSON.stringify(source))
},
{
scheduler(effect) {
requestAnimationFrame(effect)
}
}
)
return {
stop
}
}
const logger = createLoggerEffect(state, '[State]')
// 停止日志记录
logger.stop()
性能优化考虑
对于高频变化的响应式数据,可以通过以下方式优化effect性能:
effect(() => {
// 使用computed减少不必要的重新计算
const double = computed(() => state.count * 2)
console.log(double.value)
}, {
// 设置flush时机
flush: 'post'
})
与watch的对比
effect
和watch
都是响应式API,但有不同的使用场景:
// effect适合副作用操作
effect(() => {
document.title = state.title
})
// watch适合观察特定值的变化
watch(
() => state.count,
(newVal, oldVal) => {
console.log('count changed', oldVal, '->', newVal)
}
)
错误处理
可以为effect添加错误处理:
effect(
() => {
throw new Error('Effect error')
},
{
onError(err) {
console.error('Effect error:', err)
}
}
)
响应式effect的高级模式
实现一个自动清理资源的effect:
function useAutoCleanupEffect(fn) {
let cleanup
effect((onCleanup) => {
if (cleanup) cleanup()
cleanup = onCleanup(() => {
console.log('Cleaning up...')
})
return fn()
})
}
useAutoCleanupEffect(() => {
const timer = setInterval(() => {
console.log('Tick')
}, 1000)
return () => clearInterval(timer)
})
响应式effect与组件生命周期
在组件中使用effect时,需要注意生命周期管理:
import { onUnmounted } from 'vue'
export default {
setup() {
const stop = effect(() => {
// 组件逻辑
})
onUnmounted(() => {
stop()
})
}
}
自定义响应式存储
基于effect可以实现简单的响应式存储:
function createStore(initialState) {
const state = reactive(initialState)
const subscribers = new Set()
function subscribe(fn) {
const effectFn = effect(fn, { lazy: true })
subscribers.add(effectFn)
effectFn()
return () => {
subscribers.delete(effectFn)
}
}
function notify() {
subscribers.forEach(effect => effect())
}
return {
state,
subscribe,
notify
}
}
响应式effect的调试
Vue提供了effect调试工具:
effect(
() => {
debugger // 可以在这里打断点
console.log(state.count)
},
{
onTrack(e) {
console.log('tracking', e)
},
onTrigger(e) {
console.log('triggering', e)
}
}
)
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
下一篇:响应式与渲染器协同