💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
>[success] # watchEffect 1. `watchEffect `是一个新的`api`,需要将这个单词拆解来看`watch `和`effect `,**effect全称叫side effect,副作用**。在watch里面进行异步请求,这个请求是随时的不可控的这个就是一种副作用,所以可以理解成`watchEffect`的意思就是在观察(watch)到变化后执行一些操作(effect) * `onTrack `和 `onTrigger `只能在开发模式下工作。可用于调试侦听器的行为。`onTrack`作为依赖项被追踪时被调用。`onTrigger `将在依赖项变更导致副作用被触发时被调用。 * `flush : 'pre' | 'post' | 'sync' `默认是`pre`,`pre` 会在 Vue 组件更新**之前**被调用。这意味着你在侦听器回调中访问的 DOM 将是被 Vue 更新之前的状态,`post`,在侦听器回调中能访问被 Vue 更新**之后**的 DOM,`sync` 是每一步都会触发(实际vue 为了防止使用队列缓冲回调。回调只会添加到队列中一次,即使所监视的值更改了多次。中间值将被跳过,并且不会传递给回调,但`sync`是次次触发) ,具体可[查看的案例参考](https://github.com/vuejs/core/issues/5721) ~~~ watchEffect(() => {}, { flush: 'post', onTrack(e) { debugger }, onTrigger(e) { debugger } }) ~~~ >[danger] ##### 特点 1. `watchEffect` 不需要指定监听的属性,监听是立即执行的(**会默认初始时就会执行第一次, 从而可以收集需要监听的数据**) 2. 调用返回值以停止侦听 3. 传入`watchEffect `函数可以接收一个 `onInvalidate `函数作入参,用来注册清理失效时的回调,失效回调会被触发 条件: * 副作用即将重新执行时 * 侦听器被停止 (如果在 setup() 或生命周期钩子函数中使用了 watchEffect,则在组件卸载时) 4. 调整`watchEffect`的执行时机,`watchEffect`立即执行时候`dom `还没被渲染,可以调整执行时机来获取`dom` * 没有任何指定的属性依然会执行下面案例运行后控制台打印1,`watchEffect `需要先立即执行收集内部需要**被监听的数据** ~~~ <template> <div> </div> </template> <script lang="ts"> import {ref, watchEffect} from 'vue' export default { setup() { const stop = watchEffect(()=>{ console.log(1) }) return { } } } </script> ~~~ * **自动只能收集是属性**虽然会自动收集监听但是,如果是一个`reactive `对象是不会触发,**但是如果是其一个具体属性值可以触发** 第立即执行时候都执行,但第二次改变时候`info`的`watchEffect `没触发 ![](https://img.kancloud.cn/dd/2c/dd2cdcc9f7aac8f9bd033e0a6e7cd273_716x288.png) ~~~ <template> <button @click="changeInfo">change</button> </template> <script> import { watchEffect, ref, reactive } from '@vue/runtime-core' export default { name: 'App', setup() { const test = ref(1) const info = reactive({ name: 'w', hobbies: ['footer'] }) const changeTest = () => {} const changeInfo = () => { info.hobbies[0] = 'sleep' test.value = 12 } watchEffect(() => { console.log(info) }) watchEffect(() => { console.log(info.hobbies[0]) }) watchEffect(() => { console.log(test.value) }) return { test, info, changeInfo, changeTest, } }, } </script> <style></style> ~~~ >[danger] ##### 简单案例 1. 下面代码执行结果先打印**1 0 "w" ** 在一秒后打印 **8 1 10 "z"** ,因为`watchEffect `是立即执行因此先打印一次,在第一次立即执行的时候收集了需要监听的响应属性,在定时器1s后执行改变了监听的响应数据,因此`watchEffect `**被触发** ~~~ <template> <div> </div> </template> <script lang="ts"> import {ref, watchEffect} from 'vue' export default { setup() { const count = ref(0) const str = ref('w') const stop = watchEffect(()=>{ console.log(1,count.value,str.value) count.value = 100 }) setTimeout(()=>{ count.value = 10 str.value = "z" },1000) return { count } } } </script> ~~~ >[danger] ##### 调用返回值以停止侦听 1. 在一些情况下,也可以显式调用返回值以停止侦听,下面案例只打印了**1 0 "w"** 在一秒后并没有打印 **8 1 10 "z"**原因手动调用了**停止响应式监听** ~~~ <template> <div> </div> </template> <script lang="ts"> import {ref, watchEffect} from 'vue' export default { setup() { const count = ref(0) const str = ref('w') const stop = watchEffect(()=>{ console.log(1,count.value,str.value) count.value = 100 }) // 停止了响应式监听 stop() setTimeout(()=>{ count.value = 10 str.value = "z" },1000) return { count } } } </script> ~~~ >[danger] ##### 清除副作用 1. 回调函数还提供了参数,例如下面`onInvalidate `在没有放开下面两条注释条件任意一个的时候`onInvalidate `不会执行,但是放开其中任意一个条件`onInvalidate `都将会执行,说明参数会在触发监听属性改变前执行即,如果放开下面1s后执行的注释控制台打印的结果: ~~~ 1 0 "w" 执行回调 1 10 "z" ~~~ 所以`onInvalidate `触发跟你放的顺序无关 ~~~ <template> <div> </div> </template> <script lang="ts"> import {ref, watchEffect} from 'vue' export default { setup() { const count = ref(0) const str = ref('w') const stop = watchEffect((onInvalidate )=>{ console.log(1,count.value,str.value) onInvalidate(()=>{ console.log('执行回调') }) }) // 停止了响应式监听 --侦听器被停止触发回调 // stop() // ---------------------- // setTimeout(()=>{ // 副作用即将重新执行时 // count.value = 10 // str.value = "z" // },1000) return { count } } } </script> ~~~ * 官网给了一个案例参考 1. 当请求 id 改变时候,请求接口,但是id 是高频率变化,希望每次变化如果上次接口数据没请求完则销毁 ~~~ watchEffect(onInvalidate => { const token = performAsyncOperation(id.value) onInvalidate(() => { // id has changed or watcher is stopped. // invalidate previously pending async operation token.cancel() }) }) ~~~ >[danger] ##### 关于停止监听说明 1. 除了手动触发 停止监听,实际同步语句创建的侦听器会自动绑定到宿主组件实例上,并且会在宿主组件卸载时自动停止,所以不用担心如果页面已经销毁了监听还在内存中。 2. 但如果是异步创建 ,必须手动监听例如下面例子 ~~~ <script setup> import { watchEffect } from 'vue' // 它会自动停止 watchEffect(() => {}) // ...这个则不会! setTimeout(() => { watchEffect(() => {}) }, 100) </script> ~~~ >[danger] ##### 虽然监听是立即执行的但可控 1. 监听虽然立即执行但是可以使用**条件式的侦听逻辑** 来控制内部代码 ~~~ // 需要异步请求得到的数据 const data = ref(null) watchEffect(() => { if (data.value) { // 数据加载后执行某些操作... } }) ~~~ >[danger] ##### 渲染 是获取不到dom 1. 默认情况下,用户创建的侦听器回调,都会在 Vue 组件更新之前被调用。这意味着你在侦听器回调中访问的DOM 将是被 Vue 更新之前的状态 ~~~ <template> <div ref="test"></div> </template> <script> import { watchEffect, ref } from '@vue/runtime-core' export default { name: 'App', setup() { const test = ref(null) watchEffect(() => { console.log(test.value) }) return { test, } }, } </script> <style></style> ~~~ * 如图 ~~~ 1第一次执行时候dom 还没挂载此时获取为null,当挂载上后再次触发此时为dom元素 ~~~ ![](https://img.kancloud.cn/14/56/1456d9bf1be18cd1a5b42664512950d5_278x68.png) * 解决上面方法 ~~~ watchEffect(()=>{.....}, { flush: 'post' }) 或者 import { watchPostEffect } from 'vue' watchPostEffect(() => { /* 在 Vue 更新后执行 */ }) 两个都是在vue更新后执行此时第一次就能获取dom ~~~ >[success] # watchPostEffect()[](https://cn.vuejs.org/api/reactivity-core.html#watchposteffect) [`watchEffect()`](https://cn.vuejs.org/api/reactivity-core.html#watcheffect)使用`flush: 'post'`选项时的别名。 >[success] # watchSyncEffect()[](https://cn.vuejs.org/api/reactivity-core.html#watchsynceffect) [`watchEffect()`](https://cn.vuejs.org/api/reactivity-core.html#watcheffect)使用`flush: 'sync'`选项时的别名。 >[info] ## 官网参考 [## watchEffect](https://cn.vuejs.org/api/reactivity-core.html#readonly) [## `watchEffect()`](https://cn.vuejs.org/guide/essentials/watchers.html#watcheffect)