0
# Internal Render Helpers
1
2
Vue's internal render helpers provide low-level utilities for advanced rendering scenarios, custom renderers, and compiler-generated code. These APIs are primarily used by Vue's template compiler and custom renderer implementations.
3
4
## Capabilities
5
6
### Block Tracking
7
8
Control Vue's block tracking system for optimized re-rendering.
9
10
```typescript { .api }
11
/**
12
* Opens a new block for tracking dynamic children
13
* @param disableTracking - Whether to disable tracking for this block
14
*/
15
function openBlock(disableTracking?: boolean): void;
16
17
/**
18
* Creates a block VNode with tracked dynamic children
19
* @param type - Element type or component
20
* @param props - Element/component props
21
* @param children - Child nodes
22
* @param patchFlag - Optimization hints
23
* @param dynamicProps - Names of dynamic props
24
* @returns Block VNode
25
*/
26
function createBlock(
27
type: VNodeTypes,
28
props?: VNodeProps | null,
29
children?: VNodeArrayChildren,
30
patchFlag?: number,
31
dynamicProps?: string[]
32
): VNode;
33
34
/**
35
* Sets the block tracking value
36
* @param value - Positive number enables tracking, 0 or negative disables
37
*/
38
function setBlockTracking(value: number): void;
39
```
40
41
**Usage Examples:**
42
43
```typescript
44
import { openBlock, createBlock, setBlockTracking, h } from "@vue/runtime-core";
45
46
// Manual block creation (typically done by compiler)
47
const createOptimizedElement = () => {
48
openBlock();
49
50
return createBlock('div', { class: 'container' }, [
51
h('span', 'Static content'),
52
// Dynamic content will be tracked
53
h('span', { key: Math.random() }, 'Dynamic content')
54
]);
55
};
56
57
// Disable tracking for certain scenarios
58
const createUnoptimizedElement = () => {
59
setBlockTracking(-1); // Disable tracking
60
61
const element = h('div', [
62
h('span', 'This won\'t be optimized')
63
]);
64
65
setBlockTracking(1); // Re-enable tracking
66
67
return element;
68
};
69
70
// Conditional block tracking
71
const createConditionalBlock = (optimize: boolean) => {
72
if (optimize) {
73
openBlock();
74
return createBlock('div', null, [
75
// Children will be tracked for optimization
76
]);
77
} else {
78
return h('div', [
79
// Children won't be tracked
80
]);
81
}
82
};
83
```
84
85
### VNode Creation Helpers
86
87
Low-level VNode creation functions for specific node types.
88
89
```typescript { .api }
90
/**
91
* Creates a text VNode
92
* @param text - Text content
93
* @param flag - Patch flag for optimization
94
* @returns Text VNode
95
*/
96
function createTextVNode(text?: string, flag?: number): VNode;
97
98
/**
99
* Creates a comment VNode
100
* @param text - Comment content
101
* @param asBlock - Whether to create as block
102
* @returns Comment VNode
103
*/
104
function createCommentVNode(text?: string, asBlock?: boolean): VNode;
105
106
/**
107
* Creates a static VNode for hoisted content
108
* @param content - Static HTML content
109
* @param numberOfNodes - Number of nodes in the content
110
* @returns Static VNode
111
*/
112
function createStaticVNode(content: string, numberOfNodes: number): VNode;
113
114
/**
115
* Creates an element VNode
116
* @param type - Element tag name
117
* @param props - Element properties
118
* @param children - Child nodes
119
* @param patchFlag - Optimization flag
120
* @param dynamicProps - Dynamic property names
121
* @returns Element VNode
122
*/
123
function createElementVNode(
124
type: string,
125
props?: VNodeProps | null,
126
children?: VNodeArrayChildren,
127
patchFlag?: number,
128
dynamicProps?: string[]
129
): VNode;
130
131
/**
132
* Creates an element block VNode (with tracking)
133
* @param type - Element tag name
134
* @param props - Element properties
135
* @param children - Child nodes
136
* @param patchFlag - Optimization flag
137
* @param dynamicProps - Dynamic property names
138
* @returns Element block VNode
139
*/
140
function createElementBlock(
141
type: string,
142
props?: VNodeProps | null,
143
children?: VNodeArrayChildren,
144
patchFlag?: number,
145
dynamicProps?: string[]
146
): VNode;
147
```
148
149
**Usage Examples:**
150
151
```typescript
152
import {
153
createTextVNode,
154
createCommentVNode,
155
createStaticVNode,
156
createElementVNode,
157
createElementBlock
158
} from "@vue/runtime-core";
159
160
// Text node creation
161
const textNode = createTextVNode('Hello World');
162
const dynamicTextNode = createTextVNode(
163
'Dynamic text',
164
1 // PatchFlags.TEXT - indicates text content can change
165
);
166
167
// Comment nodes
168
const commentNode = createCommentVNode('This is a comment');
169
const blockComment = createCommentVNode('Block comment', true);
170
171
// Static content (hoisted by compiler)
172
const staticNode = createStaticVNode(
173
'<div class="static"><span>Static content</span></div>',
174
2 // Number of nodes in the static content
175
);
176
177
// Element nodes
178
const simpleElement = createElementVNode('div', { id: 'simple' });
179
180
const dynamicElement = createElementVNode(
181
'div',
182
{ class: 'dynamic' },
183
[textNode],
184
8, // PatchFlags.PROPS - indicates props can change
185
['class'] // Dynamic prop names
186
);
187
188
// Element blocks (with optimization tracking)
189
const elementBlock = createElementBlock(
190
'div',
191
{ class: 'block' },
192
[
193
createTextVNode('Block content'),
194
createElementVNode('span', null, 'Span content')
195
]
196
);
197
```
198
199
### Render List Helpers
200
201
Utilities for rendering lists efficiently.
202
203
```typescript { .api }
204
/**
205
* Renders a list of items with a render function
206
* @param source - Array, object, string, or number to iterate
207
* @param renderItem - Function to render each item
208
* @returns Array of VNodes
209
*/
210
function renderList<T>(
211
source: T[] | Record<string | number, T> | string | number,
212
renderItem: (value: T, key: string | number, index: number) => VNode
213
): VNode[];
214
```
215
216
**Usage Examples:**
217
218
```typescript
219
import { renderList, h } from "@vue/runtime-core";
220
221
// Array rendering
222
const items = ['apple', 'banana', 'cherry'];
223
const listNodes = renderList(items, (item, key, index) => {
224
return h('li', { key }, `${index}: ${item}`);
225
});
226
227
// Object rendering
228
const user = { name: 'John', age: 30, city: 'New York' };
229
const userInfoNodes = renderList(user, (value, key) => {
230
return h('div', { key }, `${key}: ${value}`);
231
});
232
233
// Number rendering (1 to n)
234
const numberNodes = renderList(5, (value, key) => {
235
return h('span', { key }, `Number ${value}`);
236
});
237
238
// String rendering (each character)
239
const charNodes = renderList('hello', (char, index) => {
240
return h('span', { key: index }, char);
241
});
242
243
// Complete list component
244
const ListComponent = defineComponent({
245
props: {
246
items: Array,
247
itemRenderer: Function
248
},
249
250
setup(props) {
251
return () => h('ul',
252
renderList(props.items || [], (item, index) => {
253
return props.itemRenderer
254
? props.itemRenderer(item, index)
255
: h('li', { key: index }, String(item));
256
})
257
);
258
}
259
});
260
```
261
262
### Slot Rendering Helpers
263
264
Utilities for rendering slots and slot content.
265
266
```typescript { .api }
267
/**
268
* Renders a slot with optional props and fallback content
269
* @param slots - Slots object
270
* @param name - Slot name
271
* @param props - Props to pass to slot
272
* @param fallback - Fallback content if slot is empty
273
* @returns VNode or array of VNodes
274
*/
275
function renderSlot(
276
slots: Slots,
277
name: string,
278
props?: Data,
279
fallback?: () => VNodeArrayChildren
280
): VNode;
281
282
/**
283
* Creates a slots object from slot definitions
284
* @param slots - Base slots object
285
* @param dynamicSlots - Dynamic slot descriptors
286
* @returns Complete slots object
287
*/
288
function createSlots(
289
slots: Record<string, Slot>,
290
dynamicSlots: (CompiledSlotDescriptor | CompiledSlotDescriptor[])[]
291
): Slots;
292
293
interface CompiledSlotDescriptor {
294
name: string;
295
fn: Slot;
296
key?: string | number;
297
}
298
```
299
300
**Usage Examples:**
301
302
```typescript
303
import { renderSlot, createSlots, h } from "@vue/runtime-core";
304
305
// Render slot with fallback
306
const SlotComponent = defineComponent({
307
setup(_, { slots }) {
308
return () => h('div', { class: 'wrapper' }, [
309
renderSlot(slots, 'header', {}, () => [
310
h('h1', 'Default Header')
311
]),
312
313
renderSlot(slots, 'default'),
314
315
renderSlot(slots, 'footer', { timestamp: Date.now() }, () => [
316
h('footer', 'Default Footer')
317
])
318
]);
319
}
320
});
321
322
// Dynamic slot creation
323
const DynamicSlotComponent = defineComponent({
324
props: {
325
slotConfigs: Array
326
},
327
328
setup(props, { slots }) {
329
return () => {
330
const dynamicSlots = props.slotConfigs?.map(config => ({
331
name: config.name,
332
fn: (props) => h('div', `Dynamic slot: ${config.name}`)
333
})) || [];
334
335
const allSlots = createSlots(slots, dynamicSlots);
336
337
return h('div',
338
Object.keys(allSlots).map(name =>
339
renderSlot(allSlots, name)
340
)
341
);
342
};
343
}
344
});
345
```
346
347
### Event Handler Helpers
348
349
Utilities for handling events and handler normalization.
350
351
```typescript { .api }
352
/**
353
* Converts event object to handler functions
354
* @param obj - Object with event handlers
355
* @returns Object with normalized handlers
356
*/
357
function toHandlers(obj: Record<string, any>): Record<string, Function | Function[]>;
358
```
359
360
**Usage Examples:**
361
362
```typescript
363
import { toHandlers, h } from "@vue/runtime-core";
364
365
// Convert event object to handlers
366
const EventComponent = defineComponent({
367
props: {
368
events: Object
369
},
370
371
setup(props) {
372
return () => {
373
const handlers = toHandlers(props.events || {});
374
375
return h('button', {
376
...handlers,
377
class: 'event-button'
378
}, 'Click me');
379
};
380
}
381
});
382
383
// Usage
384
const eventConfig = {
385
onClick: () => console.log('clicked'),
386
onMouseenter: () => console.log('mouse enter'),
387
onMouseleave: () => console.log('mouse leave')
388
};
389
390
h(EventComponent, { events: eventConfig });
391
392
// Multiple handlers for same event
393
const multiHandlerConfig = {
394
onClick: [
395
() => console.log('First handler'),
396
() => console.log('Second handler')
397
]
398
};
399
400
const multiHandlers = toHandlers(multiHandlerConfig);
401
// Results in: { onClick: [Function, Function] }
402
```
403
404
### Memoization Helpers
405
406
Utilities for memoizing render functions and expensive computations.
407
408
```typescript { .api }
409
/**
410
* Memoizes a render function based on dependencies
411
* @param memo - Dependency array for memoization
412
* @param render - Render function to memoize
413
* @param cache - Cache array for storing previous results
414
* @param index - Cache index for this memoization
415
* @returns Memoized result or new computation
416
*/
417
function withMemo<T>(
418
memo: any[],
419
render: () => T,
420
cache: any[],
421
index: number
422
): T;
423
424
/**
425
* Checks if memoization dependencies are the same
426
* @param cached - Previously cached dependencies
427
* @param memo - Current dependencies
428
* @returns True if dependencies are the same
429
*/
430
function isMemoSame(cached: any[], memo: any[]): boolean;
431
```
432
433
**Usage Examples:**
434
435
```typescript
436
import { withMemo, isMemoSame, h } from "@vue/runtime-core";
437
438
const MemoizedComponent = defineComponent({
439
props: {
440
items: Array,
441
processor: Function
442
},
443
444
setup(props) {
445
const cache: any[] = [];
446
447
return () => {
448
// Memoize expensive list processing
449
const processedItems = withMemo(
450
[props.items, props.processor], // Dependencies
451
() => {
452
console.log('Processing items...');
453
return props.items?.map(props.processor) || [];
454
},
455
cache, // Cache array
456
0 // Cache index
457
);
458
459
return h('ul',
460
processedItems.map((item, index) =>
461
h('li', { key: index }, item)
462
)
463
);
464
};
465
}
466
});
467
468
// Manual memoization check
469
const useMemoCheck = () => {
470
const lastDeps = ref<any[]>([]);
471
const lastResult = ref<any>(null);
472
473
const memoizedCompute = (deps: any[], computeFn: () => any) => {
474
if (!isMemoSame(lastDeps.value, deps)) {
475
lastResult.value = computeFn();
476
lastDeps.value = deps;
477
}
478
return lastResult.value;
479
};
480
481
return { memoizedCompute };
482
};
483
```
484
485
### Context and Scope Helpers
486
487
Utilities for managing render context and scoped CSS.
488
489
```typescript { .api }
490
/**
491
* Wraps a function with render context
492
* @param fn - Function to wrap
493
* @param ctx - Component context
494
* @returns Wrapped function
495
*/
496
function withCtx<T extends Function>(fn: T, ctx?: ComponentInternalInstance): T;
497
498
/**
499
* Pushes a scope ID for scoped CSS
500
* @param id - Scope ID to push
501
*/
502
function pushScopeId(id: string | null): void;
503
504
/**
505
* Pops the current scope ID
506
*/
507
function popScopeId(): void;
508
509
/**
510
* Wraps a function to run with a specific scope ID
511
* @param id - Scope ID to use
512
* @returns Function wrapper
513
*/
514
function withScopeId<T extends Function>(id: string): (fn: T) => T;
515
```
516
517
**Usage Examples:**
518
519
```typescript
520
import { withCtx, pushScopeId, popScopeId, withScopeId } from "@vue/runtime-core";
521
522
// Context wrapping
523
const ContextComponent = defineComponent({
524
setup() {
525
const instance = getCurrentInstance();
526
527
const wrappedHandler = withCtx(() => {
528
console.log('Handler called with context');
529
}, instance);
530
531
return () => h('button', { onClick: wrappedHandler }, 'Click');
532
}
533
});
534
535
// Scoped CSS management
536
const ScopedComponent = defineComponent({
537
setup() {
538
const scopeId = 'data-v-123abc';
539
540
return () => {
541
pushScopeId(scopeId);
542
543
const element = h('div', { class: 'scoped' }, [
544
h('span', 'Scoped content')
545
]);
546
547
popScopeId();
548
549
return element;
550
};
551
}
552
});
553
554
// Scope ID wrapper
555
const createScopedRenderer = (scopeId: string) => {
556
const withScope = withScopeId(scopeId);
557
558
return withScope((props: any) => {
559
return h('div', { class: 'scoped-component' }, props.children);
560
});
561
};
562
563
const ScopedRenderer = createScopedRenderer('data-v-456def');
564
```
565
566
## Advanced Usage Patterns
567
568
### Custom Render Pipeline
569
570
```typescript
571
// Custom render function with optimizations
572
const createOptimizedRenderer = () => {
573
const cache: any[] = [];
574
let blockStack: VNode[] = [];
575
576
const render = (component: any, props: any) => {
577
openBlock();
578
579
const vnode = withMemo(
580
[component, props],
581
() => {
582
setBlockTracking(1);
583
584
const result = createElementBlock(
585
'div',
586
{ class: 'optimized' },
587
[
588
createTextVNode('Optimized content'),
589
renderList(props.items || [], (item, index) =>
590
createElementVNode('span', { key: index }, item)
591
)
592
]
593
);
594
595
setBlockTracking(0);
596
return result;
597
},
598
cache,
599
0
600
);
601
602
return vnode;
603
};
604
605
return { render };
606
};
607
608
// Static hoisting simulation
609
const createHoistedContent = () => {
610
// This would typically be done by the compiler
611
const hoistedStatic = createStaticVNode(
612
'<div class="static-header"><h1>Static Title</h1></div>',
613
2
614
);
615
616
return (dynamicContent: VNode[]) => {
617
return createElementBlock('div', null, [
618
hoistedStatic,
619
...dynamicContent
620
]);
621
};
622
};
623
```
624
625
## Types
626
627
```typescript { .api }
628
interface Slots {
629
[name: string]: Slot | undefined;
630
}
631
632
type Slot<T = any> = (...args: any[]) => VNode[];
633
634
interface CompiledSlotDescriptor {
635
name: string;
636
fn: Slot;
637
key?: string | number;
638
}
639
640
type Data = Record<string, unknown>;
641
642
// Patch flags for optimization
643
enum PatchFlags {
644
TEXT = 1,
645
CLASS = 2,
646
STYLE = 4,
647
PROPS = 8,
648
FULL_PROPS = 16,
649
HYDRATE_EVENTS = 32,
650
STABLE_FRAGMENT = 64,
651
KEYED_FRAGMENT = 128,
652
UNKEYED_FRAGMENT = 256,
653
NEED_PATCH = 512,
654
DYNAMIC_SLOTS = 1024,
655
HOISTED = -1,
656
BAIL = -2
657
}
658
```
659
660
## Performance Considerations
661
662
### Block Tracking Benefits
663
664
1. **Selective Updates**: Only tracked dynamic children are diffed
665
2. **Skip Static Content**: Static content is completely skipped during updates
666
3. **Efficient Reconciliation**: Reduces the number of nodes to compare
667
668
### Memoization Guidelines
669
670
1. **Use for Expensive Operations**: Only memoize computationally expensive functions
671
2. **Dependency Management**: Keep dependency arrays minimal and specific
672
3. **Cache Management**: Consider cache size limits for long-running applications
673
674
### Static Hoisting
675
676
1. **Compiler Optimization**: Static content is hoisted outside render functions
677
2. **Memory Efficiency**: Static VNodes are created once and reused
678
3. **Performance Improvement**: Reduces VNode creation overhead