You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: src/guide/essentials/watchers.md
+138-3Lines changed: 138 additions & 3 deletions
Original file line number
Diff line number
Diff line change
@@ -319,10 +319,145 @@ watchPostEffect(() => {
319
319
})
320
320
```
321
321
322
-
## Side Effect Invalidation **
322
+
</div>
323
+
324
+
<divclass="options-api">
325
+
326
+
## `$watch()`\*
327
+
328
+
It's also possible to imperatively create watchers using the [`$watch()` instance method](http://localhost:3000/api/component-instance.html#watch):
329
+
330
+
```js
331
+
exportdefault {
332
+
created() {
333
+
this.$watch('question', (newQuestion) => {
334
+
// ...
335
+
})
336
+
}
337
+
}
338
+
```
339
+
340
+
This is useful when you need to conditional set up a watcher, or only watch something in response to user interaction. It also allows you to stop the watcher early.
341
+
342
+
</div>
343
+
344
+
## Stopping a Watcher
345
+
346
+
<divclass="options-api">
347
+
348
+
Watchers declared using the `watch` option or the `$watch()` instance method are automatically stopped when the owner component is unmounted, so in most cases you don't need to worry about stopping the watcher yourself.
349
+
350
+
In the rare case where you need to stop a watcher before the owner component unmounts, the `$watch()` API returns a function for that:
323
351
324
-
## Stopping a Watcher **
352
+
```js
353
+
constunwatch=this.$watch('foo', callback)
354
+
355
+
// ...when the watcher is no longer needed:
356
+
unwatch()
357
+
```
358
+
359
+
</div>
360
+
361
+
<divclass="composition-api">
362
+
363
+
Watchers declared synchronously inside `setup()` or `<script setup>` are bound to the owner component instance, and will be automatically stopped when the owner component is unmounted. In most cases, you don't need to worry about stopping the watcher yourself.
364
+
365
+
The key here is that the watcher must be created **synchronously**: if the watcher is created in an async callback, it won't be bound to the owner component and must be stopped manually to avoid memory leaks. Here's an example:
366
+
367
+
```vue
368
+
<script setup>
369
+
import { watchEffect } from 'vue'
370
+
371
+
// this one will be automatically stopped
372
+
watchEffect(() => {})
373
+
374
+
// ...this one will not!
375
+
setTimeout(() => {
376
+
watchEffect(() => {})
377
+
}, 100)
378
+
</script>
379
+
```
380
+
381
+
To manually stop a watcher, use the returned handle function. This works for both `watch` and `watchEffect`:
382
+
383
+
```js
384
+
constunwatch=watchEffect(() => {})
385
+
386
+
// ...later, when no longer needed
387
+
unwatch()
388
+
```
389
+
390
+
Note that there should be very few cases where you need to create watchers asynchronously, and synchronous creation should be preferred whenever possible. If you need to wait for some async data, you can make your watch logic conditional instead:
391
+
392
+
```js
393
+
// data to be loaded asynchronously
394
+
constdata=ref(null)
395
+
396
+
watchEffect(() => {
397
+
if (data.value) {
398
+
// do something when data is loaded
399
+
}
400
+
})
401
+
```
402
+
403
+
</div>
404
+
405
+
<divclass="composition-api">
406
+
407
+
## Side Effect Invalidation \*\*
408
+
409
+
Sometimes the watched effect function will perform asynchronous side effects that need to be cleaned up when it is invalidated (i.e. state changed before the effects can be completed). The effect function receives an `onInvalidate` function that can be used to register an invalidation callback. This invalidation callback is called when:
410
+
411
+
- the effect is about to re-run
412
+
- the watcher is stopped (i.e. when the component is unmounted if `watchEffect` is used inside `setup()` or lifecycle hooks)
413
+
414
+
```js
415
+
watchEffect((onInvalidate) => {
416
+
consttoken=performAsyncOperation(id.value)
417
+
onInvalidate(() => {
418
+
// id has changed or watcher is stopped.
419
+
// invalidate previously pending async operation
420
+
token.cancel()
421
+
})
422
+
})
423
+
```
424
+
425
+
We are registering the invalidation callback via a passed-in function instead of returning it from the callback because the return value is important for async error handling. It is very common for the effect function to be an async function when performing data fetching:
426
+
427
+
```js
428
+
constdata=ref(null)
429
+
watchEffect(async (onInvalidate) => {
430
+
onInvalidate(() => {
431
+
/* ... */
432
+
}) // we register cleanup function before Promise resolves
433
+
data.value=awaitfetchData(props.id)
434
+
})
435
+
```
436
+
437
+
An async function implicitly returns a Promise, but the cleanup function needs to be registered immediately before the Promise resolves. In addition, Vue relies on the returned Promise to automatically handle potential errors in the Promise chain.
438
+
439
+
## Watcher Debugging \*\*
440
+
441
+
The `onTrack` and `onTrigger` options can be used to debug a watcher's behavior.
442
+
443
+
-`onTrack` will be called when a reactive property or ref is tracked as a dependency.
444
+
-`onTrigger` will be called when the watcher callback is triggered by the mutation of a dependency.
445
+
446
+
Both callbacks will receive a debugger event which contains information on the dependency in question. It is recommended to place a `debugger` statement in these callbacks to interactively inspect the dependency:
447
+
448
+
```js
449
+
watchEffect(
450
+
() => {
451
+
/* side effect */
452
+
},
453
+
{
454
+
onTrigger(e) {
455
+
debugger
456
+
}
457
+
}
458
+
)
459
+
```
325
460
326
-
## Watcher Debugging **
461
+
`onTrack` and `onTrigger` only work in development mode.
0 commit comments