vue3【实用教程】侦听器 watch,自动侦听 watchEffect(),$watch,手动停止侦听器

简介: vue3【实用教程】侦听器 watch,自动侦听 watchEffect(),$watch,手动停止侦听器

watch

侦听明确指定的状态变化执行回调

实战场景

  • 侦听路由传参的变化,重新访问接口,刷新页面
  • 侦听接口返回值的变化,刷新页面

侦听值类型数据

// 选项式 API
watch: {
  // 每当 question 改变时,这个函数就会执行
  question(newQuestion, oldQuestion) {
    // 第一个参数是新值,第二个参数是旧值
  }
},
// 组合式 API
<script setup>
  import { ref, watch } from 'vue'
  let question = ref('')
  
  watch(
    question,
    (newQuestion, oldQuestion) => {
        // 第一个参数是新值,第二个参数是旧值
    }
  )
</script>

侦听值类型的对象属性

// 选项式 API
watch: {
  // 监听对象person中gender属性的变化
    'person.gender':function(new_gender){
    },
},


// 组合式 API
watch(
  // 不能直接写 person.gender,会导致watch函数的第一个参数会得到一个字符串,不具有响应式,应改用返回对象属性的 getter 函数
  () => person.gender,
  (new_gender) => {
  }
)

侦听引用类型数据

选项式API需添加深度侦听 deep

watch选项默认是浅层监听,无法监听到嵌套属性的变化,所以监听对象时通常需要使用深度监听

// 选项式 API 
watch: {
  person: {
    // 在嵌套的属性变更时触发
    handler(newValue, oldValue) {
      // 因对象是引用类型的数据,对象属性发生变化时,对象引用的地址并没有发生变化,所以只要没有替换对象本身,这里的 `newValue` 和 `oldValue` 相同
    },
    // 开启深度监听
    deep: true
  }
}

注意事项

深度侦听需要遍历被侦听对象中的所有嵌套的属性,当用于大型数据结构时,开销很大。因此请只在必要时才使用它,并且要留意性能。

组合式API默认就是深度侦听

// 组合式 API 
let person = reactive({ age: 35 })

watch(
  person,
  // 在嵌套的属性变更时触发
  (newValue, oldValue) => {
      // 因对象是引用类型的数据,对象属性发生变化时,对象引用的地址并没有发生变化,所以只要没有替换对象本身,这里的 `newValue` 和 `oldValue` 相同
  }
)

返回响应式对象的 getter 函数,只在返回不同的对象时触发回调:

watch(
  () => state.someObject,
  () => {
    // 仅当 state.someObject 被替换时触发
  }
)

通过 deep 选项,可强制转成深层侦听器

watch(
  () => state.someObject,
  (newValue, oldValue) => {
    // 此处`newValue` 和 `oldValue` 是相等的,除非 state.someObject 被整个替换
  },
  { deep: true }
)

注意事项

深度侦听需要遍历被侦听对象中的所有嵌套的属性,当用于大型数据结构时,开销很大。因此请只在必要时才使用它,并且要留意性能。

侦听多个数据

// 选项式 API
watch: {
  x(newX,oldX) {
  },
  'y.value':function(newY) {
  },
},
// 选项式 API
watch([x, () => y.value], ([newX, newY]) => {
}) 

watch 函数的第一个参数可以是一个 ref (包括计算属性)、一个响应式对象、一个 getter 函数、或多个数据源组成的数组。

立即侦听 immediate

watch 默认是懒执行的:仅当数据源变化时,才会执行回调。

但有时需要在侦听器被创建时就触发监听

// 选项式 API
watch: {
  question: {
    handler(newValue, oldValue) {
      // 在组件实例创建时会立即执行(在 created 钩子之前执行,此时 data、computed 和 methods 选项都是可用的)
    },
    // 开启立即监听
    immediate: true
  }
}
// 组合式 API
watch(
  question,
  (newValue, oldValue) => {
      // 在组件实例创建时会立即执行(在 created 钩子之前执行,此时 data、computed 和 methods 选项都是可用的)
  },
  // 开启立即监听
  { immediate: true }
)

单次侦听 once

vue3.4以上版本的新增功能:侦听器只执行一次

// 选项式 API
watch: {
  source: {
    handler(newValue, oldValue) {
      // 当 `source` 变化时,仅触发一次
    },
    // 开启单次监听
    once: true
  }
}
// 组合式 API
watch(
  source,
  (newValue, oldValue) => {
    // 当 `source` 变化时,仅触发一次
  },
  // 开启单次监听
  { once: true }
)

改变侦听回调的触发时机

