>[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)
- 官网给的工具
- 声明vue2 和 vue3
- 指令速览
- Mustache -- 语法
- v-once -- 只渲染一次
- v-text -- 插入文本
- v-html -- 渲染html
- v-pre -- 显示原始的Mustache标签
- v-cloak -- 遮盖
- v-memo(新)-- 缓存指定值
- v-if/v-show -- 条件渲染
- v-for -- 循环
- v-bind -- 知识
- v-bind -- 修饰符
- v-on -- 点击事件
- v-model -- 双向绑定
- 其他基础知识速览
- 快速使用
- 常识知识点
- key -- 作用 (后续要更新)
- computed -- 计算属性
- watch -- 侦听
- 防抖和节流
- vue3 -- 生命周期
- vue-cli 和 vite 项目搭建方法
- vite -- 导入动态图片
- 组件
- 单文件组件 -- SFC
- 组件通信 -- porp
- 组件通信 -- $emit
- 组件通信 -- Provide / Inject
- 组件通信 -- 全局事件总线mitt库
- 插槽 -- slot
- 整体使用案例
- 动态组件 -- is
- keep-alive
- 分包 -- 异步组价
- mixin -- 混入
- v-model-- 组件
- 使用计算属性
- v-model -- 自定义修饰符
- Suspense -- 实验属性
- Teleport -- 指定挂载
- 组件实例 -- $ 属性
- Option API VS Composition API
- Setup -- 组合API 入口
- api -- reactive
- api -- ref
- 使用ref 和 reactive 场景
- api -- toRefs 和 toRef
- api -- readonly
- 判断性 -- API
- 功能性 -- API
- api -- computed
- api -- $ref 使用
- api -- 生命周期
- Provide 和 Inject
- watch
- watchEffect
- watch vs. watchEffect
- 简单使用composition Api
- 响应性语法糖
- css -- 功能
- 修改css -- :deep() 和 var
- Vue3.2 -- 语法
- ts -- vscode 配置
- attrs/emit/props/expose/slots -- 使用
- props -- defineProps
- props -- defineProps Ts
- emit -- defineEmits
- emit -- defineEmits Ts
- $ref -- defineExpose
- slots/attrs -- useSlots() 和 useAttrs()
- 自定义指令
- Vue -- 插件
- Vue2.x 和 Vue3.x 不同点
- $children -- 移除
- v-for 和 ref
- attribute 强制行为
- 按键修饰符
- v-if 和 v-for 优先级
- 组件使用 v-model -- 非兼容
- 组件
- h -- 函数
- jsx -- 编写
- Vue -- Router
- 了解路由和vue搭配
- vueRouter -- 简单实现
- 安装即使用
- 路由懒加载
- router-view
- router-link
- 路由匹配规则
- 404 页面配置
- 路由嵌套
- 路由组件传参
- 路由重定向和别名
- 路由跳转方法
- 命名路由
- 命名视图
- Composition API
- 路由守卫
- 路由元信息
- 路由其他方法 -- 添加/删除/获取
- 服务器配置映射
- 其他
- Vuex -- 状态管理
- Option Api -- VUEX
- composition API -- VUEX
- module -- VUEX
- 刷新后vuex 数据同步
- 小技巧
- Pinia -- 状态管理
- 开始使用
- pinia -- state
- pinia -- getter
- pinia -- action
- pinia -- 插件 ??
- Vue 源码解读
- 开发感悟
- 练手项目