vue3在watch和watchEffect的使用

Vue3 的 watch 是一个函数,能接收三个参数,参数一是监听的属性,参数二是接收新值和老值的回调函数,参数三是配置项。

javascript 复制代码
<script setup>
import { watch, ref, reactive } from 'vue'
  props: ['info'],
const data = reactive({
	name:'Watch',
    age: 18,
    height: 180,
    children: [],
    money:10
})

// 监听 props属性
 watch(
        ()=>props,
        (val) => {
          if (!val) {
            return
          }
			data.money=props.info.money
			data.height=props.info.money
        }
      )

// 监听其他属性、路由或者状态管理的都这样
watch(
    () => data.age, 
    (newAge, oldAge) => { ... }
)

// 监听多个属性,数组放多个值,返回的新值和老值也是数组形式
watch([data.age, data.money], ([newAge, newMoney], [oldAge, oldMoney]) => { ... })

// 第三个参数是一个对象,为可配置项,有5个可配置属性
watch(data.children, (newList, oldList) => { ... }, {
    // 这两个和 Vue2 一样,没啥说的
    immediate: true,
    deep: true,
    // 回调函数的执行时机,默认在组件更新之前调用。更新后调用改成post
    flush: 'pre', // 默认值是 pre,可改成 post 或 sync
    // 下面两个是调试用的
    onTrack (e) { debugger }
    onTrigger (e) { debugger }
})
</script>

关于副作用

在 watch 回调函数中能接收第三个参数 onInvalidate,为清除副作用的函数,首次触发监听的回调函数(handler)不会执行 onInvalidate,之后每次触发默认会先执行 onInvalidate

就是说默认它的执行机制在更新之前调用,比如如下代码,当 key 触发更新时会先打印 222 再打印 Watch,如果需要在更新之后调用,可以在 watch 第三个配置项中添加 flush: post,

javascript 复制代码
// 回调函数接收一个参数,为清除副作用的函数
watch(key, (newKey, oldKey, onInvalidate) => {
    console.log('Watch')
    // 获取DOM默认获取到的是更新前的dom,如果是flush: post,可以获取到更新后的dom
    console.log('DOM节点:', dom.innterHTML)
    onInvalidate(() => {
        console.log(2222)
    })
})

onInvalidate 的使用场景就是:比如监听的回调函数(handler)里有一些异步操作,当再次触发 watch 的时候可以用它来对前面未完成的异步任务执行取消/忽略/重置/初始化某些操作,比如取消上一次触发 watch 时未完成的请求。

注意

javascript 复制代码
// 配置immediate参数,立即执行,以及深层次监听
 watch(data , (newValue, oldValue) => {
      // 回调函数
      console.log('触发监控更新了new',  newValue);
      console.log('触发监控更新了old',  oldValue);
    }, {
      immediate: true,
      deep: true
    })

如果定义了reactive的数据,想去使用watch监听数据改变,则无法正确获取旧值,并且deep属性配置无效,自动强制开启了深层次监听。

如果使用 ref 初始化一个对象或者数组类型的数据,会被自动转成reactive的实现方式,生成proxy代理对象。也会变得无法正确取旧值。

用任何方式生成的数据,如果接收的变量是一个proxy代理对象,就都会导致watch这个对象时,watch回调里无法正确获取旧值。

所以当大家使用watch监听对象时,如果在不需要使用旧值的情况,可以正常监听对象没关系;但是如果当监听改变函数里面需要用到旧值时,只能监听 对象.xxx属性 的方式才行。

watchEffect

Vue 3中的watchEffect函数是一种响应式副作用函数,它的原理可以分为两个阶段

初始化阶段:在初始化阶段,Vue 3会通过reactive函数将传入watchEffect的响应式数据转化为Pro某y对象。Pro某y对象可以拦截对响应式数据的访问和修改操作,并在数据变化时通知相关的副作用代码。在初始化阶段结束后,会立即执行一次副作用的代码,这样可以保证在watchEffect第一次调用前的响应式数据的值是被追踪的。

