0
# Advanced Features
1
2
Effect scopes, app instances, and other advanced composition features for complex state management and application architecture patterns.
3
4
## Capabilities
5
6
### Effect Scopes
7
8
Advanced effect management system for organizing and cleaning up reactive effects in groups.
9
10
```typescript { .api }
11
/**
12
* Creates a new effect scope for organizing effects
13
* @param detached - Whether the scope runs independently of parent scope
14
* @returns Effect scope instance
15
*/
16
function effectScope(detached?: boolean): EffectScope;
17
18
/**
19
* Gets the currently active effect scope
20
* @returns Current effect scope or undefined
21
*/
22
function getCurrentScope(): EffectScope | undefined;
23
24
/**
25
* Registers a disposal callback for the current effect scope
26
* @param fn - Cleanup function to run when scope is disposed
27
*/
28
function onScopeDispose(fn: () => void): void;
29
30
class EffectScope {
31
/**
32
* Runs a function within this effect scope
33
* @param fn - Function to run in scope
34
* @returns Result of the function
35
*/
36
run<T>(fn: () => T): T | undefined;
37
38
/**
39
* Stops and cleans up all effects in this scope
40
*/
41
stop(): void;
42
}
43
```
44
45
**Usage Examples:**
46
47
```typescript
48
import { effectScope, watchEffect, watch, ref, onScopeDispose } from "@vue/composition-api";
49
50
// Basic effect scope usage
51
const scope = effectScope();
52
53
scope.run(() => {
54
const count = ref(0);
55
56
// This watcher will be collected by the scope
57
watchEffect(() => {
58
console.log("Count:", count.value);
59
});
60
61
// Cleanup when scope is disposed
62
onScopeDispose(() => {
63
console.log("Scope disposed");
64
});
65
});
66
67
// Later, stop all effects in the scope
68
scope.stop();
69
70
// Detached scope (won't be stopped when parent stops)
71
const detachedScope = effectScope(true);
72
73
detachedScope.run(() => {
74
// Effects here won't be cleaned up by parent scope
75
const timer = setInterval(() => {
76
console.log("Timer tick");
77
}, 1000);
78
79
onScopeDispose(() => {
80
clearInterval(timer);
81
});
82
});
83
```
84
85
**Advanced Scope Patterns:**
86
87
```typescript
88
import { effectScope, ref, watch, onScopeDispose } from "@vue/composition-api";
89
90
// Scope-based service management
91
class DataService {
92
private scope = effectScope();
93
private data = ref([]);
94
private loading = ref(false);
95
96
constructor(private apiUrl: string) {
97
this.scope.run(() => {
98
// Set up reactive data fetching
99
watch(
100
() => this.apiUrl,
101
async (url) => {
102
this.loading.value = true;
103
try {
104
const response = await fetch(url);
105
this.data.value = await response.json();
106
} finally {
107
this.loading.value = false;
108
}
109
},
110
{ immediate: true }
111
);
112
113
// Cleanup on dispose
114
onScopeDispose(() => {
115
console.log("DataService disposed");
116
});
117
});
118
}
119
120
getData() {
121
return readonly(this.data);
122
}
123
124
isLoading() {
125
return readonly(this.loading);
126
}
127
128
dispose() {
129
this.scope.stop();
130
}
131
}
132
133
// Usage
134
const service = new DataService("/api/users");
135
const users = service.getData();
136
const loading = service.isLoading();
137
138
// Later cleanup
139
service.dispose();
140
```
141
142
### App Instance (Vue 3 Compatibility)
143
144
Vue 3-like app instance creation for better migration compatibility.
145
146
```typescript { .api }
147
/**
148
* Creates a Vue 3-like app instance for compatibility
149
* @param rootComponent - Root component definition
150
* @param rootProps - Props for root component
151
* @returns App instance with Vue 3-like API
152
*/
153
function createApp(rootComponent: any, rootProps?: any): App;
154
155
interface App<T = any> {
156
/**
157
* Mounts the app to a DOM element
158
* @param rootContainer - DOM element or selector
159
* @returns Component instance
160
*/
161
mount(rootContainer: Element | string): T;
162
163
/**
164
* Unmounts the app
165
*/
166
unmount(): void;
167
168
/**
169
* Provides a value for the entire app
170
* @param key - Injection key
171
* @param value - Value to provide
172
* @returns App instance for chaining
173
*/
174
provide<T>(key: InjectionKey<T> | string, value: T): this;
175
176
/**
177
* Registers a global component
178
* @param name - Component name
179
* @param component - Component definition
180
* @returns App instance for chaining
181
*/
182
component(name: string, component: Component): this;
183
184
/**
185
* Registers a global directive
186
* @param name - Directive name
187
* @param directive - Directive definition
188
* @returns App instance for chaining
189
*/
190
directive(name: string, directive: Directive): this;
191
192
/**
193
* Installs a plugin
194
* @param plugin - Plugin to install
195
* @param options - Plugin options
196
* @returns App instance for chaining
197
*/
198
use(plugin: Plugin, ...options: any[]): this;
199
200
/**
201
* Sets a global property
202
* @param key - Property key
203
* @param value - Property value
204
* @returns App instance for chaining
205
*/
206
config: {
207
globalProperties: Record<string, any>;
208
};
209
}
210
```
211
212
**Usage Examples:**
213
214
```typescript
215
import { createApp, defineComponent, ref } from "@vue/composition-api";
216
217
const RootComponent = defineComponent({
218
setup() {
219
const message = ref("Hello Vue 3 App!");
220
221
return {
222
message,
223
};
224
},
225
template: `<h1>{{ message }}</h1>`,
226
});
227
228
// Create and configure app
229
const app = createApp(RootComponent);
230
231
// Global provide
232
app.provide("appName", "My Vue App");
233
234
// Global component
235
app.component("GlobalButton", {
236
template: `<button><slot /></button>`,
237
});
238
239
// Global directive
240
app.directive("focus", {
241
inserted(el) {
242
el.focus();
243
},
244
});
245
246
// Install plugin
247
app.use(SomePlugin, { option: "value" });
248
249
// Mount app
250
const vm = app.mount("#app");
251
252
// Later unmount
253
app.unmount();
254
```
255
256
### Advanced Reactive Utilities
257
258
Advanced reactive programming utilities for complex state management scenarios.
259
260
```typescript { .api }
261
/**
262
* Gets the current scope's Vue instance for internal operations
263
* @returns Vue instance or null
264
*/
265
function getCurrentScopeVM(): ComponentInstance | null;
266
267
/**
268
* Records effect scope for tracking (internal utility)
269
* @param effect - Effect to record
270
* @param scope - Target scope
271
*/
272
function recordEffectScope(effect: ReactiveEffect, scope?: EffectScope): void;
273
```
274
275
**Advanced Reactive Patterns:**
276
277
```typescript
278
import { effectScope, ref, computed, watch, onScopeDispose } from "@vue/composition-api";
279
280
// Advanced store pattern with scoped effects
281
class ReactiveStore<T> {
282
private scope = effectScope();
283
private state = ref<T>({} as T);
284
private computedCache = new Map();
285
private watchers = new Set<() => void>();
286
287
constructor(initialState: T) {
288
this.scope.run(() => {
289
this.state.value = initialState;
290
291
onScopeDispose(() => {
292
this.watchers.forEach(stop => stop());
293
this.computedCache.clear();
294
});
295
});
296
}
297
298
getState(): Readonly<Ref<T>> {
299
return readonly(this.state);
300
}
301
302
setState(updater: (state: T) => T): void {
303
this.state.value = updater(this.state.value);
304
}
305
306
select<R>(selector: (state: T) => R): ComputedRef<R> {
307
const key = selector.toString();
308
309
if (!this.computedCache.has(key)) {
310
const computed = this.scope.run(() =>
311
computed(() => selector(this.state.value))
312
);
313
this.computedCache.set(key, computed);
314
}
315
316
return this.computedCache.get(key);
317
}
318
319
subscribe(callback: (state: T) => void): () => void {
320
const stop = this.scope.run(() =>
321
watch(this.state, callback, { deep: true })
322
);
323
324
if (stop) {
325
this.watchers.add(stop);
326
return () => {
327
stop();
328
this.watchers.delete(stop);
329
};
330
}
331
332
return () => {};
333
}
334
335
destroy(): void {
336
this.scope.stop();
337
}
338
}
339
340
// Usage
341
interface AppState {
342
user: { name: string; id: number } | null;
343
theme: "light" | "dark";
344
notifications: Array<{ id: string; message: string }>;
345
}
346
347
const store = new ReactiveStore<AppState>({
348
user: null,
349
theme: "light",
350
notifications: [],
351
});
352
353
// Select derived state
354
const isAuthenticated = store.select(state => !!state.user);
355
const notificationCount = store.select(state => state.notifications.length);
356
357
// Subscribe to changes
358
const unsubscribe = store.subscribe(state => {
359
console.log("State changed:", state);
360
});
361
362
// Update state
363
store.setState(state => ({
364
...state,
365
user: { name: "Alice", id: 1 },
366
}));
367
```
368
369
### Global Property Registration
370
371
Utilities for registering global properties and methods.
372
373
```typescript { .api }
374
/**
375
* Development warning utility (internal)
376
* @param message - Warning message
377
*/
378
function warn(message: string): void;
379
```
380
381
**Global Registration Patterns:**
382
383
```typescript { .api }
384
import Vue from "vue";
385
import { ref, reactive } from "@vue/composition-api";
386
387
// Extend Vue with global properties
388
declare module "vue/types/vue" {
389
interface Vue {
390
$api: ApiService;
391
$eventBus: EventBus;
392
}
393
}
394
395
// Global API service
396
class ApiService {
397
private baseUrl: string;
398
399
constructor(baseUrl: string) {
400
this.baseUrl = baseUrl;
401
}
402
403
async get<T>(endpoint: string): Promise<T> {
404
const response = await fetch(`${this.baseUrl}${endpoint}`);
405
return response.json();
406
}
407
}
408
409
// Global event bus
410
class EventBus {
411
private events = reactive<Record<string, Array<Function>>>({});
412
413
on(event: string, callback: Function): void {
414
if (!this.events[event]) {
415
this.events[event] = [];
416
}
417
this.events[event].push(callback);
418
}
419
420
emit(event: string, ...args: any[]): void {
421
if (this.events[event]) {
422
this.events[event].forEach(callback => callback(...args));
423
}
424
}
425
426
off(event: string, callback?: Function): void {
427
if (!this.events[event]) return;
428
429
if (callback) {
430
const index = this.events[event].indexOf(callback);
431
if (index > -1) {
432
this.events[event].splice(index, 1);
433
}
434
} else {
435
this.events[event] = [];
436
}
437
}
438
}
439
440
// Install globally
441
Vue.prototype.$api = new ApiService("/api");
442
Vue.prototype.$eventBus = new EventBus();
443
444
// Usage in composition API
445
export default defineComponent({
446
setup() {
447
const instance = getCurrentInstance();
448
const api = instance?.proxy?.$api;
449
const eventBus = instance?.proxy?.$eventBus;
450
451
onMounted(() => {
452
eventBus?.on("refresh", () => {
453
// Handle refresh event
454
});
455
});
456
457
onBeforeUnmount(() => {
458
eventBus?.off("refresh");
459
});
460
461
return {};
462
},
463
});
464
```
465
466
## Types
467
468
```typescript { .api }
469
interface EffectScope {
470
active: boolean;
471
effects: ReactiveEffect[];
472
cleanups: (() => void)[];
473
parent: EffectScope | undefined;
474
475
run<T>(fn: () => T): T | undefined;
476
stop(): void;
477
}
478
479
interface App<T = any> {
480
version: string;
481
config: AppConfig;
482
483
mount(rootContainer: Element | string): T;
484
unmount(): void;
485
provide<T>(key: InjectionKey<T> | string, value: T): this;
486
component(name: string, component?: Component): this | Component;
487
directive(name: string, directive?: Directive): this | Directive;
488
use(plugin: Plugin, ...options: any[]): this;
489
}
490
491
interface AppConfig {
492
errorHandler?: (err: Error, instance: ComponentPublicInstance | null, info: string) => void;
493
warnHandler?: (msg: string, instance: ComponentPublicInstance | null, trace: string) => void;
494
globalProperties: Record<string, any>;
495
isCustomElement?: (tag: string) => boolean;
496
}
497
498
interface ReactiveEffect<T = any> {
499
(): T;
500
_isEffect: true;
501
id: number;
502
active: boolean;
503
raw: () => T;
504
deps: Array<Dep>;
505
options: ReactiveEffectOptions;
506
allowRecurse: boolean;
507
}
508
509
interface ReactiveEffectOptions {
510
lazy?: boolean;
511
scheduler?: (job: ReactiveEffect) => void;
512
onTrack?: (event: DebuggerEvent) => void;
513
onTrigger?: (event: DebuggerEvent) => void;
514
onStop?: () => void;
515
allowRecurse?: boolean;
516
}
517
518
type Dep = Set<ReactiveEffect> & {
519
cleanup: () => void;
520
computed?: ComputedRef<any>;
521
};
522
523
interface DebuggerEvent {
524
effect: ReactiveEffect;
525
target: object;
526
type: TrackOpTypes | TriggerOpTypes;
527
key: any;
528
newValue?: any;
529
oldValue?: any;
530
oldTarget?: Map<any, any> | Set<any>;
531
}
532
```