0
# Component Utilities
1
2
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.
3
4
## Capabilities
5
6
### Component Definition
7
8
Enhanced component definition with better TypeScript inference and composition API support.
9
10
```typescript { .api }
11
/**
12
* Defines a component with enhanced type inference for the composition API
13
* @param setup - Setup function or component options with setup
14
* @returns Component options with proper typing
15
*/
16
function defineComponent<Props, RawBindings = object>(
17
setup: SetupFunction<Props, RawBindings>
18
): ComponentOptions<Vue>;
19
20
/**
21
* Defines a component with options object
22
* @param options - Component options including setup function
23
* @returns Component options with proper typing
24
*/
25
function defineComponent<
26
Props = {},
27
RawBindings = {},
28
D = {},
29
C extends ComputedOptions = {},
30
M extends MethodOptions = {}
31
>(options: ComponentOptionsWithoutProps<Props, RawBindings, D, C, M>): ComponentOptions<Vue>;
32
33
/**
34
* Defines an async component with lazy loading
35
* @param source - Async component loader function
36
* @returns Async component definition
37
*/
38
function defineAsyncComponent<T extends Component = any>(
39
source: AsyncComponentLoader<T>
40
): T;
41
42
/**
43
* Defines an async component with options
44
* @param options - Async component options
45
* @returns Async component definition
46
*/
47
function defineAsyncComponent<T extends Component = any>(
48
options: AsyncComponentOptions<T>
49
): T;
50
51
types SetupFunction<Props, RawBindings> = (
52
props: Readonly<Props>,
53
ctx: SetupContext
54
) => RawBindings | (() => VNode | null) | void;
55
```
56
57
**Usage Examples:**
58
59
```typescript
60
import { defineComponent, ref, computed } from "@vue/composition-api";
61
62
// Simple setup function
63
const MyComponent = defineComponent({
64
props: {
65
message: {
66
type: String,
67
required: true,
68
},
69
count: {
70
type: Number,
71
default: 0,
72
},
73
},
74
setup(props, { emit }) {
75
const localCount = ref(props.count);
76
77
const doubleCount = computed(() => localCount.value * 2);
78
79
const increment = () => {
80
localCount.value++;
81
emit("update:count", localCount.value);
82
};
83
84
return {
85
localCount,
86
doubleCount,
87
increment,
88
};
89
},
90
});
91
92
// Component with render function
93
const RenderComponent = defineComponent({
94
props: {
95
tag: {
96
type: String,
97
default: "div",
98
},
99
},
100
setup(props, { slots }) {
101
return () => {
102
const Tag = props.tag;
103
return <Tag>{slots.default?.()}</Tag>;
104
};
105
},
106
});
107
108
// Async component
109
const AsyncComponent = defineAsyncComponent(() => import("./HeavyComponent.vue"));
110
111
// Async component with options
112
const AsyncComponentWithOptions = defineAsyncComponent({
113
loader: () => import("./HeavyComponent.vue"),
114
loadingComponent: LoadingSpinner,
115
errorComponent: ErrorDisplay,
116
delay: 200,
117
timeout: 3000,
118
});
119
```
120
121
### Instance Context
122
123
Access to the current component instance and setup context utilities.
124
125
```typescript { .api }
126
/**
127
* Gets the current component instance (only available during setup)
128
* @returns Current component instance or null
129
*/
130
function getCurrentInstance(): ComponentInternalInstance | null;
131
132
/**
133
* Accesses component attributes in setup function
134
* @returns Component attrs object
135
*/
136
function useAttrs(): SetupContext["attrs"];
137
138
/**
139
* Accesses component slots in setup function
140
* @returns Component slots object
141
*/
142
function useSlots(): SetupContext["slots"];
143
144
interface ComponentInternalInstance {
145
proxy: ComponentInstance | null;
146
setupState: Record<string, any>;
147
ctx: Record<string, any>;
148
scope: EffectScope;
149
}
150
151
interface SetupContext<E extends EmitsOptions = {}> {
152
attrs: Record<string, any>;
153
slots: Slots;
154
emit: EmitFn<E>;
155
}
156
```
157
158
**Usage Examples:**
159
160
```typescript
161
import { defineComponent, getCurrentInstance, useAttrs, useSlots } from "@vue/composition-api";
162
163
export default defineComponent({
164
setup() {
165
// Get current instance
166
const instance = getCurrentInstance();
167
168
if (instance) {
169
console.log("Component name:", instance.proxy?.$options.name);
170
}
171
172
// Access attrs reactively
173
const attrs = useAttrs();
174
175
// Access slots
176
const slots = useSlots();
177
178
const hasDefaultSlot = computed(() => !!slots.default);
179
const hasHeaderSlot = computed(() => !!slots.header);
180
181
return {
182
attrs,
183
hasDefaultSlot,
184
hasHeaderSlot,
185
};
186
},
187
});
188
```
189
190
### DOM and Timing Utilities
191
192
Utilities for DOM manipulation timing and element creation.
193
194
```typescript { .api }
195
/**
196
* Defers callback execution to the next DOM update cycle
197
* @param callback - Optional callback to execute
198
* @returns Promise that resolves after next DOM update
199
*/
200
function nextTick(callback?: () => void): Promise<void>;
201
202
/**
203
* Creates virtual DOM elements (alias for createElement)
204
* @param tag - Element tag or component
205
* @param props - Element props
206
* @param children - Element children
207
* @returns Virtual DOM node
208
*/
209
function h(
210
tag: string | Component,
211
props?: Record<string, any> | null,
212
children?: any
213
): VNode;
214
215
/**
216
* Development warning utility
217
* @param message - Warning message
218
* @param args - Additional arguments
219
*/
220
function warn(message: string, ...args: any[]): void;
221
```
222
223
**Usage Examples:**
224
225
```typescript
226
import { defineComponent, ref, nextTick, h } from "@vue/composition-api";
227
228
export default defineComponent({
229
setup() {
230
const showMessage = ref(false);
231
const messageEl = ref<HTMLElement | null>(null);
232
233
const toggleMessage = async () => {
234
showMessage.value = !showMessage.value;
235
236
// Wait for DOM update
237
await nextTick();
238
239
if (showMessage.value && messageEl.value) {
240
messageEl.value.focus();
241
}
242
};
243
244
// Using h() for render function
245
return () => h("div", [
246
h("button", { onClick: toggleMessage }, "Toggle Message"),
247
showMessage.value
248
? h("p", { ref: messageEl, tabindex: -1 }, "Hello World!")
249
: null,
250
]);
251
},
252
});
253
254
// NextTick with callback
255
export default defineComponent({
256
setup() {
257
const updateData = () => {
258
// Update reactive data
259
someData.value = newValue;
260
261
// Execute after DOM update
262
nextTick(() => {
263
console.log("DOM has been updated");
264
document.getElementById("updated-element")?.scrollIntoView();
265
});
266
};
267
268
return {
269
updateData,
270
};
271
},
272
});
273
```
274
275
### CSS Modules Integration
276
277
Access to CSS modules within composition API setup functions.
278
279
```typescript { .api }
280
/**
281
* Accesses CSS module classes
282
* @param name - Optional CSS module name
283
* @returns Object mapping class names to CSS module classes
284
*/
285
function useCssModule(name?: string): Record<string, string>;
286
287
/**
288
* Alternative spelling for useCssModule
289
* @param name - Optional CSS module name
290
* @returns Object mapping class names to CSS module classes
291
*/
292
function useCSSModule(name?: string): Record<string, string>;
293
```
294
295
**Usage Examples:**
296
297
```typescript
298
import { defineComponent, useCssModule } from "@vue/composition-api";
299
300
export default defineComponent({
301
setup() {
302
// Access default CSS module
303
const styles = useCssModule();
304
305
// Access named CSS module
306
const buttonStyles = useCssModule("button");
307
308
return {
309
styles,
310
buttonStyles,
311
};
312
},
313
314
template: `
315
<div :class="styles.container">
316
<button :class="buttonStyles.primary">
317
Click me
318
</button>
319
</div>
320
`,
321
});
322
323
// In component with CSS modules
324
// styles.module.css
325
/*
326
.container {
327
padding: 1rem;
328
}
329
*/
330
331
// button.module.css
332
/*
333
.primary {
334
background: blue;
335
color: white;
336
}
337
*/
338
```
339
340
### Props Type Utilities
341
342
Utilities for working with component props and type extraction.
343
344
```typescript { .api }
345
/**
346
* Extracts prop types from props definition
347
*/
348
type ExtractPropTypes<O> = O extends object
349
? { [K in keyof O]?: O[K] extends PropOptions<infer T> ? T : any }
350
: {};
351
352
/**
353
* Extracts default prop types from props definition
354
*/
355
type ExtractDefaultPropTypes<O> = O extends object
356
? { [K in keyof O]: O[K] extends { default: infer D } ? D : never }
357
: {};
358
359
interface PropOptions<T = any> {
360
type?: PropType<T> | true | null;
361
required?: boolean;
362
default?: T | null | undefined | (() => T | null | undefined);
363
validator?(value: T): boolean;
364
}
365
366
type PropType<T> = { new (...args: any[]): T & object } | { (): T } | PropType<T>[];
367
```
368
369
**Usage Examples:**
370
371
```typescript
372
import { defineComponent, PropType } from "@vue/composition-api";
373
374
interface User {
375
id: number;
376
name: string;
377
email: string;
378
}
379
380
export default defineComponent({
381
props: {
382
user: {
383
type: Object as PropType<User>,
384
required: true,
385
},
386
theme: {
387
type: String as PropType<"light" | "dark">,
388
default: "light",
389
},
390
count: {
391
type: Number,
392
default: 0,
393
validator: (value: number) => value >= 0,
394
},
395
},
396
setup(props) {
397
// props are fully typed based on the props definition
398
console.log(props.user.name); // TypeScript knows this is string
399
console.log(props.theme); // TypeScript knows this is "light" | "dark"
400
console.log(props.count); // TypeScript knows this is number
401
402
return {};
403
},
404
});
405
406
// Type extraction utility
407
type MyComponentProps = ExtractPropTypes<{
408
user: { type: PropType<User>; required: true };
409
theme: { type: PropType<"light" | "dark">; default: "light" };
410
count: { type: Number; default: 0 };
411
}>;
412
413
// MyComponentProps is:
414
// {
415
// user?: User;
416
// theme?: "light" | "dark";
417
// count?: number;
418
// }
419
```
420
421
### Event Handling
422
423
Enhanced event handling with TypeScript support.
424
425
```typescript { .api }
426
type EmitsOptions = ObjectEmitsOptions | string[];
427
428
type ObjectEmitsOptions = Record<
429
string,
430
((...args: any[]) => any) | null
431
>;
432
433
type EmitFn<
434
Options = ObjectEmitsOptions,
435
Event extends keyof Options = keyof Options
436
> = Options extends Array<infer V>
437
? (event: V, ...args: any[]) => void
438
: {} extends Options
439
? (event: string, ...args: any[]) => void
440
: Options extends ObjectEmitsOptions
441
? {
442
[K in Event]: Options[K] extends (...args: infer Args) => any
443
? (event: K, ...args: Args) => void
444
: (event: K, ...args: any[]) => void;
445
}[Event]
446
: never;
447
```
448
449
**Usage Examples:**
450
451
```typescript
452
import { defineComponent } from "@vue/composition-api";
453
454
export default defineComponent({
455
emits: {
456
// Typed event with validation
457
click: (payload: { x: number; y: number }) => {
458
return payload.x >= 0 && payload.y >= 0;
459
},
460
// Simple event
461
close: null,
462
// Event with multiple arguments
463
update: (id: string, value: any) => true,
464
},
465
setup(props, { emit }) {
466
const handleClick = (event: MouseEvent) => {
467
// emit is fully typed based on emits definition
468
emit("click", { x: event.clientX, y: event.clientY });
469
};
470
471
const handleClose = () => {
472
emit("close");
473
};
474
475
const handleUpdate = () => {
476
emit("update", "item-id", { data: "new value" });
477
};
478
479
return {
480
handleClick,
481
handleClose,
482
handleUpdate,
483
};
484
},
485
});
486
```
487
488
## Types
489
490
```typescript { .api }
491
interface ComponentOptions<V extends Vue> {
492
name?: string;
493
props?: ComponentPropsOptions<Data>;
494
setup?: SetupFunction<Data, Data>;
495
data?: ComponentOptionsData<V>;
496
computed?: Accessors<V>;
497
methods?: Methods<V>;
498
watch?: ComponentWatchOptions<V>;
499
// ... other Vue component options
500
}
501
502
interface ComponentInternalInstance {
503
proxy: ComponentInstance | null;
504
setupState: Record<string, any>;
505
ctx: Record<string, any>;
506
scope: EffectScope;
507
}
508
509
interface SetupContext<E extends EmitsOptions = {}> {
510
attrs: Record<string, any>;
511
slots: Slots;
512
emit: EmitFn<E>;
513
}
514
515
type Slots = Readonly<InternalSlots>;
516
517
interface InternalSlots {
518
[name: string]: Slot | undefined;
519
}
520
521
type Slot = (...args: any[]) => VNode[];
522
523
type AsyncComponentLoader<T = any> = () => Promise<T>;
524
525
interface AsyncComponentOptions<T = any> {
526
loader: AsyncComponentLoader<T>;
527
loadingComponent?: Component;
528
errorComponent?: Component;
529
delay?: number;
530
timeout?: number;
531
suspensible?: boolean;
532
onError?(error: Error, retry: () => void, fail: () => void, attempts: number): any;
533
}
534
535
type Component<
536
Props = any,
537
RawBindings = any,
538
D = any,
539
C extends ComputedOptions = ComputedOptions,
540
M extends MethodOptions = MethodOptions
541
> = ComponentOptions<Vue, Props, RawBindings, D, C, M> | ComponentConstructor<Vue>;
542
```