百科狗-知识改变命运!
--

侦听器 - watch()、watchEffect() - vue 基础

百变鹏仔1年前 (2023-11-21)阅读数 8#技术干货
文章标签侦听器

侦听器

基本示例

侦听器 - watch()、watchEffect() - vue 基础

计算属性允许我们声明性地计算衍生值。然而,在有些情况下,为了应对一些状态的变化,我们需要运行些effect(副作用):例如更改 DOM,或者根据异步操作的结果,去修改另一处的状态。

在组合式 API 中,我们可以使用watch()函数,在每次响应式状态发生变化时,触发回调函数:

 

Ask a yes/no question:

{{ question }}


侦听来源类型

watch的第一个参数,可以是不同形式的“数据源”:它可以是一个ref(包括计算属性)、一个响应式对象、一个getter函数、或多个来源组成的数组:

const x = ref(0)
const y = ref(0)

// 单个 ref
watch(x, (newX) => {
  console.log(`x is ${newX}`)
})

// getter 函数
watch(
  () => x.value + y.value,
  (sum) => {
    console.log(`sum of x + y is: ${sum}`)
  }
)

// 多个来源组成的数组
watch([x, () => y.value], ([newX, newY]) => {
  console.log(`x is ${newX} and y is ${newY}`)
})

注意,你不能直接侦听响应式对象的属性值,例如:

const obj = reactive({ count: 0 })

// 这不会正常工作,因为你是向 watch() 传入了一个 number
watch(obj.count, (count) => {
  console.log(`count is: ${count}`)
})

若需要侦听响应式对象的属性值,而是用getter函数

// 提供一个 getter 函数
watch(
  () => obj.count,
  (count) => {
    console.log(`count is: ${count}`)
  }
)


深层侦听器

直接给watch()传入一个响应式对象,会隐式地创建一个深层侦听器——该回调函数在所有嵌套的变更时都会被触发:

const obj = reactive({ count: 0 })

watch(obj, (newValue, oldValue) => {
  // 在嵌套的属性变更时触发
  // 注意:`newValue` 此处和 `oldValue` 是相等的
  // 因为它们是同一个对象!
})

obj.count++

相比之下,一个返回响应式对象的getter函数,只有在getter函数返回不同的对象时,才会触发回调:

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

然而,在上面的例子里,你可以显式地加上deep选项,强制转成深层侦听器:

watch(
  () => state.someObject,
  (newValue, oldValue) => {
    // 注意:`newValue` 此处和 `oldValue` 是相等的
    // *除非* state.someObject 被整个替换了
  },
  { deep: true }
)
深度侦听需要遍历被侦听对象中的所有嵌套的 property,当用于大型数据结构时,开销很大。因此请只在必要时才使用它,并且要留意性能。


watchEffect()

watch()是懒执行的:仅在侦听源变化时,才会执行回调。但在某些场景中,我们希望在创建侦听器时,立即执行一遍回调,或者说要积极地执行回调。举个例子,我们想请求一些初始数据,然后在相关状态更改时重新请求数据。我们可以这样写:

const url = ref('https://...')
const data = ref(null)

async function fetchData() {
  const response = await fetch(url.value)
  data.value = await response.json()
}

// 立即获取
fetchData()

// ...再侦听 url 变化
watch(url, fetchData)

这段代码还可以用watchEffect()函数来简化。watchEffect()会立即执行一遍回调函数,如果这时函数产生了effect(副作用),Vue 会自动追踪effect的依赖关系,自动分析出响应源。上面的例子可以重写为:

const url = ref('https://...')
const data = ref(null) watchEffect(async () => {
  const response = await fetch(url.value)
  data.value = await response.json()
})

这个例子中,回调会立即执行。在执行期间,它会自动追踪url.value作为依赖(近似于计算属性)。每当url.value变化时,回调会再次执行。

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


watch vs. watchEffect

watchwatchEffect都能响应式地执行有effect(副作用)的回调。它们之间的主要区别是追踪响应式依赖的方式:

  • watch只追踪明确侦听的源。它不会追踪任何在回调中访问到的东西。另外,仅在响应源确实改变时才会触发回调。watch会避免在发生副作用时追踪依赖,因此,我们能更加精确地控制回调函数的触发时机。
  • watchEffect会在effect(副作用)发生期间追踪依赖。它会在同步执行过程中,自动追踪所有能访问到的响应式 property。这更方便,而且代码往往更简洁,但其响应性依赖关系不那么明确。


回调的刷新时机

当你更改了响应式状态,它可能会同时触发 Vue 组件更新和侦听器回调。

默认情况下,用户创建的侦听器回调,都会在 Vue 组件更新之前被调用。这意味着,你在侦听器回调中,访问的 DOM 将是被 Vue 更新之前的状态。如果想在侦听器回调中,能访问被 Vue 更新之后的DOM,你需要指明flush:'post'选项:

watch(source, callback, { flush: 'post'
})

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

后置刷新的watchEffect()有个更方便的别名watchPostEffect()

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


停止侦听器

setup()

要手动停止一个侦听器,请调用watchwatchEffect返回的函数:

const unwatch = watchEffect(() => {})

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

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

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

watchEffect(() => { if (data.value) {
    // 得到数据后要做的事...
  }
})

鹏仔微信 15129739599 鹏仔QQ344225443 鹏仔前端 pjxi.com 共享博客 sharedbk.com

免责声明:我们致力于保护作者版权,注重分享,当前被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理!邮箱:344225443@qq.com)

图片声明:本站部分配图来自网络。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

内容声明:本文中引用的各种信息及资料(包括但不限于文字、数据、图表及超链接等)均来源于该信息及资料的相关主体(包括但不限于公司、媒体、协会等机构)的官方网站或公开发表的信息。部分内容参考包括:(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供参考使用,不准确地方联系删除处理!本站为非盈利性质站点,本着为中国教育事业出一份力,发布内容不收取任何费用也不接任何广告!)