You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue3 Options API中watch未响应ref状态变更问题求助

Vue3 Options API中watch未触发的原因及修复方案

问题根源

你的代码里有两个关键问题导致watch不触发:

  1. 你把组件内的state ref直接塞进了data,Vue会自动对data返回的对象做响应式处理——这里相当于把ref解包成了普通值,但之后你又给它赋值了另一个ref对象,导致组件的state变成了指向类内部ref的引用。
  2. 当你执行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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 17:43:17