Vue3 Options API中watch未响应ref状态变更问题求助
Vue3 Options API中watch未触发的原因及修复方案
问题根源
你的代码里有两个关键问题导致watch不触发:
- 你把组件内的
stateref直接塞进了data,Vue会自动对data返回的对象做响应式处理——这里相当于把ref解包成了普通值,但之后你又给它赋值了另一个ref对象,导致组件的state变成了指向类内部ref的引用。 - 当你执行
state.value = cls.getState()后,组件的state就指向了类里的那个ref对象。模板能正常显示是因为Vue会自动解包ref的value,但Options API的watch默认只监听引用的变化——类内部的state.value更新时,组件里的state引用本身没改,所以watch不会触发。
修复方案
方案一:直接同步类内部的state值
修改app.vue,让组件的state直接绑定类内部ref的value,并监听类的state变化:
<template> <button @click="onDigitizePolygon()">click</button> <span> state: {{ state }}</span> </template> <script> import { ref } from 'vue' import ClsUseStates from './composables/ClsUseStates.js' const cls = new ClsUseStates() export default { data() { return { state: '---' }; }, name: 'App', methods: { onDigitizePolygon() { cls.useStates() // 初始同步值 this.state = cls.getState().value // 监听类内部state的变化,同步到组件 const unwatch = cls.getState().value = newVal => { this.state = newVal } // 组件销毁时取消监听,避免内存泄漏 this.$once('hook:beforeUnmount', unwatch) } }, watch: { state(oldval, newval) { console.log(oldval); console.log(newval); } } } </script>
方案二:使用深度监听或监听state.value
如果要保留ref引用传递的方式,可修改watch配置:
<template> <button @click="onDigitizePolygon()">click</button> <span> state: {{ state }}</span> </template> <script> import { ref } from 'vue' import ClsUseStates from './composables/ClsUseStates.js' const cls = new ClsUseStates() const state = ref('---') export default { data() { return { state }; }, name: 'App', methods: { onDigitizePolygon() { cls.useStates() state.value = cls.getState() } }, watch: { // 方式1:深度监听state内部变化 state: { handler(oldVal, newVal) { console.log(oldVal.value); console.log(newVal.value); }, deep: true }, // 方式2:直接监听state.value 'state.value'(oldVal, newVal) { console.log(oldVal); console.log(newVal); } } } </script>
更合理的写法建议
其实用类封装这种状态逻辑没必要,Vue3的组合式函数(Composable)更适合这类场景,改写如下:
// composables/useStates.js import { ref } from 'vue' export default function useStates() { const state = ref('not-started') const startStates = () => { setTimeout(() => state.value = 'started', 1000) setTimeout(() => state.value = 'processing', 2000) setTimeout(() => state.value = 'successful', 3000) } return { state, startStates } }
在app.vue中使用:
<template> <button @click="startStates()">click</button> <span> state: {{ state }}</span> </template> <script> import { watch } from 'vue' import useStates from './composables/useStates.js' export default { setup() { const { state, startStates } = useStates() // 直接在setup里监听state变化 watch(state, (newVal, oldVal) => { console.log(oldVal, newVal) }) return { state, startStates } }, name: 'App' } </script>
这种写法更贴合Vue3的设计思路,逻辑也更清晰。
内容的提问来源于stack exchange,提问作者Amrmsmb
相关产品推荐
相关产品推荐

