0
# Method and Lifecycle Decorators
1
2
Decorators for watchers, event emitters, dependency injection, and lifecycle hooks in Vue components.
3
4
## Capabilities
5
6
### Watch Decorator
7
8
Decorator for reactive data watchers with comprehensive configuration options.
9
10
```typescript { .api }
11
/**
12
* Decorator for reactive data watchers
13
* @param key - The reactive property or path to watch
14
* @param options - Optional watcher configuration
15
* @returns Method decorator
16
*/
17
function Watch(key: string, options?: WatchOptions): MethodDecorator;
18
19
interface WatchOptions {
20
flush?: 'post';
21
deep?: boolean;
22
immediate?: boolean;
23
}
24
25
interface WatchConfig {
26
key: string;
27
handler: WatchCallback;
28
flush?: 'post';
29
deep?: boolean;
30
immediate?: boolean;
31
}
32
```
33
34
**Usage Examples:**
35
36
```typescript
37
import { Component, Setup, Watch } from "vue-facing-decorator";
38
import { ref, reactive } from "vue";
39
40
@Component
41
class WatchComponent {
42
@Setup(() => ref("initial"))
43
message!: string;
44
45
@Setup(() => ref(0))
46
count!: number;
47
48
@Setup(() => reactive({ nested: { value: "deep" } }))
49
deepObject!: { nested: { value: string } };
50
51
// Basic watcher
52
@Watch("message")
53
onMessageChange(newVal: string, oldVal: string) {
54
console.log(`Message changed from "${oldVal}" to "${newVal}"`);
55
}
56
57
// Immediate watcher
58
@Watch("count", { immediate: true })
59
onCountChange(newVal: number, oldVal: number) {
60
console.log(`Count: ${newVal} (was: ${oldVal})`);
61
}
62
63
// Deep watcher for objects
64
@Watch("deepObject", { deep: true })
65
onDeepObjectChange(newVal: any, oldVal: any) {
66
console.log("Deep object changed:", newVal, oldVal);
67
}
68
69
// Post-flush watcher (after DOM updates)
70
@Watch("message", { flush: "post" })
71
onMessageChangePost(newVal: string) {
72
// This runs after DOM updates
73
console.log("DOM updated with new message:", newVal);
74
}
75
76
// Multiple watchers on same property
77
@Watch("count")
78
firstCountWatcher(newVal: number) {
79
console.log("First watcher:", newVal);
80
}
81
82
@Watch("count", { immediate: true })
83
secondCountWatcher(newVal: number) {
84
console.log("Second watcher:", newVal);
85
}
86
}
87
```
88
89
### Emit Decorator
90
91
Decorator for event emission methods with custom event names.
92
93
```typescript { .api }
94
/**
95
* Decorator for event emission methods
96
* @param eventName - Optional custom event name, defaults to method name
97
* @returns Method decorator
98
*/
99
function Emit(eventName?: string): MethodDecorator;
100
101
type EmitConfig = null | string;
102
```
103
104
**Usage Examples:**
105
106
```typescript
107
import { Component, Emit, Setup } from "vue-facing-decorator";
108
import { ref } from "vue";
109
110
@Component
111
class EmitComponent {
112
@Setup(() => ref("Hello"))
113
message!: string;
114
115
// Basic emit - event name matches method name
116
@Emit()
117
clicked() {
118
return { timestamp: Date.now() };
119
}
120
121
// Custom event name
122
@Emit("customEvent")
123
handleAction() {
124
return this.message;
125
}
126
127
// Emit without return value
128
@Emit("simpleEvent")
129
simpleAction() {
130
console.log("Action performed");
131
}
132
133
// Async emit
134
@Emit("asyncEvent")
135
async asyncAction() {
136
await new Promise(resolve => setTimeout(resolve, 1000));
137
return { result: "async complete" };
138
}
139
140
// Emit with parameters
141
@Emit("dataChange")
142
changeData(newData: any) {
143
this.message = newData.message;
144
return newData;
145
}
146
147
// Multiple emits from same method
148
@Emit("start")
149
@Emit("process")
150
complexAction() {
151
return { action: "complex", data: this.message };
152
}
153
}
154
155
// Usage in parent:
156
// <EmitComponent
157
// @clicked="onClicked"
158
// @customEvent="onCustom"
159
// @simpleEvent="onSimple"
160
// @asyncEvent="onAsync"
161
// @dataChange="onDataChange"
162
// />
163
```
164
165
### Provide Decorator
166
167
Decorator for providing reactive data to descendant components.
168
169
```typescript { .api }
170
/**
171
* Decorator for providing reactive data to descendant components
172
* @param key - Optional provide key, defaults to property name
173
* @returns Property decorator
174
*/
175
function Provide(key?: string): PropertyDecorator;
176
177
type ProvideConfig = null | string;
178
```
179
180
**Usage Examples:**
181
182
```typescript
183
import { Component, Provide, Setup } from "vue-facing-decorator";
184
import { ref, reactive } from "vue";
185
186
@Component
187
class ProvideComponent {
188
// Provide with default key (property name)
189
@Provide()
190
@Setup(() => ref("provided value"))
191
sharedData!: string;
192
193
// Provide with custom key
194
@Provide("customKey")
195
@Setup(() => reactive({ theme: "dark", lang: "en" }))
196
appConfig!: { theme: string; lang: string };
197
198
// Provide computed value
199
@Provide("computedValue")
200
@Setup(() => computed(() => `Config: ${this.appConfig.theme}/${this.appConfig.lang}`))
201
configSummary!: string;
202
203
// Provide method
204
@Provide()
205
updateTheme(newTheme: string) {
206
this.appConfig.theme = newTheme;
207
}
208
209
// Provide static value
210
@Provide("staticKey")
211
staticValue = "never changes";
212
}
213
```
214
215
### Inject Decorator
216
217
Decorator for injecting provided data from ancestor components.
218
219
```typescript { .api }
220
/**
221
* Decorator for injecting provided data from ancestor components
222
* @param config - Optional injection configuration
223
* @returns Property decorator
224
*/
225
function Inject(config?: InjectConfig): PropertyDecorator;
226
227
interface InjectConfig {
228
from?: string | symbol | Symbol | InjectionKey<any>;
229
default?: any;
230
}
231
```
232
233
**Usage Examples:**
234
235
```typescript
236
import { Component, Inject } from "vue-facing-decorator";
237
import { InjectionKey } from "vue";
238
239
// Define injection keys for type safety
240
const ThemeKey: InjectionKey<string> = Symbol('theme');
241
const ConfigKey: InjectionKey<{theme: string, lang: string}> = Symbol('config');
242
243
@Component
244
class InjectComponent {
245
// Basic inject - uses property name as key
246
@Inject()
247
sharedData!: string;
248
249
// Inject with custom key
250
@Inject({ from: "customKey" })
251
appConfig!: { theme: string; lang: string };
252
253
// Inject with default value
254
@Inject({ from: "maybeUndefined", default: "default value" })
255
optionalValue!: string;
256
257
// Inject with symbol key for type safety
258
@Inject({ from: ThemeKey })
259
theme!: string;
260
261
@Inject({ from: ConfigKey, default: () => ({ theme: "light", lang: "en" }) })
262
config!: { theme: string; lang: string };
263
264
// Inject method
265
@Inject({ from: "updateTheme" })
266
updateTheme!: (theme: string) => void;
267
268
mounted() {
269
console.log("Injected data:", this.sharedData);
270
console.log("App config:", this.appConfig);
271
272
// Call injected method
273
if (this.updateTheme) {
274
this.updateTheme("light");
275
}
276
}
277
}
278
```
279
280
### Hook Decorator
281
282
Decorator for explicitly marking methods as lifecycle hooks.
283
284
```typescript { .api }
285
/**
286
* Decorator for explicitly marking methods as lifecycle hooks
287
* @returns Method decorator
288
*/
289
function Hook(): MethodDecorator;
290
291
type HookConfig = null;
292
```
293
294
**Usage Examples:**
295
296
```typescript
297
import { Component, Hook, Setup } from "vue-facing-decorator";
298
import { ref } from "vue";
299
300
@Component
301
class HookComponent {
302
@Setup(() => ref("component data"))
303
data!: string;
304
305
// Explicit hook decoration (optional - hooks are auto-detected by name)
306
@Hook()
307
mounted() {
308
console.log("Component mounted");
309
}
310
311
@Hook()
312
beforeUnmount() {
313
console.log("About to unmount");
314
}
315
316
// Regular lifecycle hooks (auto-detected, no decorator needed)
317
created() {
318
console.log("Component created");
319
}
320
321
updated() {
322
console.log("Component updated");
323
}
324
325
// Custom method that happens to have hook name but isn't a hook
326
@Hook()
327
customMounted() {
328
// This will be treated as a regular method, not a lifecycle hook
329
console.log("Custom method with hook-like name");
330
}
331
}
332
```
333
334
## Lifecycle Hook Support
335
336
All Vue 3 lifecycle hooks are automatically supported by name:
337
338
```typescript { .api }
339
// Vue 3 Lifecycle Hooks (all supported automatically)
340
interface LifecycleHooks {
341
beforeCreate?(): void;
342
created?(): void;
343
beforeMount?(): void;
344
mounted?(): void;
345
beforeUpdate?(): void;
346
updated?(): void;
347
activated?(): void;
348
deactivated?(): void;
349
beforeUnmount?(): void;
350
unmounted?(): void;
351
renderTracked?(): void;
352
renderTriggered?(): void;
353
errorCaptured?(): void;
354
serverPrefetch?(): void;
355
render?(): VNode;
356
}
357
358
// Legacy Vue 2 hooks (still supported)
359
interface LegacyHooks {
360
beforeDestroy?(): void;
361
destroyed?(): void;
362
}
363
```
364
365
**Complete Lifecycle Example:**
366
367
```typescript
368
import { Component, Setup, Watch, Emit, Provide, Inject } from "vue-facing-decorator";
369
import { ref } from "vue";
370
371
@Component
372
class CompleteLifecycleComponent {
373
@Setup(() => ref(0))
374
counter!: number;
375
376
@Provide()
377
@Setup(() => ref("provided from parent"))
378
sharedValue!: string;
379
380
@Inject({ from: "parentData", default: "no parent" })
381
parentData!: string;
382
383
// All lifecycle hooks
384
beforeCreate() {
385
console.log("Before create - component instance being created");
386
}
387
388
created() {
389
console.log("Created - component instance created");
390
}
391
392
beforeMount() {
393
console.log("Before mount - about to mount");
394
}
395
396
mounted() {
397
console.log("Mounted - component mounted to DOM");
398
}
399
400
beforeUpdate() {
401
console.log("Before update - reactive data changed, about to update");
402
}
403
404
updated() {
405
console.log("Updated - DOM updated");
406
}
407
408
beforeUnmount() {
409
console.log("Before unmount - about to unmount");
410
}
411
412
unmounted() {
413
console.log("Unmounted - component unmounted");
414
}
415
416
// Watcher
417
@Watch("counter")
418
onCounterChange(newVal: number, oldVal: number) {
419
this.counterChanged(newVal, oldVal);
420
}
421
422
// Emitter
423
@Emit("counterChanged")
424
counterChanged(newVal: number, oldVal: number) {
425
return { newVal, oldVal, timestamp: Date.now() };
426
}
427
428
increment() {
429
this.counter++;
430
}
431
}
432
```