Vue.js 3 runtime core library providing foundational APIs for building custom renderers and managing reactive component systems
—
Vue's asset resolution system enables dynamic resolution of components and directives at runtime, supporting flexible component registration and usage patterns.
Resolve registered components by name at runtime.
/**
* Resolves a globally registered component by name
* @param name - Component name to resolve
* @returns Resolved component or component name string if not found
*/
function resolveComponent(name: string): ConcreteComponent | string;Usage Examples:
import { defineComponent, resolveComponent, h } from "@vue/runtime-core";
// Basic component resolution
const DynamicComponentExample = defineComponent({
props: {
componentName: { type: String, required: true }
},
setup(props) {
return () => {
// Resolve component by name
const Component = resolveComponent(props.componentName);
// Check if component was found
if (typeof Component === 'string') {
console.warn(`Component "${props.componentName}" not found`);
return h('div', `Component "${props.componentName}" not registered`);
}
return h(Component, { message: 'Hello from dynamic component' });
};
}
});
// Usage with component registry
const app = createApp({
components: {
'my-button': MyButtonComponent,
'my-input': MyInputComponent,
'my-modal': MyModalComponent
},
setup() {
const currentComponent = ref('my-button');
return () => {
const Component = resolveComponent(currentComponent.value);
return h(Component);
};
}
});
// Conditional component rendering
const ConditionalRenderer = defineComponent({
props: {
showAdvanced: Boolean
},
setup(props) {
return () => {
const componentName = props.showAdvanced ? 'advanced-form' : 'simple-form';
const Component = resolveComponent(componentName);
return typeof Component !== 'string'
? h(Component)
: h('div', 'Component not available');
};
}
});Resolve registered directives by name at runtime.
/**
* Resolves a globally registered directive by name
* @param name - Directive name to resolve (without v- prefix)
* @returns Resolved directive or undefined if not found
*/
function resolveDirective(name: string): Directive | undefined;Usage Examples:
import { defineComponent, resolveDirective, withDirectives, h } from "@vue/runtime-core";
// Dynamic directive application
const DynamicDirectiveExample = defineComponent({
props: {
directiveName: String,
directiveValue: String
},
setup(props) {
return () => {
const element = h('input', { type: 'text' });
if (props.directiveName) {
const directive = resolveDirective(props.directiveName);
if (directive) {
return withDirectives(element, [
[directive, props.directiveValue]
]);
}
}
return element;
};
}
});
// Conditional directive usage
const ConditionalDirective = defineComponent({
props: {
enableTooltip: Boolean,
tooltipText: String
},
setup(props) {
return () => {
let element = h('button', 'Hover me');
if (props.enableTooltip) {
const tooltipDirective = resolveDirective('tooltip');
if (tooltipDirective) {
element = withDirectives(element, [
[tooltipDirective, props.tooltipText]
]);
}
}
return element;
};
}
});
// Multiple directive resolution
const MultiDirectiveComponent = defineComponent({
setup() {
const directives = ['focus', 'tooltip', 'ripple'];
return () => {
const resolvedDirectives = directives
.map(name => ({ name, directive: resolveDirective(name) }))
.filter(item => item.directive);
let element = h('button', 'Multi-directive button');
if (resolvedDirectives.length > 0) {
const directiveBindings = resolvedDirectives.map(({ directive }) => [
directive,
true // Simple binding value
]);
element = withDirectives(element, directiveBindings);
}
return element;
};
}
});Resolve components dynamically with support for various input types.
/**
* Resolves dynamic component reference to actual component
* @param component - Component reference (string, component object, or function)
* @returns Resolved component type
*/
function resolveDynamicComponent(component: unknown): VNodeTypes;Usage Examples:
import { defineComponent, resolveDynamicComponent, h } from "@vue/runtime-core";
// Flexible component resolution
const FlexibleRenderer = defineComponent({
props: {
component: [String, Object, Function]
},
setup(props) {
return () => {
const resolvedComponent = resolveDynamicComponent(props.component);
return h(resolvedComponent);
};
}
});
// Component factory pattern
const ComponentFactory = defineComponent({
props: {
type: { type: String, required: true },
config: Object
},
setup(props) {
const componentMap: Record<string, any> = {
'button': () => import('./ButtonComponent.vue'),
'input': () => import('./InputComponent.vue'),
'select': () => import('./SelectComponent.vue')
};
return () => {
const componentSpec = componentMap[props.type];
const resolvedComponent = resolveDynamicComponent(componentSpec);
return h(resolvedComponent, props.config);
};
}
});
// Plugin-based component resolution
const PluginRenderer = defineComponent({
props: {
pluginName: String,
componentName: String
},
setup(props) {
return () => {
// Resolve from plugin registry
const pluginComponent = window.plugins?.[props.pluginName]?.[props.componentName];
const resolvedComponent = resolveDynamicComponent(pluginComponent || 'div');
return h(resolvedComponent);
};
}
});// Asset resolution with fallbacks
function useComponentWithFallback(primaryName: string, fallbackName: string) {
return computed(() => {
const primary = resolveComponent(primaryName);
if (typeof primary !== 'string') {
return primary;
}
const fallback = resolveComponent(fallbackName);
return typeof fallback !== 'string' ? fallback : 'div';
});
}
// Lazy directive resolution
function useLazyDirective(name: string) {
const directive = ref<Directive | null>(null);
onMounted(() => {
directive.value = resolveDirective(name) || null;
});
return directive;
}
// Dynamic asset registry
class DynamicAssetRegistry {
private components = new Map<string, Component>();
private directives = new Map<string, Directive>();
registerComponent(name: string, component: Component) {
this.components.set(name, component);
}
registerDirective(name: string, directive: Directive) {
this.directives.set(name, directive);
}
resolveComponent(name: string) {
return this.components.get(name) || resolveComponent(name);
}
resolveDirective(name: string) {
return this.directives.get(name) || resolveDirective(name);
}
}
const registry = new DynamicAssetRegistry();
// Theme-based component resolution
function useThemeComponent(baseName: string) {
const theme = inject('theme', 'default');
return computed(() => {
const themedName = `${baseName}-${theme}`;
const themedComponent = resolveComponent(themedName);
// Fallback to base component if themed version not found
if (typeof themedComponent === 'string') {
return resolveComponent(baseName);
}
return themedComponent;
});
}
// Async component resolution
async function resolveAsyncComponent(name: string): Promise<Component | null> {
const syncComponent = resolveComponent(name);
if (typeof syncComponent !== 'string') {
return syncComponent;
}
// Try to load async
try {
const module = await import(`./components/${name}.vue`);
return module.default;
} catch {
return null;
}
}// Safe component resolution with validation
function useSafeComponent(name: string, validator?: (component: any) => boolean) {
return computed(() => {
const component = resolveComponent(name);
if (typeof component === 'string') {
console.warn(`Component "${name}" not found`);
return null;
}
if (validator && !validator(component)) {
console.warn(`Component "${name}" failed validation`);
return null;
}
return component;
});
}
// Directive resolution with type checking
function useSafeDirective(name: string): Ref<Directive | null> {
const directive = ref<Directive | null>(null);
watchEffect(() => {
const resolved = resolveDirective(name);
if (resolved && typeof resolved === 'object') {
directive.value = resolved;
} else {
console.warn(`Directive "v-${name}" not found`);
directive.value = null;
}
});
return directive;
}
// Resolution with dependency tracking
function useTrackedResolution() {
const resolvedComponents = reactive(new Map<string, Component>());
const resolvedDirectives = reactive(new Map<string, Directive>());
const getComponent = (name: string) => {
if (!resolvedComponents.has(name)) {
const component = resolveComponent(name);
if (typeof component !== 'string') {
resolvedComponents.set(name, component);
}
}
return resolvedComponents.get(name);
};
const getDirective = (name: string) => {
if (!resolvedDirectives.has(name)) {
const directive = resolveDirective(name);
if (directive) {
resolvedDirectives.set(name, directive);
}
}
return resolvedDirectives.get(name);
};
return {
getComponent,
getDirective,
resolvedComponents: readonly(resolvedComponents),
resolvedDirectives: readonly(resolvedDirectives)
};
}type ConcreteComponent = ComponentOptions | FunctionalComponent;
type VNodeTypes =
| string
| VNode
| Component
| typeof Text
| typeof Static
| typeof Comment
| typeof Fragment;
interface Directive<T = any, V = any> {
created?(el: T, binding: DirectiveBinding<V>, vnode: VNode, prevVNode: VNode | null): void;
beforeMount?(el: T, binding: DirectiveBinding<V>, vnode: VNode, prevVNode: VNode | null): void;
mounted?(el: T, binding: DirectiveBinding<V>, vnode: VNode, prevVNode: VNode | null): void;
beforeUpdate?(el: T, binding: DirectiveBinding<V>, vnode: VNode, prevVNode: VNode): void;
updated?(el: T, binding: DirectiveBinding<V>, vnode: VNode, prevVNode: VNode): void;
beforeUnmount?(el: T, binding: DirectiveBinding<V>, vnode: VNode, prevVNode: VNode | null): void;
unmounted?(el: T, binding: DirectiveBinding<V>, vnode: VNode, prevVNode: VNode | null): void;
}
interface DirectiveBinding<V = any> {
instance: ComponentPublicInstance | null;
value: V;
oldValue: V | null;
arg?: string;
modifiers: Record<string, boolean>;
dir: ObjectDirective<any, V>;
}
type FunctionalComponent<P = {}, E extends EmitsOptions = {}> = {
(props: P, ctx: Omit<SetupContext<E>, 'expose'>): any;
props?: ComponentPropsOptions<P>;
emits?: E | (keyof E)[];
inheritAttrs?: boolean;
displayName?: string;
};computed to memoize resolution resultsInstall with Tessl CLI
npx tessl i tessl/npm-vue--runtime-core