Vue.js 3 runtime core library providing foundational APIs for building custom renderers and managing reactive component systems
—
Vue's watch system enables reactive observation of data changes with flexible scheduling and deep/shallow watching options. Effects provide low-level reactive behavior for custom reactivity patterns.
Watch reactive data sources and execute callbacks when they change.
/**
* Watches a single reactive source
* @param source - Reactive source to watch
* @param callback - Callback executed when source changes
* @param options - Watch configuration options
* @returns Function to stop the watcher
*/
function watch<T, Immediate extends Readonly<boolean> = false>(
source: WatchSource<T>,
callback: WatchCallback<T, Immediate extends true ? T | undefined : T>,
options?: WatchOptions<T, Immediate>
): WatchHandle;
/**
* Watches multiple reactive sources
* @param sources - Array of reactive sources to watch
* @param callback - Callback executed when any source changes
* @param options - Watch configuration options
* @returns Function to stop the watcher
*/
function watch<T extends MultiWatchSources, Immediate extends Readonly<boolean> = false>(
sources: [...T],
callback: WatchCallback<MapSources<T>, Immediate extends true ? Partial<MapSources<T>> : MapSources<T>>,
options?: WatchOptions<T, Immediate>
): WatchHandle;
/**
* Watches an object and its properties
* @param source - Object to watch
* @param callback - Callback executed when object changes
* @param options - Watch configuration options
* @returns Function to stop the watcher
*/
function watch<T extends object, Immediate extends Readonly<boolean> = false>(
source: T,
callback: WatchCallback<T, Immediate extends true ? T | undefined : T>,
options?: WatchOptions<T, Immediate>
): WatchHandle;Usage Examples:
import { ref, reactive, watch } from "@vue/runtime-core";
const count = ref(0);
const state = reactive({ name: "Vue", version: 3 });
// Watch a ref
const stopWatchingCount = watch(count, (newValue, oldValue) => {
console.log(`Count changed from ${oldValue} to ${newValue}`);
});
// Watch multiple sources
const stopWatchingMultiple = watch(
[count, () => state.name],
([newCount, newName], [oldCount, oldName]) => {
console.log(`Count: ${oldCount} -> ${newCount}`);
console.log(`Name: ${oldName} -> ${newName}`);
}
);
// Watch with immediate execution
watch(
() => state.version,
(version) => {
console.log(`Version: ${version}`);
},
{ immediate: true } // Runs immediately with current value
);
// Deep watch for objects
watch(
state,
(newState, oldState) => {
console.log("State changed:", newState);
},
{ deep: true } // Watches nested properties
);
// Cleanup
stopWatchingCount();
stopWatchingMultiple();Automatically track dependencies and re-run when they change.
/**
* Runs a function immediately and re-runs when dependencies change
* @param effect - Function to run and track dependencies
* @param options - Effect configuration options
* @returns Function to stop the effect
*/
function watchEffect(
effect: WatchEffect,
options?: WatchEffectOptions
): WatchStopHandle;
/**
* Like watchEffect but runs after component updates (post-flush)
* @param effect - Function to run and track dependencies
* @param options - Effect configuration options
* @returns Function to stop the effect
*/
function watchPostEffect(
effect: WatchEffect,
options?: WatchEffectOptions
): WatchStopHandle;
/**
* Like watchEffect but runs synchronously
* @param effect - Function to run and track dependencies
* @param options - Effect configuration options
* @returns Function to stop the effect
*/
function watchSyncEffect(
effect: WatchEffect,
options?: WatchEffectOptions
): WatchStopHandle;Usage Examples:
import { ref, watchEffect, watchPostEffect, watchSyncEffect } from "@vue/runtime-core";
const count = ref(0);
const name = ref("Vue");
// Basic watchEffect
const stopEffect = watchEffect(() => {
console.log(`Count is ${count.value} and name is ${name.value}`);
});
// Runs immediately and when count or name changes
// watchEffect with cleanup
watchEffect((onInvalidate) => {
const timer = setInterval(() => {
console.log(`Timer: ${count.value}`);
}, 1000);
// Cleanup when effect is invalidated
onInvalidate(() => {
clearInterval(timer);
});
});
// Post-flush effect (runs after DOM updates)
watchPostEffect(() => {
// This runs after component DOM updates
console.log("DOM updated, count is:", count.value);
});
// Sync effect (runs synchronously)
watchSyncEffect(() => {
console.log("Sync effect, count:", count.value);
});
// Stop effects
stopEffect();Configure watch behavior with various options.
interface WatchOptions<T = any, Immediate extends boolean = boolean> {
/**
* Run the callback immediately with current value
*/
immediate?: Immediate;
/**
* Watch nested properties in objects and arrays
*/
deep?: boolean;
/**
* Flush timing: 'pre' (before updates), 'post' (after updates), 'sync' (synchronous)
*/
flush?: 'pre' | 'post' | 'sync';
/**
* Debug hook called when watcher triggers
*/
onTrack?: (event: DebuggerEvent) => void;
/**
* Debug hook called when watcher triggers
*/
onTrigger?: (event: DebuggerEvent) => void;
/**
* Indicates whether the job is allowed to recursively trigger itself
*/
allowRecurse?: boolean;
}
interface WatchEffectOptions {
/**
* Flush timing for the effect
*/
flush?: 'pre' | 'post' | 'sync';
/**
* Debug hook called when effect is tracked
*/
onTrack?: (event: DebuggerEvent) => void;
/**
* Debug hook called when effect is triggered
*/
onTrigger?: (event: DebuggerEvent) => void;
/**
* Indicates whether the job is allowed to recursively trigger itself
*/
allowRecurse?: boolean;
}Usage Examples:
import { ref, reactive, watch, watchEffect } from "@vue/runtime-core";
const user = reactive({
profile: {
name: "Alice",
settings: {
theme: "dark"
}
}
});
// Deep watching
watch(
user,
(newUser, oldUser) => {
console.log("User changed:", newUser);
},
{
deep: true, // Watch nested properties
immediate: true, // Run immediately
flush: 'post' // Run after DOM updates
}
);
// Flush timing examples
const count = ref(0);
// Pre-flush (default) - runs before DOM updates
watch(count, () => console.log("Pre-flush"), { flush: 'pre' });
// Post-flush - runs after DOM updates
watch(count, () => console.log("Post-flush"), { flush: 'post' });
// Sync - runs synchronously
watch(count, () => console.log("Sync"), { flush: 'sync' });
// Debug hooks
watchEffect(
() => {
console.log("Count:", count.value);
},
{
onTrack(e) {
console.log("Tracked:", e);
},
onTrigger(e) {
console.log("Triggered:", e);
}
}
);type WatchSource<T = any> = Ref<T> | ComputedRef<T> | (() => T);
type MultiWatchSources = (WatchSource<unknown> | object)[];
type MapSources<T> = {
[K in keyof T]: T[K] extends WatchSource<infer V>
? V
: T[K] extends object
? T[K]
: never;
};
type WatchCallback<V = any, OV = any> = (
value: V,
oldValue: OV,
onCleanup: OnCleanup
) => any;
type WatchStopHandle = () => void;
type WatchHandle = WatchStopHandle;
interface WatchEffect {
(onCleanup: OnCleanup): void;
}
type OnCleanup = (cleanupFn: () => void) => void;
interface DebuggerEvent {
effect: ReactiveEffect;
target: object;
type: TrackOpTypes | TriggerOpTypes;
key: any;
newValue?: any;
oldValue?: any;
oldTarget?: Map<any, any> | Set<any>;
}
enum TrackOpTypes {
GET = 'get',
HAS = 'has',
ITERATE = 'iterate'
}
enum TriggerOpTypes {
SET = 'set',
ADD = 'add',
DELETE = 'delete',
CLEAR = 'clear'
}Install with Tessl CLI
npx tessl i tessl/npm-vue--runtime-core