Vue.js 3 runtime core library providing foundational APIs for building custom renderers and managing reactive component systems
—
Vue's scheduler provides utilities for controlling the timing of updates and DOM operations, enabling precise control over when code executes relative to Vue's update cycle.
Schedule callbacks to run after the next DOM update cycle.
/**
* Defers callback execution until after the next DOM update cycle
* @param fn - Optional callback function to execute
* @returns Promise that resolves after the next tick
*/
function nextTick(fn?: () => void): Promise<void>;Usage Examples:
import { ref, nextTick } from "@vue/runtime-core";
const MyComponent = defineComponent({
setup() {
const count = ref(0);
const elementRef = useTemplateRef<HTMLElement>('element');
const updateAndRead = async () => {
// Update reactive data
count.value++;
// Wait for DOM to update
await nextTick();
// Now DOM reflects the new count
if (elementRef.value) {
console.log('Updated DOM content:', elementRef.value.textContent);
console.log('DOM height:', elementRef.value.offsetHeight);
}
};
// With callback style
const updateWithCallback = () => {
count.value++;
nextTick(() => {
console.log('DOM updated with new count:', count.value);
});
};
// Multiple nextTick calls
const chainedUpdates = async () => {
count.value = 1;
await nextTick();
console.log('First update complete');
count.value = 2;
await nextTick();
console.log('Second update complete');
};
return {
count,
elementRef,
updateAndRead,
updateWithCallback,
chainedUpdates
};
}
});
// Usage in lifecycle hooks
const LifecycleExample = defineComponent({
setup() {
const data = ref('initial');
onMounted(async () => {
// Change data
data.value = 'mounted';
// Wait for DOM update
await nextTick();
// Now safe to access updated DOM
console.log('DOM updated after mount');
});
return { data };
}
});
// Error handling with nextTick
const ErrorHandlingExample = defineComponent({
setup() {
const handleUpdate = async () => {
try {
// Some reactive updates
// ...
await nextTick();
// DOM operations that might fail
const element = document.querySelector('.my-element');
if (element) {
element.scrollIntoView();
}
} catch (error) {
console.error('Error after nextTick:', error);
}
};
return { handleUpdate };
}
});Queue callbacks to run after all component updates have been flushed.
/**
* Queues a callback to run after all pending updates have been flushed
* @param cb - Callback function to queue
*/
function queuePostFlushCb(cb: SchedulerJob): void;
interface SchedulerJob {
(): void;
id?: number;
pre?: boolean;
active?: boolean;
computed?: boolean;
}Usage Examples:
import { ref, queuePostFlushCb } from "@vue/runtime-core";
const PostFlushExample = defineComponent({
setup() {
const list = ref([1, 2, 3]);
const addItems = () => {
// Update the list
list.value.push(4, 5, 6);
// Queue callback to run after all updates
queuePostFlushCb(() => {
console.log('All updates flushed, list length:', list.value.length);
// Safe to access updated DOM here
const listElement = document.querySelector('.list');
if (listElement) {
console.log('List DOM children:', listElement.children.length);
}
});
};
// Multiple post-flush callbacks
const multipleCallbacks = () => {
list.value = [10, 20, 30];
queuePostFlushCb(() => {
console.log('First post-flush callback');
});
queuePostFlushCb(() => {
console.log('Second post-flush callback');
});
};
// Callback with job properties
const advancedCallback = () => {
list.value.reverse();
const job: SchedulerJob = () => {
console.log('Advanced post-flush job executed');
};
job.id = 1;
queuePostFlushCb(job);
};
return {
list,
addItems,
multipleCallbacks,
advancedCallback
};
}
});
// Integration with watchers
const WatcherIntegration = defineComponent({
setup() {
const count = ref(0);
const computedDouble = computed(() => count.value * 2);
watch(count, (newCount) => {
console.log('Watcher fired for count:', newCount);
// Queue post-flush callback
queuePostFlushCb(() => {
console.log('Post-flush: computed value is', computedDouble.value);
});
});
return {
count,
computedDouble,
increment: () => count.value++
};
}
});Common patterns for precise timing control in Vue applications.
// Debounced updates with nextTick
function useDebounceWithNextTick<T extends (...args: any[]) => any>(
fn: T,
delay: number
): T {
let timeoutId: NodeJS.Timeout;
return ((...args: Parameters<T>) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(async () => {
await nextTick();
fn(...args);
}, delay);
}) as T;
}
// Batch DOM reads after updates
class DOMBatchReader {
private callbacks: (() => void)[] = [];
private scheduled = false;
read(callback: () => void) {
this.callbacks.push(callback);
if (!this.scheduled) {
this.scheduled = true;
nextTick(() => {
const toExecute = [...this.callbacks];
this.callbacks.length = 0;
this.scheduled = false;
toExecute.forEach(cb => cb());
});
}
}
}
const batchReader = new DOMBatchReader();
const BatchReadingExample = defineComponent({
setup() {
const height = ref(0);
const width = ref(0);
const elementRef = useTemplateRef<HTMLElement>('element');
const measureElement = () => {
batchReader.read(() => {
if (elementRef.value) {
height.value = elementRef.value.offsetHeight;
width.value = elementRef.value.offsetWidth;
}
});
};
return {
height,
width,
elementRef,
measureElement
};
}
});
// Coordinated updates across components
const useCoordinatedUpdate = () => {
const pendingUpdates = new Set<() => void>();
const scheduleUpdate = (updateFn: () => void) => {
pendingUpdates.add(updateFn);
nextTick(() => {
queuePostFlushCb(() => {
pendingUpdates.forEach(fn => fn());
pendingUpdates.clear();
});
});
};
return { scheduleUpdate };
};
// Animation frame coordination
const useAnimationTiming = () => {
const requestNextFrame = (callback: () => void) => {
nextTick(() => {
requestAnimationFrame(callback);
});
};
const requestDoubleFrame = (callback: () => void) => {
requestNextFrame(() => {
requestAnimationFrame(callback);
});
};
return {
requestNextFrame,
requestDoubleFrame
};
};queuePostFlushCb callbacks execute after nextTicknextTick before measuring DOM elementsnextTick when integrating with libraries that need updated DOMrequestAnimationFrame for smooth animationsnextTick// Good: Batch multiple updates
const batchUpdates = async () => {
item1.value = 'new value 1';
item2.value = 'new value 2';
item3.value = 'new value 3';
// Single nextTick for all updates
await nextTick();
measureAllElements();
};
// Avoid: Multiple nextTick calls for same update cycle
const inefficientUpdates = async () => {
item1.value = 'new value 1';
await nextTick(); // Unnecessary
item2.value = 'new value 2';
await nextTick(); // Unnecessary
item3.value = 'new value 3';
await nextTick(); // Only this one needed
};interface SchedulerJob {
(): void;
/**
* Job ID for sorting/deduplication
*/
id?: number;
/**
* Whether this is a pre-flush job
*/
pre?: boolean;
/**
* Whether the job is currently active
*/
active?: boolean;
/**
* Whether this job is from a computed
*/
computed?: boolean;
}
/**
* Function signature for nextTick
*/
interface NextTickFunction {
(): Promise<void>;
(fn: () => void): Promise<void>;
}
/**
* Function signature for queuePostFlushCb
*/
interface QueuePostFlushCbFunction {
(cb: SchedulerJob): void;
}Install with Tessl CLI
npx tessl i tessl/npm-vue--runtime-core