vue更新前回调(同步侦听)

若想在 Vue 进行任何更新之前触发侦听回调,则需添加 flush: 'sync' 选项,将其变成一个同步侦听器

// 选项式 API
  watch: {
    key: {
      handler() {},
      // 提前侦听回调
      flush: 'sync'
    }
  }
// 组合式 API
watch(source, callback, {
  flush: 'sync'
})

注意事项

同步侦听器不会进行批处理,每当检测到响应式数据发生变化时就会触发。可以使用它来监视简单的布尔值,但应避免在可能多次同步修改的数据源 (如数组) 上使用。

【默认】vue更新后,DOM 更新前回调

watch 的回调默认在父组件更新 (如有) 之后、所属组件的 DOM 更新之前执行,所以此时回调中访问的是所属组件更新之前的 DOM

DOM 更新后回调(后置侦听)

要想在回调中访问的是所属组件更新之后的 DOM,需添加 flush: 'post' 选项

// 选项式 API
  watch: {
    key: {
      handler() {},
      // 延后侦听回调
      flush: 'post'
    }
  }
// 组合式 API
watch(source, callback, {
  flush: 'post'
}) 

watchEffect()

仅组合式 API 可用

立即运行一个函数,同时响应式地追踪其依赖,并在依赖更改时重新执行

const count = ref(0)

watchEffect(() => {
    // 在创建时,即执行第一次回调,并开始侦听count,在后续每次 count 发生变化时,都会触发回调
  console.log(count.value)
})
  • 自动追踪所有能访问到的响应式属性执行回调
  • 在创建时,就会执行一次(与 watch 添加了 immediate 选项的效果相同,其目的是收集要侦听的数据)

适用场景

  • 侦听的目标依赖于多个响应式数据
  • 侦听嵌套数据结构中的多个属性(可能会比深度侦听器性能更佳,因为它只跟踪回调中被使用到的属性,而不是递归地跟踪所有的属性)

注意事项

watchEffect 仅会在其同步执行期间,才追踪依赖。在使用异步回调时,只有在第一个 await 正常工作前访问到的属性才会被追踪。

同步侦听

vue更新前回调

import { watchEffect} from 'vue'

watchEffect(callback, {
  flush: 'sync'
})

import { watchSyncEffect } from 'vue'

watchSyncEffect(() => {
  /* 在响应式数据变化时同步执行 */
})

watchSyncEffect 是同步触发的 watchEffect 的别名

注意事项

同步侦听器不会进行批处理,每当检测到响应式数据发生变化时就会触发。可以使用它来监视简单的布尔值,但应避免在可能多次同步修改的数据源 (如数组) 上使用。

后置侦听

DOM 更新后回调

import { watchEffect} from 'vue'

watchEffect(callback, {
  flush: 'post'
})

import { watchPostEffect } from 'vue'

watchPostEffect (() => {
   /* 在 Vue 更新后执行 */
})

watchPostEffect 是后置刷新的 watchEffect 的别名

官方范例

watchEffect(async () => {
  const response = await fetch(
    `https://jsonplaceholder.typicode.com/todos/${todoId.value}`
  )
  data.value = await response.json()
})

与下方 watch 的写法相比, watchEffect 更加方便和简洁

const todoId = ref(1)
const data = ref(null)

watch(
  todoId,
  async () => {
    const response = await fetch(
      `https://jsonplaceholder.typicode.com/todos/${todoId.value}`
    )
    data.value = await response.json()
  },
  { immediate: true }
)

watch 和 watchEffect() 的区别

  • watch 只追踪明确侦听的数据源,在回调执行期间会避免追踪依赖,能更加精确地控制回调函数的触发时机。
  • watchEffect 会自动追踪所有能访问到的响应式属性,在回调执行期间会追踪依赖,导致其响应性依赖关系有时会不那么明确。

用 $watch() 创建侦听器

this.$watch('question', (newQuestion) => {
})

适用场景:

  • 在特定条件下设置一个侦听器
  • 只侦听响应用户交互的内容
  • 需要提前停止侦听器

手动停止侦听器

  • 用 watch 选项或者 $watch() 实例方法声明的侦听器,会在宿主组件卸载时自动停止。
  • 在 setup() 或 <script setup> 中用同步语句创建的侦听器,会自动绑定到宿主组件实例上,并且会在宿主组件卸载时自动停止。
  • 用异步回调创建的侦听器,不会绑定到当前组件上,必须手动停止它,以防内存泄漏。
<script setup>
import { watchEffect } from 'vue'

