Provides Vue 3 Composition API compatibility for Vue 2 applications with reactive state management and lifecycle hooks.
—
Utilities for component definition, setup context access, and Vue 3 compatibility features. These utilities provide enhanced component creation with better TypeScript support and composition API integration.
Enhanced component definition with better TypeScript inference and composition API support.
/**
* Defines a component with enhanced type inference for the composition API
* @param setup - Setup function or component options with setup
* @returns Component options with proper typing
*/
function defineComponent<Props, RawBindings = object>(
setup: SetupFunction<Props, RawBindings>
): ComponentOptions<Vue>;
/**
* Defines a component with options object
* @param options - Component options including setup function
* @returns Component options with proper typing
*/
function defineComponent<
Props = {},
RawBindings = {},
D = {},
C extends ComputedOptions = {},
M extends MethodOptions = {}
>(options: ComponentOptionsWithoutProps<Props, RawBindings, D, C, M>): ComponentOptions<Vue>;
/**
* Defines an async component with lazy loading
* @param source - Async component loader function
* @returns Async component definition
*/
function defineAsyncComponent<T extends Component = any>(
source: AsyncComponentLoader<T>
): T;
/**
* Defines an async component with options
* @param options - Async component options
* @returns Async component definition
*/
function defineAsyncComponent<T extends Component = any>(
options: AsyncComponentOptions<T>
): T;
types SetupFunction<Props, RawBindings> = (
props: Readonly<Props>,
ctx: SetupContext
) => RawBindings | (() => VNode | null) | void;Usage Examples:
import { defineComponent, ref, computed } from "@vue/composition-api";
// Simple setup function
const MyComponent = defineComponent({
props: {
message: {
type: String,
required: true,
},
count: {
type: Number,
default: 0,
},
},
setup(props, { emit }) {
const localCount = ref(props.count);
const doubleCount = computed(() => localCount.value * 2);
const increment = () => {
localCount.value++;
emit("update:count", localCount.value);
};
return {
localCount,
doubleCount,
increment,
};
},
});
// Component with render function
const RenderComponent = defineComponent({
props: {
tag: {
type: String,
default: "div",
},
},
setup(props, { slots }) {
return () => {
const Tag = props.tag;
return <Tag>{slots.default?.()}</Tag>;
};
},
});
// Async component
const AsyncComponent = defineAsyncComponent(() => import("./HeavyComponent.vue"));
// Async component with options
const AsyncComponentWithOptions = defineAsyncComponent({
loader: () => import("./HeavyComponent.vue"),
loadingComponent: LoadingSpinner,
errorComponent: ErrorDisplay,
delay: 200,
timeout: 3000,
});Access to the current component instance and setup context utilities.
/**
* Gets the current component instance (only available during setup)
* @returns Current component instance or null
*/
function getCurrentInstance(): ComponentInternalInstance | null;
/**
* Accesses component attributes in setup function
* @returns Component attrs object
*/
function useAttrs(): SetupContext["attrs"];
/**
* Accesses component slots in setup function
* @returns Component slots object
*/
function useSlots(): SetupContext["slots"];
interface ComponentInternalInstance {
proxy: ComponentInstance | null;
setupState: Record<string, any>;
ctx: Record<string, any>;
scope: EffectScope;
}
interface SetupContext<E extends EmitsOptions = {}> {
attrs: Record<string, any>;
slots: Slots;
emit: EmitFn<E>;
}Usage Examples:
import { defineComponent, getCurrentInstance, useAttrs, useSlots } from "@vue/composition-api";
export default defineComponent({
setup() {
// Get current instance
const instance = getCurrentInstance();
if (instance) {
console.log("Component name:", instance.proxy?.$options.name);
}
// Access attrs reactively
const attrs = useAttrs();
// Access slots
const slots = useSlots();
const hasDefaultSlot = computed(() => !!slots.default);
const hasHeaderSlot = computed(() => !!slots.header);
return {
attrs,
hasDefaultSlot,
hasHeaderSlot,
};
},
});Utilities for DOM manipulation timing and element creation.
/**
* Defers callback execution to the next DOM update cycle
* @param callback - Optional callback to execute
* @returns Promise that resolves after next DOM update
*/
function nextTick(callback?: () => void): Promise<void>;
/**
* Creates virtual DOM elements (alias for createElement)
* @param tag - Element tag or component
* @param props - Element props
* @param children - Element children
* @returns Virtual DOM node
*/
function h(
tag: string | Component,
props?: Record<string, any> | null,
children?: any
): VNode;
/**
* Development warning utility
* @param message - Warning message
* @param args - Additional arguments
*/
function warn(message: string, ...args: any[]): void;Usage Examples:
import { defineComponent, ref, nextTick, h } from "@vue/composition-api";
export default defineComponent({
setup() {
const showMessage = ref(false);
const messageEl = ref<HTMLElement | null>(null);
const toggleMessage = async () => {
showMessage.value = !showMessage.value;
// Wait for DOM update
await nextTick();
if (showMessage.value && messageEl.value) {
messageEl.value.focus();
}
};
// Using h() for render function
return () => h("div", [
h("button", { onClick: toggleMessage }, "Toggle Message"),
showMessage.value
? h("p", { ref: messageEl, tabindex: -1 }, "Hello World!")
: null,
]);
},
});
// NextTick with callback
export default defineComponent({
setup() {
const updateData = () => {
// Update reactive data
someData.value = newValue;
// Execute after DOM update
nextTick(() => {
console.log("DOM has been updated");
document.getElementById("updated-element")?.scrollIntoView();
});
};
return {
updateData,
};
},
});Access to CSS modules within composition API setup functions.
/**
* Accesses CSS module classes
* @param name - Optional CSS module name
* @returns Object mapping class names to CSS module classes
*/
function useCssModule(name?: string): Record<string, string>;
/**
* Alternative spelling for useCssModule
* @param name - Optional CSS module name
* @returns Object mapping class names to CSS module classes
*/
function useCSSModule(name?: string): Record<string, string>;Usage Examples:
import { defineComponent, useCssModule } from "@vue/composition-api";
export default defineComponent({
setup() {
// Access default CSS module
const styles = useCssModule();
// Access named CSS module
const buttonStyles = useCssModule("button");
return {
styles,
buttonStyles,
};
},
template: `
<div :class="styles.container">
<button :class="buttonStyles.primary">
Click me
</button>
</div>
`,
});
// In component with CSS modules
// styles.module.css
/*
.container {
padding: 1rem;
}
*/
// button.module.css
/*
.primary {
background: blue;
color: white;
}
*/Utilities for working with component props and type extraction.
/**
* Extracts prop types from props definition
*/
type ExtractPropTypes<O> = O extends object
? { [K in keyof O]?: O[K] extends PropOptions<infer T> ? T : any }
: {};
/**
* Extracts default prop types from props definition
*/
type ExtractDefaultPropTypes<O> = O extends object
? { [K in keyof O]: O[K] extends { default: infer D } ? D : never }
: {};
interface PropOptions<T = any> {
type?: PropType<T> | true | null;
required?: boolean;
default?: T | null | undefined | (() => T | null | undefined);
validator?(value: T): boolean;
}
type PropType<T> = { new (...args: any[]): T & object } | { (): T } | PropType<T>[];Usage Examples:
import { defineComponent, PropType } from "@vue/composition-api";
interface User {
id: number;
name: string;
email: string;
}
export default defineComponent({
props: {
user: {
type: Object as PropType<User>,
required: true,
},
theme: {
type: String as PropType<"light" | "dark">,
default: "light",
},
count: {
type: Number,
default: 0,
validator: (value: number) => value >= 0,
},
},
setup(props) {
// props are fully typed based on the props definition
console.log(props.user.name); // TypeScript knows this is string
console.log(props.theme); // TypeScript knows this is "light" | "dark"
console.log(props.count); // TypeScript knows this is number
return {};
},
});
// Type extraction utility
type MyComponentProps = ExtractPropTypes<{
user: { type: PropType<User>; required: true };
theme: { type: PropType<"light" | "dark">; default: "light" };
count: { type: Number; default: 0 };
}>;
// MyComponentProps is:
// {
// user?: User;
// theme?: "light" | "dark";
// count?: number;
// }Enhanced event handling with TypeScript support.
type EmitsOptions = ObjectEmitsOptions | string[];
type ObjectEmitsOptions = Record<
string,
((...args: any[]) => any) | null
>;
type EmitFn<
Options = ObjectEmitsOptions,
Event extends keyof Options = keyof Options
> = Options extends Array<infer V>
? (event: V, ...args: any[]) => void
: {} extends Options
? (event: string, ...args: any[]) => void
: Options extends ObjectEmitsOptions
? {
[K in Event]: Options[K] extends (...args: infer Args) => any
? (event: K, ...args: Args) => void
: (event: K, ...args: any[]) => void;
}[Event]
: never;Usage Examples:
import { defineComponent } from "@vue/composition-api";
export default defineComponent({
emits: {
// Typed event with validation
click: (payload: { x: number; y: number }) => {
return payload.x >= 0 && payload.y >= 0;
},
// Simple event
close: null,
// Event with multiple arguments
update: (id: string, value: any) => true,
},
setup(props, { emit }) {
const handleClick = (event: MouseEvent) => {
// emit is fully typed based on emits definition
emit("click", { x: event.clientX, y: event.clientY });
};
const handleClose = () => {
emit("close");
};
const handleUpdate = () => {
emit("update", "item-id", { data: "new value" });
};
return {
handleClick,
handleClose,
handleUpdate,
};
},
});interface ComponentOptions<V extends Vue> {
name?: string;
props?: ComponentPropsOptions<Data>;
setup?: SetupFunction<Data, Data>;
data?: ComponentOptionsData<V>;
computed?: Accessors<V>;
methods?: Methods<V>;
watch?: ComponentWatchOptions<V>;
// ... other Vue component options
}
interface ComponentInternalInstance {
proxy: ComponentInstance | null;
setupState: Record<string, any>;
ctx: Record<string, any>;
scope: EffectScope;
}
interface SetupContext<E extends EmitsOptions = {}> {
attrs: Record<string, any>;
slots: Slots;
emit: EmitFn<E>;
}
type Slots = Readonly<InternalSlots>;
interface InternalSlots {
[name: string]: Slot | undefined;
}
type Slot = (...args: any[]) => VNode[];
type AsyncComponentLoader<T = any> = () => Promise<T>;
interface AsyncComponentOptions<T = any> {
loader: AsyncComponentLoader<T>;
loadingComponent?: Component;
errorComponent?: Component;
delay?: number;
timeout?: number;
suspensible?: boolean;
onError?(error: Error, retry: () => void, fail: () => void, attempts: number): any;
}
type Component<
Props = any,
RawBindings = any,
D = any,
C extends ComputedOptions = ComputedOptions,
M extends MethodOptions = MethodOptions
> = ComponentOptions<Vue, Props, RawBindings, D, C, M> | ComponentConstructor<Vue>;Install with Tessl CLI
npx tessl i tessl/npm-vue--composition-api