Custom responsive effect
Basic Concepts of Reactive Effects
One of the core components of Vue.js's reactivity system is the effect
, which is responsible for tracking dependencies and re-executing when data changes. An effect
is essentially a side-effect function that automatically re-runs when the reactive data it internally accesses changes.
import { reactive, effect } from 'vue'
const state = reactive({
count: 0
})
effect(() => {
console.log('count changed:', state.count)
})
state.count++ // Output: count changed: 1
Creating Custom Effects
Vue provides the effect
API to allow us to create custom reactive side effects. The basic usage is to pass in a function directly:
const stop = effect(() => {
// Side-effect logic
document.title = `Count: ${state.count}`
})
// Stop the effect
stop()
The effect
function returns a stop function, which can be called to cancel the effect's reactive tracking.
Effect Scheduler
We can control the execution timing of an effect using the scheduler
option:
effect(
() => {
console.log('count changed:', state.count)
},
{
scheduler(effect) {
// Execute during the next microtask
queueMicrotask(effect)
}
}
)
This is useful for implementing batch updates or asynchronous rendering.
Lazy Execution of Effects
Using the lazy
option, we can create an effect without immediately executing it:
const lazyEffect = effect(
() => {
console.log('Lazy effect ran')
},
{ lazy: true }
)
// Manually execute
lazyEffect()
Nested Effects
Effects can be nested, and Vue will automatically establish the correct dependency relationships:
effect(() => {
console.log('Outer effect')
effect(() => {
console.log('Inner effect', state.count)
})
})
Custom Dependency Tracking
We can manually control dependency tracking using track
and trigger
:
import { track, trigger } from 'vue'
const obj = { value: 1 }
effect(() => {
track(obj, 'value') // Manual tracking
console.log(obj.value)
})
// Manually trigger
trigger(obj, 'value')
Effect Scope
Vue 3.2+ introduced the effectScope
API for managing a group of effects:
import { effectScope } from 'vue'
const scope = effectScope()
scope.run(() => {
effect(() => {
console.log(state.count)
})
})
// Stop all effects within the scope
scope.stop()
Custom Reactive Effect Practice
Combining the above features, we can implement a custom reactive 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]')
// Stop logging
logger.stop()
Performance Optimization Considerations
For frequently changing reactive data, we can optimize effect performance in the following ways:
effect(() => {
// Use computed to reduce unnecessary recalculations
const double = computed(() => state.count * 2)
console.log(double.value)
}, {
// Set flush timing
flush: 'post'
})
Comparison with watch
Both effect
and watch
are reactive APIs but serve different use cases:
// effect is suitable for side effects
effect(() => {
document.title = state.title
})
// watch is suitable for observing specific value changes
watch(
() => state.count,
(newVal, oldVal) => {
console.log('count changed', oldVal, '->', newVal)
}
)
Error Handling
Error handling can be added to effects:
effect(
() => {
throw new Error('Effect error')
},
{
onError(err) {
console.error('Effect error:', err)
}
}
)
Advanced Patterns for Reactive Effects
Implementing an effect that automatically cleans up resources:
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)
})
Reactive Effects and Component Lifecycle
When using effects in components, lifecycle management is important:
import { onUnmounted } from 'vue'
export default {
setup() {
const stop = effect(() => {
// Component logic
})
onUnmounted(() => {
stop()
})
}
}
Custom Reactive Store
A simple reactive store can be implemented based on effects:
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
}
}
Debugging Reactive Effects
Vue provides debugging tools for effects:
effect(
() => {
debugger // Can set breakpoints here
console.log(state.count)
},
{
onTrack(e) {
console.log('tracking', e)
},
onTrigger(e) {
console.log('triggering', e)
}
}
)
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
下一篇:响应式与渲染器协同