From 25f82d2c532af5eb61fbca1d0a63c3a8ab4364c4 Mon Sep 17 00:00:00 2001 From: Xicheng Guo Date: Thu, 10 Oct 2024 11:49:17 +0800 Subject: [PATCH] update docs --- src/.vitepress/sidebars/vue.yaml | 39 +++++-- src/frontend/vue/watch/basic.md | 151 +++++++++++++++++++++++-- src/frontend/vue/watch/source-type.md | 13 +++ src/frontend/vue/watch/watch-effect.md | 25 ++++ 4 files changed, 209 insertions(+), 19 deletions(-) create mode 100644 src/frontend/vue/watch/source-type.md create mode 100644 src/frontend/vue/watch/watch-effect.md diff --git a/src/.vitepress/sidebars/vue.yaml b/src/.vitepress/sidebars/vue.yaml index 0f66d7a..300d065 100644 --- a/src/.vitepress/sidebars/vue.yaml +++ b/src/.vitepress/sidebars/vue.yaml @@ -74,15 +74,31 @@ items: - text: 基本示例 link: /frontend/vue/watch/basic#基本示例 - - text: 侦听源数据类型 - link: /frontend/vue/watch/basic#侦听源数据类型 + - text: 回调的触发时机 + link: /frontend/vue/watch/basic#回调的触发时机 items: - - text: ref 或 reactive - link: /frontend/vue/watch/basic#ref-或-reactive - - text: getter 函数 - link: /frontend/vue/watch/basic#getter-函数 - - text: 多个侦听源 - link: /frontend/vue/watch/basic#多个侦听源 + - text: Vue 更新之前触发 + link: /frontend/vue/watch/basic#vue-更新之前触发 + - text: Vue 更新之后触发 + link: /frontend/vue/watch/basic#vue-更新之后触发 + - text: 副作用清理 + link: /frontend/vue/watch/basic#副作用清理 + items: + - text: onCleanup + link: /frontend/vue/watch/basic#oncleanup + - text: onWatcherCleanup + link: /frontend/vue/watch/basic#onwatchercleanup + - text: 停止侦听 + link: /frontend/vue/watch/basic#停止侦听 + - text: 侦听源数据类型 + link: /frontend/vue/watch/source-type + items: + - text: ref 或 reactive + link: /frontend/vue/watch/source-type#ref-或-reactive + - text: getter 函数 + link: /frontend/vue/watch/source-type#getter-函数 + - text: 多个侦听源 + link: /frontend/vue/watch/source-type#多个侦听源 - text: 侦听器的类型 link: /frontend/vue/watch/types items: @@ -92,6 +108,13 @@ link: /frontend/vue/watch/types#即时侦听 - text: 一次性侦听 link: /frontend/vue/watch/types#一次性侦听 + - text: watch vs. watchEffect + link: /frontend/vue/watch/watch-effect + items: + - text: watcheffect 的使用场景 + link: /frontend/vue/watch/watch-effect#watcheffect-的使用场景 + - text: 比较 watch 和 watcheffect + link: /frontend/vue/watch/watch-effect#比较-watch-和-watcheffect - text: 表单 collapsed: true items: diff --git a/src/frontend/vue/watch/basic.md b/src/frontend/vue/watch/basic.md index 662db45..82bb3dd 100644 --- a/src/frontend/vue/watch/basic.md +++ b/src/frontend/vue/watch/basic.md @@ -4,26 +4,155 @@ `watch 函数`在每次响应式状态变化时触发回调函数,可以执行一些“副作用”,例如:更改 DOM 元素、异步请求。 -::: details 查看示例 <<< @/../projects/vue-sandbox/src/components/ComputedAndWatch/WatchBasic.vue + +## 回调的触发时机 + +:::tip +默认情况下,侦听器回调会在父组件更新 (如有) **之后**、所属组件的 DOM 更新**之前**被调用。 +::: + +### Vue 更新之前触发 + +::: warning 注意 +`{ flush: "sync" }` 每当检测到响应式数据变化时,都会立即执行回调函数。 +可以用来监视简单值(例如:布尔值),但应避免在可能多次同步修改的数据源(例如:数组)上使用。 +::: + +::: code-group + +```js [前置刷新] +watch(source, callback, { + flush: "sync", +}); + +watchEffect(callback, { + flush: "sync", +}); +``` + +```js [前置刷新 watchEffect 的等价写法] +import { watchSyncEffect } from "vue"; + +watchSyncEffect(() => { + /* 在响应式数据变化时同步执行 */ +}); +``` + ::: -## 侦听源数据类型 +### Vue 更新之后触发 + +::: code-group + +```js [后置刷新] +watch(source, callback, { + flush: "post", +}); -### ref 或 reactive +watchEffect(callback, { + flush: "post", +}); +``` + +```js [后置刷新 watchEffect 的等价写法] +import { watchPostEffect } from "vue"; + +watchPostEffect(() => { + /* 在 Vue 更新后执行 */ +}); +``` -::: details 查看示例 -<<< @/../projects/vue-sandbox/src/components/ComputedAndWatch/WatchRef.vue ::: -### getter 函数 +## 副作用清理 + +### onCleanup + +```js +watch(id, (newId, oldId, onCleanup) => { + // ... + onCleanup(() => { + // 清理逻辑 + }); +}); -::: details 查看示例 -<<< @/../projects/vue-sandbox/src/components/ComputedAndWatch/WatchGetter.vue +watchEffect((onCleanup) => { + // ... + onCleanup(() => { + // 清理逻辑 + }); +}); +``` + +### onWatcherCleanup + +当侦听器失效并准备重新调用时,`onWatcherCleanup`会在下一次调用之前执行。 + +::: warning 注意 +`onWatcherCleanup` 仅在 `Vue3.5+` 中支持。 +并且必须在 `watch` 或 `watchEffect` 回调函数的同步执行期间调用,不能在异步函数的`await`语句之后调用。 ::: -### 多个侦听源 +```js +import { watch, onWatcherCleanup } from "vue"; + +watch(id, (newId) => { + const controller = new AbortController(); + + fetch(`/api/${newId}`, { signal: controller.signal }).then(() => { + // 回调逻辑 + }); + + onWatcherCleanup(() => { + // 终止过期请求 + controller.abort(); + }); +}); +``` + +## 停止侦听 + +使用**同步**语句创建的侦听器,会自动绑定到宿主组件实例上,并且会在宿主组件卸载时自动停止。因此,在大多数情况下,无需关心怎么停止侦听器。 + +如果使用异步回调创建一个侦听器,就必须手动停止它,以防止内存泄漏。 + +```vue + +``` + +要手动停止一个侦听器,可以调用 `watch` 或 `watchEffect` 返回的函数: + +```js +const unwatch = watchEffect(() => {}); + +// ...当该侦听器不再需要时 +unwatch(); +``` + +:::warning 注意 +需要异步创建侦听器的情况很少,应尽可能选择同步创建。 +如果需要等待一些异步数据,可以使用条件式的侦听逻辑: + +```js +// 需要异步请求得到的数据 +const data = ref(null); + +watchEffect(() => { + if (data.value) { + // 数据加载后执行某些操作... + } +}); +``` -::: details 查看示例 -<<< @/../projects/vue-sandbox/src/components/ComputedAndWatch/WatchArr.vue ::: diff --git a/src/frontend/vue/watch/source-type.md b/src/frontend/vue/watch/source-type.md new file mode 100644 index 0000000..0bdbef1 --- /dev/null +++ b/src/frontend/vue/watch/source-type.md @@ -0,0 +1,13 @@ +# 侦听源数据类型 + +## ref 或 reactive + +<<< @/../projects/vue-sandbox/src/components/ComputedAndWatch/WatchRef.vue + +## getter 函数 + +<<< @/../projects/vue-sandbox/src/components/ComputedAndWatch/WatchGetter.vue + +## 多个侦听源 + +<<< @/../projects/vue-sandbox/src/components/ComputedAndWatch/WatchArr.vue diff --git a/src/frontend/vue/watch/watch-effect.md b/src/frontend/vue/watch/watch-effect.md new file mode 100644 index 0000000..866bcec --- /dev/null +++ b/src/frontend/vue/watch/watch-effect.md @@ -0,0 +1,25 @@ +# watch vs. watchEffect + +## watchEffect 的使用场景 + +`watchEffect` 会自动跟踪回调中的响应式依赖,并在依赖变更时重新运行回调。 + +::: code-group + +<<< @/../projects/vue-sandbox/src/components/ComputedAndWatch/WatchEffect.vue#snippet{js} [watchEffect] + +<<< @/../projects/vue-sandbox/src/components/ComputedAndWatch/WatchEffectWatch.vue#snippet{js} [watchEffect 的等价写法] + +::: + +## 比较 watch 和 watchEffect + +`watch` 只追踪明确侦听的数据源。仅在数据确实改变时才触发回调,因此能更精确地控制回调函数的触发时机。 + +`watchEffect` 会追踪回调中的所有能访问到的响应式依赖。会在副作用发生期间追踪依赖,在有多个依赖时代码会更简洁。 + +如果需要侦听一个嵌套数据结构中的几个属性,使用`watchEffect`可能会比深层侦听更有效,因为它只跟踪回调中被使用的属性,而不是递归地跟踪所有属性。 + +::: tip +`watchEffect` 仅会在同步执行期间追踪依赖。在异步执行期间,只有在第一个`await`正常工作前访问到的属性才会被追踪。 +:::