// 异步回调创建的侦听器必须手动停止!
setTimeout(() => {
  watchEffect(() => {})
}, 100)
</script>

注意事项

需要异步创建侦听器的情况很少,请尽可能选择同步创建。如果需要等待一些异步数据,可以用条件式的侦听逻辑。

// 需要异步请求得到的数据
const data = ref(null)

watchEffect(() => {
  if (data.value) {
    // 数据加载后执行某些操作...
  }
})
  • 调用 $watch() 或 watchEffect() 返回的函数,可手动停止侦听器
const unwatch = this.$watch('question', (newQuestion) => {
})

// 当该侦听器不再需要时,停止该侦听器
unwatch()
const unwatch = watchEffect(() => {})

// ...当该侦听器不再需要时
unwatch()

目录
相关文章
|
21天前
|
缓存 JavaScript UED
Vue3中v-model在处理自定义组件双向数据绑定时有哪些注意事项?
在使用`v-model`处理自定义组件双向数据绑定时,要仔细考虑各种因素,确保数据的准确传递和更新,同时提供良好的用户体验和代码可维护性。通过合理的设计和注意事项的遵循,能够更好地发挥`v-model`的优势,实现高效的双向数据绑定效果。
125 64
|
21天前
|
前端开发 JavaScript 测试技术
Vue3中v-model在处理自定义组件双向数据绑定时,如何避免循环引用?
Web 组件化是一种有效的开发方法,可以提高项目的质量、效率和可维护性。在实际项目中,要结合项目的具体情况,合理应用 Web 组件化的理念和技术,实现项目的成功实施和交付。通过不断地探索和实践,将 Web 组件化的优势充分发挥出来,为前端开发领域的发展做出贡献。
28 8
|
20天前
|
存储 JavaScript 数据管理
除了provide/inject,Vue3中还有哪些方式可以避免v-model的循环引用?
需要注意的是,在实际开发中,应根据具体的项目需求和组件结构来选择合适的方式来避免`v-model`的循环引用。同时,要综合考虑代码的可读性、可维护性和性能等因素,以确保系统的稳定和高效运行。
21 1
|
20天前
|
JavaScript
Vue3中使用provide/inject来避免v-model的循环引用
`provide`和`inject`是 Vue 3 中非常有用的特性,在处理一些复杂的组件间通信问题时,可以提供一种灵活的解决方案。通过合理使用它们,可以帮助我们更好地避免`v-model`的循环引用问题,提高代码的质量和可维护性。
32 1
|
25天前
|
JavaScript API 开发者
Vue是如何进行组件化的
Vue是如何进行组件化的
|
26天前
|
JavaScript 前端开发 开发者
vue 数据驱动视图
总之,Vue 数据驱动视图是一种先进的理念和技术,它为前端开发带来了巨大的便利和优势。通过理解和应用这一特性,开发者能够构建出更加动态、高效、用户体验良好的前端应用。在不断发展的前端领域中,数据驱动视图将继续发挥重要作用,推动着应用界面的不断创新和进化。
|
28天前
|
JavaScript 前端开发 开发者
vue学习第一章
欢迎来到我的博客!我是瑞雨溪,一名热爱前端的大一学生,专注于JavaScript与Vue,正向全栈进发。博客分享Vue学习心得、命令式与声明式编程对比、列表展示及计数器案例等。关注我,持续更新中!🎉🎉🎉
32 1
vue学习第一章
|
28天前
|
JavaScript 前端开发 索引
vue学习第三章
欢迎来到瑞雨溪的博客,一名热爱JavaScript与Vue的大一学生。本文介绍了Vue中的v-bind指令,包括基本使用、动态绑定class及style等,希望能为你的前端学习之路提供帮助。持续关注,更多精彩内容即将呈现!🎉🎉🎉
26 1
vue学习第三章
|
28天前
|
缓存 JavaScript 前端开发
vue学习第四章
欢迎来到我的博客!我是瑞雨溪,一名热爱JavaScript与Vue的大一学生。本文介绍了Vue中计算属性的基本与复杂使用、setter/getter、与methods的对比及与侦听器的总结。如果你觉得有用,请关注我,将持续更新更多优质内容!🎉🎉🎉
35 1
vue学习第四章
|
28天前
|
JavaScript 前端开发 算法
vue学习第7章(循环)
欢迎来到瑞雨溪的博客,一名热爱JavaScript和Vue的大一学生。本文介绍了Vue中的v-for指令,包括遍历数组和对象、使用key以及数组的响应式方法等内容,并附有综合练习实例。关注我,将持续更新更多优质文章!🎉🎉🎉
24 1
vue学习第7章(循环)