运行阶段:在运行阶段,Vue 3会将副作用代码包装成一个函数,并将这个函数作为当前组件的副作用函数进行管理。在组件渲染时,会执行这个副作用函数。在执行过程中,Pro某y对象会收集对响应式数据的访问,从而建立数据与副作用之间的依赖关系。当响应式数据发生变化时,Pro某y对象会触发依赖更新,进而通知相关的副作用代码进行重新执行。

Vue3 中除了 watch 还增加了一个 watchEffect。区别是:

watch 是对传入的一个或多个值进行监听,且默认第一次不会执行,只有监听的值发生变化的时候才会执行触发时会返回新值和老值、

watchEffect 是每次代码加载watchEffect都会执行,且不需要传入监听内容,会自动收集函数内的数据源作为依赖,当依赖发生变化时会重新执行函数(有点像computed的味道),而且不会返回新值和老值

正常情况下组件销毁/卸载后这两都会自动停止监听,但也有例外,比如异步的方式,在 setTimeout 里创建的监听就都需要手动停止监听,停止方式如下

javascript 复制代码
// 监听方法赋值
const unwatch = watch('key', callback)
const unwatchEffect = watchEffect(() => {})
// 需要停止监听的时候,手动调用停止监听
unwatch()
unwatchEffect()

watchEffect的使用方法

javascript 复制代码
<script setup>
import { watchEffect } from 'vue'

// 正常使用
watchEffect(() => {
    // 会自动收集这个函数使用到的属性作为依赖,进行监听
    // 监听的是 userInfo.name 属性,不会监听 userInfo
    console.log(userInfo.name)
    
    // 如果是console.log(userInfo),就只会首次加载触发,
    //因为监控了整个对象,对象里面的值变化不会触发
})

// 有两个参数,参数一是触发监听回调函数,参数二是可选配置项
watchEffect(() => {...}, {
    // 这里是可配置项,意思和 watch 是一样的,不过这只有3个可配置的
    flush: 'pre',
    //onTrack 将在响应式 property 或 ref 作为依赖项被追踪时被调用。
	//onTrigger 将在依赖项变更导致副作用被触发时被调用。
    onTrack (e) { debugger }
    onTrigger (e) { debugger }
})

// 回调函数接收一个参数,为清除副作用的函数,和 watch 的同理
watchEffect(onInvalidate => {
    console.log('watchEffect')
    onInvalidate(() => {
        console.log(2222)
    })
})
//比如:
watchEffect(onInvalidate => {
  const token = getUserInfo(id) // 获取用户信息
  onInvalidate(() => {
  //id已更改或观察程序已停止。使以前挂起的异步操作无效
    token.cancel()
  })
})

watchEffect 如果需要修改配置项 flush 为 post 或 sync 时,可以直接使用别名,如下

javascript 复制代码
watchEffect(() => {...}, {
    flush: 'post',
})
// 和下面这个是一样的
watchPostEffect(() => {})
-----------------------------
watchEffect(() => {...}, {
    flush: 'sync',
})
// 和下面这个是一样的
watchSyncEffect(() => {})

写在最后,欢迎各位大佬指正留言~

相关推荐
腾讯TNTWeb前端团队1 小时前
helux v5 发布了,像pinia一样优雅地管理你的react状态吧
前端·javascript·react.js
范文杰5 小时前
AI 时代如何更高效开发前端组件?21st.dev 给了一种答案
前端·ai编程
拉不动的猪5 小时前
刷刷题50(常见的js数据通信与渲染问题)
前端·javascript·面试
拉不动的猪5 小时前
JS多线程Webworks中的几种实战场景演示
前端·javascript·面试
FreeCultureBoy6 小时前
macOS 命令行 原生挂载 webdav 方法
前端
uhakadotcom6 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom6 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom6 小时前
React与Next.js:基础知识及应用场景
前端·面试·github
uhakadotcom7 小时前
Remix 框架:性能与易用性的完美结合
前端·javascript·面试
uhakadotcom7 小时前
Node.js 包管理器:npm vs pnpm
前端·javascript·面试