Manage Many Watchers Together
effectScope — Group & Dispose Effects
effectScope groups computed and watchers so you can dispose them all at once — the building block libraries like Pinia and VueUse use internally.
What you'll learn
- Create an effectScope
- Run watchers inside it
- Dispose with scope.stop()
Vue automatically cleans up watchers and computed values declared inside a component when the component unmounts. But what about effects created outside any component — in a global store, a singleton, or a long-lived composable? effectScope is the answer.
Creating a Scope
import { effectScope, ref, watch, computed } from 'vue'
const scope = effectScope()
scope.run(() => {
const count = ref(0)
const doubled = computed(() => count.value * 2)
watch(count, (v) => console.log('count is now', v))
setInterval(() => count.value++, 1000)
})
// Later — dispose everything in one call
scope.stop() scope.stop() halts every watch, watchEffect, and computed created inside scope.run().
Detached Scopes
By default a scope inherits the active parent scope. Pass true to detach — useful when you want a scope that survives its parent.
const detached = effectScope(true)
detached.run(() => { /* lives independent of parent */ }) In a Composable
A composable that owns many effects can return a stop function for callers.
import { effectScope } from 'vue'
export function useFeed() {
const scope = effectScope()
scope.run(() => {
watch(/* ... */)
watch(/* ... */)
})
return { stop: () => scope.stop() }
} Where You’ll See This
You usually don’t reach for effectScope directly — Pinia uses it to dispose store effects on HMR, and VueUse uses it to give composables stop() controls. Knowing it exists helps when you read library source.