当前位置: 首页 > news >正文

Vue3侦听器实战:组件与Pinia状态监听如何高效应用?

Vue3 侦听器实战案例——在组件与Pinia中的应用

一、组件内的侦听器基础

1.1 基本概念与使用场景

侦听器是Vue3中用于响应数据变化的核心工具,当你需要在数据变化时执行异步或复杂的操作时,侦听器就派上用场了。比如:

  • 数据变化时发送API请求
  • 表单验证
  • 复杂的状态联动
Options API 写法
export default { data() { return { question: '', answer: 'Questions usually contain a question mark. ;-)', loading: false } }, watch: { // 当question变化时触发 question(newQuestion, oldQuestion) { if (newQuestion.includes('?')) { this.getAnswer() } } }, methods: { async getAnswer() { this.loading = true this.answer = 'Thinking...' try { const res = await fetch('https://yesno.wtf/api') this.answer = (await res.json()).answer } catch (error) { this.answer = 'Error! Could not reach the API. ' + error } finally { this.loading = false } } } }
Composition API 写法
<script setup> import { ref, watch } from 'vue' const question = ref('') const answer = ref('Questions usually contain a question mark. ;-)') const loading = ref(false) // 直接监听ref watch(question, async (newQuestion, oldQuestion) => { if (newQuestion.includes('?')) { loading.value = true answer.value = 'Thinking...' try { const res = await fetch('https://yesno.wtf/api') answer.value = (await res.json()).answer } catch (error) { answer.value = 'Error! Could not reach the API. ' + error } finally { loading.value = false } } }) </script> <template> <p> Ask a yes/no question: <input v-model="question" :disabled="loading" /> </p> <p>{{ answer }}</p> </template>
1.2 深度侦听器

默认情况下,侦听器是浅层的,只会监听引用类型的引用变化,不会监听内部属性变化。如果需要监听嵌套对象的变化,需要使用深度侦听器。

import { reactive, watch } from 'vue' const user = reactive({ name: 'John', address: { city: 'New York' } }) // 深度侦听整个对象 watch(user, (newUser, oldUser) => { console.log('User changed:', newUser) }, { deep: true }) // 或者只侦听嵌套属性 watch(() => user.address.city, (newCity) => { console.log('City changed to:', newCity) })
1.3 立即执行侦听器

默认情况下,侦听器是懒加载的,只有当数据变化时才会触发。如果需要在组件创建时立即执行一次,可以使用immediate: true选项。

watch(question, (newQuestion) => { // 这个回调会在组件创建时立即执行一次 }, { immediate: true })
1.4 watchEffect 的使用

watchEffect是Vue3提供的另一种侦听器,它会自动追踪回调函数中的响应式依赖,当依赖变化时自动触发。

import { ref, watchEffect } from 'vue' const todoId = ref(1) const data = ref(null) // 自动追踪todoId的变化 watchEffect(async () => { const response = await fetch( `https://jsonplaceholder.typicode.com/todos/${todoId.value}` ) data.value = await response.json() })

watchvswatchEffect对比流程图:

┌─────────────────┐ ┌─────────────────┐ │ watch │ │ watchEffect │ ├─────────────────┤ ├─────────────────┤ │ 显式指定依赖 │ │ 自动追踪依赖 │ │ 懒加载(默认) │ │ 立即执行 │ │ 可获取新旧值 │ │ 无法获取旧值 │ │ 更精确控制 │ │ 更简洁语法 │ └─────────────────┘ └─────────────────┘

二、Pinia中的侦听器应用

2.1 监听Pinia Store中的状态

在Pinia中,我们可以使用Vue的`watch`函数来监听store中的状态变化。

首先创建一个Pinia store:

// stores/counter.js import { defineStore } from 'pinia' export const useCounterStore = defineStore('counter', { state: () => ({ count: 0, message: 'Hello Pinia' }), actions: { increment() { this.count++ } } })

然后在组件中监听store状态:

<script setup> import { watch } from 'vue' import { useCounterStore } from '@/stores/counter' const counterStore = useCounterStore() // 监听单个状态 watch(() => counterStore.count, (newCount, oldCount) => { console.log(`Count changed from ${oldCount} to ${newCount}`) }) // 监听多个状态 watch([() => counterStore.count, () => counterStore.message], ([newCount, newMessage], [oldCount, oldMessage]) => { console.log(`Count: ${oldCount} -> ${newCount}`) console.log(`Message: ${oldMessage} -> ${newMessage}`) } ) // 深度监听整个store watch(counterStore, (newStore, oldStore) => { console.log('Store changed:', newStore) }, { deep: true }) </script>
2.2 实战案例:表单状态同步

假设我们有一个表单组件,需要将表单数据同步到Pinia store中,同时当store中的数据变化时,表单也需要更新。

<script setup> import { ref, watch } from 'vue' import { useFormStore } from '@/stores/form' const formStore = useFormStore() // 本地表单数据 const localForm = ref({ name: '', email: '' }) // 组件创建时同步store数据到本地 localForm.value = { ...formStore.formData } // 监听本地表单变化,同步到store watch(localForm, (newForm) => { formStore.updateFormData(newForm) }, { deep: true }) // 监听store数据变化,同步到本地 watch(() => formStore.formData, (newForm) => { localForm.value = { ...newForm } }, { deep: true }) </script> <template> <form> <input v-model="localForm.name" placeholder="Name" /> <input v-model="localForm.email" placeholder="Email" type="email" /> </form> </template>

Pinia store代码:

// stores/form.js import { defineStore } from 'pinia' export const useFormStore = defineStore('form', { state: () => ({ formData: { name: '', email: '' } }), actions: { updateFormData(newData) { this.formData = { ...newData } } } })

三、课后Quiz

问题1:watch和watchEffect的主要区别是什么?

答案解析:

  1. 依赖追踪方式:watch需要显式指定依赖,而watchEffect会自动追踪回调中的响应式依赖。
  2. 执行时机:watch默认是懒加载的,只有当依赖变化时才会触发;watchEffect会在组件创建时立即执行一次。
  3. 参数获取:watch可以获取新旧值,而watchEffect无法获取旧值。
  4. 使用场景:watch适合需要精确控制依赖和获取新旧值的场景;watchEffect适合简单的副作用处理,语法更简洁。
问题2:如何在Pinia中监听整个store的变化?

答案解析:可以直接将store实例作为watch的第一个参数,并设置deep: true选项:

watch(counterStore, (newStore, oldStore) => { console.log('Store changed:', newStore) }, { deep: true })

或者使用getter函数:

watch(() => ({ ...counterStore }), (newStore) => { console.log('Store changed:', newStore) })

四、常见报错解决方案

1. 报错:"watch() expects a source value as the first argument"

原因:传递给watch的第一个参数不是有效的响应式源。比如直接传递了一个非响应式的对象或值。解决方法

  • 确保监听的是ref、reactive对象,或者返回响应式值的getter函数。
  • 如果要监听对象的某个属性,使用getter函数:watch(() => obj.property, callback)
2. 报错:"deep option is ignored when watching a getter"

原因:当使用getter函数作为watch源时,deep选项会被忽略,因为getter函数返回的是一个值,而不是引用类型。解决方法

  • 如果需要深度监听,直接监听整个reactive对象,或者在getter函数中返回对象的副本:watch(() => ({ ...obj }), callback, { deep: true })
3. 报错:"Maximum call stack size exceeded"

原因:在watch回调中修改了被监听的数据,导致无限循环。解决方法

  • 确保在watch回调中不会修改被监听的同一个数据。
  • 如果需要修改,可以使用条件判断或者使用watchEffect代替。
4. Pinia中watch不触发

原因:可能是因为没有正确使用getter函数来监听store中的状态。解决方法

  • 使用getter函数来监听store中的状态:watch(() => store.count, callback)
  • 确保store中的状态是响应式的,使用Pinia的state定义方式。
http://www.jsqmd.com/news/321935/

相关文章:

  • 2026年上海十大国际学校排名,上海林国荣学校新加坡留学靠谱吗
  • Figma使用,把设计稿转html
  • TypeScript深度思考:一个TodoList项目教会你的不仅是语法
  • Sentinel Dashboard 与 Nacos 集成代码模板详解:实现流控规则持久化至 Nacos
  • oracle表空间管理
  • 2026年1月内蒙古十大营销策划公司优选机构,技术+运营+效果全解析
  • 告别期刊投稿“修罗场”!虎贲等考AI用智能黑科技,让你的论文一次命中
  • AutoCAD 的二次开发
  • 亲测好用10个AI论文软件,助你轻松搞定本科生论文!
  • 瑞幸前端开发二面 28k前端面试全程记录
  • 探秘锅圈盈利预告,最高92%增长背后有何过人之处?
  • 基于springboot的校园智能物流管理系统
  • Java计算机毕设之基于SpringBoot的社区帮扶邻里服务平台社区邻里服务平台设计与实现(完整前后端代码+说明文档+LW,调试定制等)
  • 【遥感应用技术科普】基于多时相数据的耕地撂荒遥感监测
  • 2026年烟台营销推广公司专项甄选报告:头部优质机构全景梳理及专业选型指南
  • 2026年郑州营销策划公司推荐:本地企业增长痛点深度评测与权威排名解析
  • 京东e卡(电子卡)回收推荐两家平台
  • 二叉树--所有路径
  • 手把手教会你什么是 Java 多态 —— 从“if-else 地狱”到“一行代码搞定”(Spring Boot 实战)
  • 2026年西安营销推广公司推荐:权威榜单揭晓,品帮科技领跑
  • 让销售团队产能实现翻番的秘密武器:从进行海量筛选转变为能够一键直连老板
  • 2026年郑州营销策划公司推荐:基于技术整合能力评测,针对数字化转型与效果衡量痛点
  • 十大家装品牌精选推荐:2026年1月厦门家装公司排行榜单
  • Thrombin (B 147-158) (human) ;TWTANVGKGQPS
  • 轻松同步 Outlook 联系人到 Android
  • 2026年长沙营销推广公司权威评测:基于实战效果的五家头部企业深度解析
  • 数字广播调制器 纽格立NGA-201 DRM-FM调频广播调制器调频数字广播改造适用
  • Java毕设项目:基于SpringBoot的社区邻里服务平台设计与实现(源码+文档,讲解、调试运行,定制等)
  • 使用 6 种方法将文件从 Android 无缝传输到iPad
  • 2026年内蒙古营销策划公司推荐:基于技术特性与本地服务评测,涵盖线上线下多场景运营痛点。