0
# Reactivity System
1
2
Vue's reactivity system provides fine-grained reactive data structures with automatic dependency tracking. The system enables efficient updates by tracking which computations depend on which reactive data.
3
4
## Capabilities
5
6
### Reactive State Creation
7
8
Create reactive proxies that automatically track dependencies and trigger updates.
9
10
```typescript { .api }
11
/**
12
* Creates a reactive proxy of an object
13
* @param target - Object to make reactive
14
* @returns Reactive proxy of the target object
15
*/
16
function reactive<T extends object>(target: T): T;
17
18
/**
19
* Creates a reactive reference wrapper for primitive values
20
* @param value - Initial value to wrap
21
* @returns Reactive reference object
22
*/
23
function ref<T>(value: T): Ref<T>;
24
25
/**
26
* Creates a read-only proxy that prevents modifications
27
* @param target - Object to make read-only
28
* @returns Read-only proxy of the target
29
*/
30
function readonly<T>(target: T): DeepReadonly<T>;
31
32
/**
33
* Creates a shallow reactive reference that only tracks .value changes
34
* @param value - Initial value
35
* @returns Shallow reactive reference
36
*/
37
function shallowRef<T>(value: T): ShallowRef<T>;
38
39
/**
40
* Creates a shallow reactive proxy that only tracks first-level properties
41
* @param target - Object to make shallow reactive
42
* @returns Shallow reactive proxy
43
*/
44
function shallowReactive<T extends object>(target: T): ShallowReactive<T>;
45
46
/**
47
* Creates a shallow read-only proxy
48
* @param target - Object to make shallow read-only
49
* @returns Shallow read-only proxy
50
*/
51
function shallowReadonly<T extends object>(target: T): Readonly<T>;
52
53
/**
54
* Creates a custom ref with explicit dependency tracking control
55
* @param factory - Factory function that returns get/set functions
56
* @returns Custom reactive reference
57
*/
58
function customRef<T>(factory: CustomRefFactory<T>): Ref<T>;
59
```
60
61
**Usage Examples:**
62
63
```typescript
64
import { reactive, ref, readonly, shallowRef } from "@vue/runtime-core";
65
66
// Reactive object
67
const state = reactive({
68
count: 0,
69
user: { name: "Alice", age: 25 }
70
});
71
72
// Reactive primitive
73
const count = ref(0);
74
count.value = 5; // Triggers reactivity
75
76
// Read-only state
77
const readonlyState = readonly(state);
78
// readonlyState.count = 10; // Error in TypeScript, ignored in runtime
79
80
// Shallow ref for complex objects
81
const complexData = shallowRef({ deep: { nested: { value: 1 } } });
82
complexData.value = { deep: { nested: { value: 2 } } }; // Triggers reactivity
83
complexData.value.deep.nested.value = 3; // Does NOT trigger reactivity
84
85
// Custom ref with debounced updates
86
function useDebouncedRef(value: string, delay = 200) {
87
let timeout: NodeJS.Timeout;
88
return customRef((track, trigger) => ({
89
get() {
90
track();
91
return value;
92
},
93
set(newValue) {
94
clearTimeout(timeout);
95
timeout = setTimeout(() => {
96
value = newValue;
97
trigger();
98
}, delay);
99
}
100
}));
101
}
102
```
103
104
### Reactivity Utilities
105
106
Utility functions for working with reactive data and references.
107
108
```typescript { .api }
109
/**
110
* Returns the inner value if it's a ref, otherwise returns the value itself
111
* @param ref - Value that may or may not be a ref
112
* @returns Unwrapped value
113
*/
114
function unref<T>(ref: MaybeRef<T>): T;
115
116
/**
117
* Creates a ref for a property on a reactive object
118
* @param object - Reactive object
119
* @param key - Property key
120
* @returns Ref linked to the object property
121
*/
122
function toRef<T extends object, K extends keyof T>(object: T, key: K): ToRef<T[K]>;
123
124
/**
125
* Converts all properties of a reactive object to refs
126
* @param object - Reactive object to convert
127
* @returns Object with all properties as refs
128
*/
129
function toRefs<T extends object>(object: T): ToRefs<T>;
130
131
/**
132
* Returns the value of value/ref/getter
133
* @param val - Value, ref, or getter function
134
* @returns Resolved value
135
*/
136
function toValue<T>(val: MaybeRefOrGetter<T>): T;
137
138
/**
139
* Proxies an object to auto-unwrap refs in properties
140
* @param objectWithRefs - Object containing refs
141
* @returns Proxy that auto-unwraps refs
142
*/
143
function proxyRefs<T extends object>(objectWithRefs: T): T;
144
145
/**
146
* Checks if a value is a ref
147
* @param val - Value to check
148
* @returns True if value is a ref
149
*/
150
function isRef<T>(val: Ref<T> | unknown): val is Ref<T>;
151
152
/**
153
* Checks if a value is a reactive proxy
154
* @param val - Value to check
155
* @returns True if value is reactive
156
*/
157
function isProxy(val: unknown): boolean;
158
159
/**
160
* Checks if a value is reactive (not readonly)
161
* @param val - Value to check
162
* @returns True if value is reactive
163
*/
164
function isReactive(val: unknown): boolean;
165
166
/**
167
* Checks if a value is readonly
168
* @param val - Value to check
169
* @returns True if value is readonly
170
*/
171
function isReadonly(val: unknown): boolean;
172
173
/**
174
* Checks if a value is shallow
175
* @param val - Value to check
176
* @returns True if value is shallow
177
*/
178
function isShallow(val: unknown): boolean;
179
180
/**
181
* Marks an object to never be converted to a proxy
182
* @param value - Object to mark as raw
183
* @returns The same object, marked as raw
184
*/
185
function markRaw<T extends object>(value: T): T;
186
187
/**
188
* Returns the raw, original object of a reactive proxy
189
* @param observed - Reactive proxy
190
* @returns Original object
191
*/
192
function toRaw<T>(observed: T): T;
193
194
/**
195
* Manually triggers effects for a shallow ref
196
* @param ref - Shallow ref to trigger
197
*/
198
function triggerRef(ref: ShallowRef): void;
199
```
200
201
**Usage Examples:**
202
203
```typescript
204
import { reactive, ref, toRefs, toRef, unref, isRef } from "@vue/runtime-core";
205
206
const state = reactive({ count: 0, name: "Vue" });
207
208
// Convert object properties to refs
209
const { count, name } = toRefs(state);
210
console.log(count.value); // 0
211
212
// Create ref for single property
213
const countRef = toRef(state, 'count');
214
countRef.value = 5; // Updates state.count
215
216
// Unwrap refs safely
217
function processValue(maybeRef: MaybeRef<number>) {
218
const value = unref(maybeRef);
219
return value * 2;
220
}
221
222
processValue(ref(5)); // 10
223
processValue(10); // 20
224
225
// Check if value is ref
226
if (isRef(count)) {
227
console.log("count is a ref:", count.value);
228
}
229
```
230
231
### Computed Properties
232
233
Create computed values that automatically update when their dependencies change.
234
235
```typescript { .api }
236
/**
237
* Creates a computed ref with getter only
238
* @param getter - Function that returns computed value
239
* @returns Read-only computed ref
240
*/
241
function computed<T>(getter: () => T): ComputedRef<T>;
242
243
/**
244
* Creates a computed ref with getter and setter
245
* @param options - Object with get and set functions
246
* @returns Writable computed ref
247
*/
248
function computed<T>(options: WritableComputedOptions<T>): WritableComputedRef<T>;
249
250
interface WritableComputedOptions<T> {
251
get: () => T;
252
set: (value: T) => void;
253
}
254
```
255
256
**Usage Examples:**
257
258
```typescript
259
import { ref, computed } from "@vue/runtime-core";
260
261
const count = ref(0);
262
263
// Read-only computed
264
const doubled = computed(() => count.value * 2);
265
console.log(doubled.value); // 0
266
count.value = 5;
267
console.log(doubled.value); // 10
268
269
// Writable computed
270
const fullName = ref("");
271
const firstName = ref("John");
272
const lastName = ref("Doe");
273
274
const computedFullName = computed({
275
get: () => `${firstName.value} ${lastName.value}`,
276
set: (value) => {
277
const parts = value.split(' ');
278
firstName.value = parts[0] || '';
279
lastName.value = parts[1] || '';
280
}
281
});
282
283
console.log(computedFullName.value); // "John Doe"
284
computedFullName.value = "Jane Smith";
285
console.log(firstName.value); // "Jane"
286
console.log(lastName.value); // "Smith"
287
```
288
289
### Effect System
290
291
Create and manage reactive effects with fine-grained control over execution.
292
293
```typescript { .api }
294
/**
295
* Creates a reactive effect that runs when dependencies change
296
* @param fn - Effect function to run
297
* @param options - Optional effect configuration
298
* @returns Effect runner function
299
*/
300
function effect(fn: () => void, options?: ReactiveEffectOptions): ReactiveEffectRunner;
301
302
/**
303
* Stops a reactive effect from running
304
* @param runner - Effect runner to stop
305
*/
306
function stop(runner: ReactiveEffectRunner): void;
307
308
/**
309
* Gets the currently running watcher/effect
310
* @returns Current reactive effect or undefined
311
*/
312
function getCurrentWatcher(): ReactiveEffect | undefined;
313
314
/**
315
* Registers a cleanup function for the current watcher
316
* @param fn - Cleanup function to register
317
*/
318
function onWatcherCleanup(fn: () => void): void;
319
320
interface ReactiveEffectOptions {
321
scheduler?: EffectScheduler;
322
scope?: EffectScope;
323
allowRecurse?: boolean;
324
onStop?: () => void;
325
}
326
327
interface ReactiveEffectRunner<T = any> {
328
(): T;
329
effect: ReactiveEffect;
330
}
331
```
332
333
**Usage Examples:**
334
335
```typescript
336
import { ref, effect, stop } from "@vue/runtime-core";
337
338
const count = ref(0);
339
const name = ref("Vue");
340
341
// Basic effect
342
const runner = effect(() => {
343
console.log(`Count is: ${count.value}`);
344
});
345
346
count.value = 1; // Logs: "Count is: 1"
347
348
// Effect with scheduler
349
const scheduledRunner = effect(
350
() => {
351
console.log(`Scheduled: ${name.value}`);
352
},
353
{
354
scheduler: (job) => {
355
// Custom scheduling logic
356
setTimeout(job, 100);
357
}
358
}
359
);
360
361
// Stop an effect
362
stop(runner);
363
count.value = 2; // No longer logs
364
```
365
366
### Effect Scope
367
368
Manage groups of effects with automatic cleanup.
369
370
```typescript { .api }
371
/**
372
* Creates an effect scope for managing multiple effects
373
* @param detached - Whether the scope is detached from parent
374
* @returns New effect scope
375
*/
376
function effectScope(detached?: boolean): EffectScope;
377
378
/**
379
* Gets the current active effect scope
380
* @returns Current effect scope or undefined
381
*/
382
function getCurrentScope(): EffectScope | undefined;
383
384
/**
385
* Registers a dispose callback for the current scope
386
* @param fn - Callback to run when scope is disposed
387
*/
388
function onScopeDispose(fn: () => void): void;
389
390
interface EffectScope {
391
run<T>(fn: () => T): T | undefined;
392
stop(): void;
393
}
394
```
395
396
**Usage Examples:**
397
398
```typescript
399
import { ref, effect, effectScope, onScopeDispose } from "@vue/runtime-core";
400
401
const count = ref(0);
402
403
// Create a scope
404
const scope = effectScope();
405
406
scope.run(() => {
407
// Effects created in this scope will be collected
408
effect(() => {
409
console.log(`Count: ${count.value}`);
410
});
411
412
effect(() => {
413
console.log(`Count doubled: ${count.value * 2}`);
414
});
415
416
// Cleanup when scope is disposed
417
onScopeDispose(() => {
418
console.log("Scope disposed");
419
});
420
});
421
422
count.value = 1; // Both effects run
423
424
// Stop all effects in scope
425
scope.stop(); // Logs: "Scope disposed"
426
count.value = 2; // No effects run
427
```
428
429
## Types
430
431
```typescript { .api }
432
interface Ref<T> {
433
value: T;
434
}
435
436
interface ShallowRef<T> extends Ref<T> {
437
/**
438
* Type differentiator only.
439
* We need this to be in public d.ts but don't want it to show up in IDE
440
* autocomplete, so we use a private Symbol instead.
441
*/
442
[ShallowRefMarker]?: true;
443
}
444
445
interface ComputedRef<T> extends WritableComputedRef<T> {
446
readonly value: T;
447
}
448
449
interface WritableComputedRef<T> extends Ref<T> {
450
readonly effect: ReactiveEffect<T>;
451
}
452
453
type MaybeRef<T> = T | Ref<T>;
454
type MaybeRefOrGetter<T> = T | Ref<T> | (() => T);
455
456
type ToRef<T> = T extends Ref ? T : Ref<T>;
457
458
type ToRefs<T> = {
459
[K in keyof T]: ToRef<T[K]>;
460
};
461
462
type UnwrapRef<T> = T extends ShallowRef<infer V>
463
? V
464
: T extends Ref<infer V>
465
? UnwrapRefSimple<V>
466
: UnwrapRefSimple<T>;
467
468
interface CustomRefFactory<T> {
469
(track: () => void, trigger: () => void): {
470
get: () => T;
471
set: (value: T) => void;
472
};
473
}
474
475
interface ReactiveEffect<T = any> {
476
(): T;
477
_isEffect: true;
478
id: number;
479
active: boolean;
480
raw: () => T;
481
deps: Array<Dep>;
482
options: ReactiveEffectOptions;
483
allowRecurse: boolean;
484
}
485
486
type EffectScheduler = (fn: () => void) => void;
487
488
interface EffectScope {
489
detached: boolean;
490
parent: EffectScope | undefined;
491
scopes: EffectScope[] | undefined;
492
effects: ReactiveEffect[] | undefined;
493
cleanups: (() => void)[] | undefined;
494
495
run<T>(fn: () => T): T | undefined;
496
on(): void;
497
off(): void;
498
stop(fromParent?: boolean): void;
499
}
500
```