0
# Asset Resolution
1
2
Vue's asset resolution system enables dynamic resolution of components and directives at runtime, supporting flexible component registration and usage patterns.
3
4
## Capabilities
5
6
### Component Resolution
7
8
Resolve registered components by name at runtime.
9
10
```typescript { .api }
11
/**
12
* Resolves a globally registered component by name
13
* @param name - Component name to resolve
14
* @returns Resolved component or component name string if not found
15
*/
16
function resolveComponent(name: string): ConcreteComponent | string;
17
```
18
19
**Usage Examples:**
20
21
```typescript
22
import { defineComponent, resolveComponent, h } from "@vue/runtime-core";
23
24
// Basic component resolution
25
const DynamicComponentExample = defineComponent({
26
props: {
27
componentName: { type: String, required: true }
28
},
29
30
setup(props) {
31
return () => {
32
// Resolve component by name
33
const Component = resolveComponent(props.componentName);
34
35
// Check if component was found
36
if (typeof Component === 'string') {
37
console.warn(`Component "${props.componentName}" not found`);
38
return h('div', `Component "${props.componentName}" not registered`);
39
}
40
41
return h(Component, { message: 'Hello from dynamic component' });
42
};
43
}
44
});
45
46
// Usage with component registry
47
const app = createApp({
48
components: {
49
'my-button': MyButtonComponent,
50
'my-input': MyInputComponent,
51
'my-modal': MyModalComponent
52
},
53
54
setup() {
55
const currentComponent = ref('my-button');
56
57
return () => {
58
const Component = resolveComponent(currentComponent.value);
59
return h(Component);
60
};
61
}
62
});
63
64
// Conditional component rendering
65
const ConditionalRenderer = defineComponent({
66
props: {
67
showAdvanced: Boolean
68
},
69
70
setup(props) {
71
return () => {
72
const componentName = props.showAdvanced ? 'advanced-form' : 'simple-form';
73
const Component = resolveComponent(componentName);
74
75
return typeof Component !== 'string'
76
? h(Component)
77
: h('div', 'Component not available');
78
};
79
}
80
});
81
```
82
83
### Directive Resolution
84
85
Resolve registered directives by name at runtime.
86
87
```typescript { .api }
88
/**
89
* Resolves a globally registered directive by name
90
* @param name - Directive name to resolve (without v- prefix)
91
* @returns Resolved directive or undefined if not found
92
*/
93
function resolveDirective(name: string): Directive | undefined;
94
```
95
96
**Usage Examples:**
97
98
```typescript
99
import { defineComponent, resolveDirective, withDirectives, h } from "@vue/runtime-core";
100
101
// Dynamic directive application
102
const DynamicDirectiveExample = defineComponent({
103
props: {
104
directiveName: String,
105
directiveValue: String
106
},
107
108
setup(props) {
109
return () => {
110
const element = h('input', { type: 'text' });
111
112
if (props.directiveName) {
113
const directive = resolveDirective(props.directiveName);
114
115
if (directive) {
116
return withDirectives(element, [
117
[directive, props.directiveValue]
118
]);
119
}
120
}
121
122
return element;
123
};
124
}
125
});
126
127
// Conditional directive usage
128
const ConditionalDirective = defineComponent({
129
props: {
130
enableTooltip: Boolean,
131
tooltipText: String
132
},
133
134
setup(props) {
135
return () => {
136
let element = h('button', 'Hover me');
137
138
if (props.enableTooltip) {
139
const tooltipDirective = resolveDirective('tooltip');
140
141
if (tooltipDirective) {
142
element = withDirectives(element, [
143
[tooltipDirective, props.tooltipText]
144
]);
145
}
146
}
147
148
return element;
149
};
150
}
151
});
152
153
// Multiple directive resolution
154
const MultiDirectiveComponent = defineComponent({
155
setup() {
156
const directives = ['focus', 'tooltip', 'ripple'];
157
158
return () => {
159
const resolvedDirectives = directives
160
.map(name => ({ name, directive: resolveDirective(name) }))
161
.filter(item => item.directive);
162
163
let element = h('button', 'Multi-directive button');
164
165
if (resolvedDirectives.length > 0) {
166
const directiveBindings = resolvedDirectives.map(({ directive }) => [
167
directive,
168
true // Simple binding value
169
]);
170
171
element = withDirectives(element, directiveBindings);
172
}
173
174
return element;
175
};
176
}
177
});
178
```
179
180
### Dynamic Component Resolution
181
182
Resolve components dynamically with support for various input types.
183
184
```typescript { .api }
185
/**
186
* Resolves dynamic component reference to actual component
187
* @param component - Component reference (string, component object, or function)
188
* @returns Resolved component type
189
*/
190
function resolveDynamicComponent(component: unknown): VNodeTypes;
191
```
192
193
**Usage Examples:**
194
195
```typescript
196
import { defineComponent, resolveDynamicComponent, h } from "@vue/runtime-core";
197
198
// Flexible component resolution
199
const FlexibleRenderer = defineComponent({
200
props: {
201
component: [String, Object, Function]
202
},
203
204
setup(props) {
205
return () => {
206
const resolvedComponent = resolveDynamicComponent(props.component);
207
return h(resolvedComponent);
208
};
209
}
210
});
211
212
// Component factory pattern
213
const ComponentFactory = defineComponent({
214
props: {
215
type: { type: String, required: true },
216
config: Object
217
},
218
219
setup(props) {
220
const componentMap: Record<string, any> = {
221
'button': () => import('./ButtonComponent.vue'),
222
'input': () => import('./InputComponent.vue'),
223
'select': () => import('./SelectComponent.vue')
224
};
225
226
return () => {
227
const componentSpec = componentMap[props.type];
228
const resolvedComponent = resolveDynamicComponent(componentSpec);
229
230
return h(resolvedComponent, props.config);
231
};
232
}
233
});
234
235
// Plugin-based component resolution
236
const PluginRenderer = defineComponent({
237
props: {
238
pluginName: String,
239
componentName: String
240
},
241
242
setup(props) {
243
return () => {
244
// Resolve from plugin registry
245
const pluginComponent = window.plugins?.[props.pluginName]?.[props.componentName];
246
const resolvedComponent = resolveDynamicComponent(pluginComponent || 'div');
247
248
return h(resolvedComponent);
249
};
250
}
251
});
252
```
253
254
### Advanced Resolution Patterns
255
256
```typescript
257
// Asset resolution with fallbacks
258
function useComponentWithFallback(primaryName: string, fallbackName: string) {
259
return computed(() => {
260
const primary = resolveComponent(primaryName);
261
if (typeof primary !== 'string') {
262
return primary;
263
}
264
265
const fallback = resolveComponent(fallbackName);
266
return typeof fallback !== 'string' ? fallback : 'div';
267
});
268
}
269
270
// Lazy directive resolution
271
function useLazyDirective(name: string) {
272
const directive = ref<Directive | null>(null);
273
274
onMounted(() => {
275
directive.value = resolveDirective(name) || null;
276
});
277
278
return directive;
279
}
280
281
// Dynamic asset registry
282
class DynamicAssetRegistry {
283
private components = new Map<string, Component>();
284
private directives = new Map<string, Directive>();
285
286
registerComponent(name: string, component: Component) {
287
this.components.set(name, component);
288
}
289
290
registerDirective(name: string, directive: Directive) {
291
this.directives.set(name, directive);
292
}
293
294
resolveComponent(name: string) {
295
return this.components.get(name) || resolveComponent(name);
296
}
297
298
resolveDirective(name: string) {
299
return this.directives.get(name) || resolveDirective(name);
300
}
301
}
302
303
const registry = new DynamicAssetRegistry();
304
305
// Theme-based component resolution
306
function useThemeComponent(baseName: string) {
307
const theme = inject('theme', 'default');
308
309
return computed(() => {
310
const themedName = `${baseName}-${theme}`;
311
const themedComponent = resolveComponent(themedName);
312
313
// Fallback to base component if themed version not found
314
if (typeof themedComponent === 'string') {
315
return resolveComponent(baseName);
316
}
317
318
return themedComponent;
319
});
320
}
321
322
// Async component resolution
323
async function resolveAsyncComponent(name: string): Promise<Component | null> {
324
const syncComponent = resolveComponent(name);
325
326
if (typeof syncComponent !== 'string') {
327
return syncComponent;
328
}
329
330
// Try to load async
331
try {
332
const module = await import(`./components/${name}.vue`);
333
return module.default;
334
} catch {
335
return null;
336
}
337
}
338
```
339
340
### Error Handling and Validation
341
342
```typescript
343
// Safe component resolution with validation
344
function useSafeComponent(name: string, validator?: (component: any) => boolean) {
345
return computed(() => {
346
const component = resolveComponent(name);
347
348
if (typeof component === 'string') {
349
console.warn(`Component "${name}" not found`);
350
return null;
351
}
352
353
if (validator && !validator(component)) {
354
console.warn(`Component "${name}" failed validation`);
355
return null;
356
}
357
358
return component;
359
});
360
}
361
362
// Directive resolution with type checking
363
function useSafeDirective(name: string): Ref<Directive | null> {
364
const directive = ref<Directive | null>(null);
365
366
watchEffect(() => {
367
const resolved = resolveDirective(name);
368
369
if (resolved && typeof resolved === 'object') {
370
directive.value = resolved;
371
} else {
372
console.warn(`Directive "v-${name}" not found`);
373
directive.value = null;
374
}
375
});
376
377
return directive;
378
}
379
380
// Resolution with dependency tracking
381
function useTrackedResolution() {
382
const resolvedComponents = reactive(new Map<string, Component>());
383
const resolvedDirectives = reactive(new Map<string, Directive>());
384
385
const getComponent = (name: string) => {
386
if (!resolvedComponents.has(name)) {
387
const component = resolveComponent(name);
388
if (typeof component !== 'string') {
389
resolvedComponents.set(name, component);
390
}
391
}
392
return resolvedComponents.get(name);
393
};
394
395
const getDirective = (name: string) => {
396
if (!resolvedDirectives.has(name)) {
397
const directive = resolveDirective(name);
398
if (directive) {
399
resolvedDirectives.set(name, directive);
400
}
401
}
402
return resolvedDirectives.get(name);
403
};
404
405
return {
406
getComponent,
407
getDirective,
408
resolvedComponents: readonly(resolvedComponents),
409
resolvedDirectives: readonly(resolvedDirectives)
410
};
411
}
412
```
413
414
## Types
415
416
```typescript { .api }
417
type ConcreteComponent = ComponentOptions | FunctionalComponent;
418
419
type VNodeTypes =
420
| string
421
| VNode
422
| Component
423
| typeof Text
424
| typeof Static
425
| typeof Comment
426
| typeof Fragment;
427
428
interface Directive<T = any, V = any> {
429
created?(el: T, binding: DirectiveBinding<V>, vnode: VNode, prevVNode: VNode | null): void;
430
beforeMount?(el: T, binding: DirectiveBinding<V>, vnode: VNode, prevVNode: VNode | null): void;
431
mounted?(el: T, binding: DirectiveBinding<V>, vnode: VNode, prevVNode: VNode | null): void;
432
beforeUpdate?(el: T, binding: DirectiveBinding<V>, vnode: VNode, prevVNode: VNode): void;
433
updated?(el: T, binding: DirectiveBinding<V>, vnode: VNode, prevVNode: VNode): void;
434
beforeUnmount?(el: T, binding: DirectiveBinding<V>, vnode: VNode, prevVNode: VNode | null): void;
435
unmounted?(el: T, binding: DirectiveBinding<V>, vnode: VNode, prevVNode: VNode | null): void;
436
}
437
438
interface DirectiveBinding<V = any> {
439
instance: ComponentPublicInstance | null;
440
value: V;
441
oldValue: V | null;
442
arg?: string;
443
modifiers: Record<string, boolean>;
444
dir: ObjectDirective<any, V>;
445
}
446
447
type FunctionalComponent<P = {}, E extends EmitsOptions = {}> = {
448
(props: P, ctx: Omit<SetupContext<E>, 'expose'>): any;
449
props?: ComponentPropsOptions<P>;
450
emits?: E | (keyof E)[];
451
inheritAttrs?: boolean;
452
displayName?: string;
453
};
454
```
455
456
## Usage Guidelines
457
458
### Best Practices
459
460
1. **Cache resolved assets** for performance in frequently called render functions
461
2. **Validate resolved components** before using them in render functions
462
3. **Provide fallbacks** for missing components to avoid runtime errors
463
4. **Use type guards** when working with dynamically resolved assets
464
465
### Common Patterns
466
467
1. **Dynamic form builders** using component resolution for field types
468
2. **Plugin systems** with runtime component registration
469
3. **Theme systems** with themed component variants
470
4. **Micro-frontends** with dynamic asset loading
471
472
### Performance Considerations
473
474
- Asset resolution happens during render, so cache results when possible
475
- Use `computed` to memoize resolution results
476
- Consider lazy loading for components that may not be used