= (
value: T,
@@ -363,6 +409,13 @@
onTrigger?: (event: DebuggerEvent) => void
once?: boolean // 初期値: false
}
+
+ interface WatchHandle {
+ (): void // 呼び出し可能、`stop` と同様
+ pause: () => void
+ resume: () => void
+ stop: () => void
+ }
```
> 読みやすくするため、型は単純化されています。
@@ -472,6 +525,21 @@
stop()
```
+ ウォッチャーの一時停止 / 再開:
+
+ ```js
+ const { stop, pause, resume } = watchEffect(() => {})
+
+ // ウォッチャーを一時停止する
+ pause()
+
+ // あとで再開する
+ resume()
+
+ // 停止する
+ stop()
+ ```
+
副作用のクリーンアップ:
```js
@@ -484,7 +552,45 @@
})
```
+ 3.5+ での副作用のクリーンアップ:
+
+ ```js
+ import { onWatcherCleanup } from 'vue'
+
+ watch(id, async (newId) => {
+ const { response, cancel } = doAsyncWork(newId)
+ onWatcherCleanup(cancel)
+ data.value = await response
+ })
+ ```
+
- **参照**
- [ガイド - ウォッチャー](/guide/essentials/watchers)
- [ガイド - ウォッチャーのデバッグ](/guide/extras/reactivity-in-depth#watcher-debugging)
+
+## onWatcherCleanup() {#onwatchercleanup}
+
+現在のウォッチャーが再実行される直前に実行されるクリーンアップ関数を登録します。`watchEffect` エフェクト関数または `watch` コールバック関数の同期実行中にのみ呼び出すことができます(つまり、非同期関数内の `await` ステートメントの後には呼び出すことができません)。
+
+- **型**
+
+ ```ts
+ function onWatcherCleanup(
+ cleanupFn: () => void,
+ failSilently?: boolean
+ ): void
+ ```
+
+- **例**
+
+ ```ts
+ import { watch, onWatcherCleanup } from 'vue'
+
+ watch(id, (newId) => {
+ const { response, cancel } = doAsyncWork(newId)
+ // `id` が変更されると `cancel` が呼ばれ、
+ // 前のリクエストがまだ完了していない場合はキャンセルされます
+ onWatcherCleanup(cancel)
+ })
+ ```
diff --git a/src/guide/essentials/watchers.md b/src/guide/essentials/watchers.md
index ee0c68f7c..f01fa1e09 100644
--- a/src/guide/essentials/watchers.md
+++ b/src/guide/essentials/watchers.md
@@ -362,6 +362,128 @@ watchEffect(async () => {
+## 副作用のクリーンアップ {#side-effect-cleanup}
+
+ウォッチャー内で、例えば非同期リクエストなど、副作用が伴う場合があります:
+
+
+
+```js
+watch(id, (newId) => {
+ fetch(`/api/${newId}`).then(() => {
+ // コールバックのロジック
+ })
+})
+```
+
+
+
+
+```js
+export default {
+ watch: {
+ id(newId) {
+ fetch(`/api/${newId}`).then(() => {
+ // コールバックのロジック
+ })
+ }
+ }
+}
+```
+
+
+
+しかし、リクエストが完了する前に `id` が変更されたらどうなるでしょう?前のリクエストが完了したときに、すでに古くなった ID 値でコールバックが発火してしまいます。理想的には、`id` が新しい値に変更されたときに古いリクエストをキャンセルしたいです。
+
+[`onWatcherCleanup()`](/api/reactivity-core#onwatchercleanup) API を使って、ウォッチャーが無効になり再実行される直前に呼び出されるクリーンアップ関数を登録することができます:
+
+
+
+```js {10-13}
+import { watch, onWatcherCleanup } from 'vue'
+
+watch(id, (newId) => {
+ const controller = new AbortController()
+
+ fetch(`/api/${newId}`, { signal: controller.signal }).then(() => {
+ // コールバックのロジック
+ })
+
+ onWatcherCleanup(() => {
+ // 古くなったリクエストを中止する
+ controller.abort()
+ })
+})
+```
+
+
+
+
+```js {12-15}
+import { onWatcherCleanup } from 'vue'
+
+export default {
+ watch: {
+ id(newId) {
+ const controller = new AbortController()
+
+ fetch(`/api/${newId}`, { signal: controller.signal }).then(() => {
+ // コールバックのロジック
+ })
+
+ onWatcherCleanup(() => {
+ // 古くなったリクエストを中止する
+ controller.abort()
+ })
+ }
+ }
+}
+```
+
+
+
+`onWatcherCleanup` は Vue 3.5+ でのみサポートされており、`watchEffect` エフェクト関数または `watch` コールバック関数の同期実行中に呼び出す必要があることに注意してください: 非同期関数の `await` ステートメントの後に呼び出すことはできません。
+
+代わりに、`onCleanup` 関数も第 3 引数としてウォッチャーコールバックに渡され、`watchEffect` エフェクト関数には第 1 引数として渡されます:
+
+
+
+```js
+watch(id, (newId, oldId, onCleanup) => {
+ // ...
+ onCleanup(() => {
+ // クリーンアップのロジック
+ })
+})
+
+watchEffect((onCleanup) => {
+ // ...
+ onCleanup(() => {
+ // クリーンアップのロジック
+ })
+})
+```
+
+
+
+
+```js
+export default {
+ watch: {
+ id(newId, oldId, onCleanup) {
+ // ...
+ onCleanup(() => {
+ // クリーンアップのロジック
+ })
+ }
+ }
+}
+```
+
+
+
+これは 3.5 以前のバージョンで動作します。また、関数の引数で渡される `onCleanup` はウォッチャーインスタンスにバインドされるため、`onWatcherCleanup` の同期的な制約は受けません。
+
## コールバックが実行されるタイミング {#callback-flush-timing}
リアクティブな状態が変更されるとき、Vue コンポーネントの更新と生成されたウォッチャーコールバックを実行します。