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

Vue3中ref可更新DOM但reactive无效的问题咨询

Vue3中reactive无法同步DOM更新的原因及解决方法

问题场景

使用ref时组件能正常渲染接口返回的数据,但用reactive时DOM完全没变化——两种情况控制台都能看到transaction有数据,这是为什么?此前的理解是「对象用reactive、基本类型用ref」,现在对二者的适用场景产生了困惑。

使用ref可正常运行的代码

<template>
   {{ transaction }}
</template>

<script setup>
import { ref } from 'vue'

let transaction = ref({})

const getPayByLinkTransaction = () => {
   axios({
      method: "get",
      url: "pay-by-link",
      params: {
         merchantUuid: import.meta.env.VITE_MERCHANT_UUID,
         uuid: route.params.uuid,
      },
   })
      .then((res) => {
         transaction.value = res.data
      })
      .catch((e) => {
         console.log(e)
      })
}

getPayByLinkTransaction()
</script>

使用reactive无法生效的代码

<template>
   {{ transaction }}
</template>

<script setup>
import { reactive } from 'vue'

let transaction = reactive({})

const getPayByLinkTransaction = () => {
   axios({
      method: "get",
      url: "pay-by-link",
      params: {
         merchantUuid: import.meta.env.VITE_MERCHANT_UUID,
         uuid: route.params.uuid,
      },
   })
      .then((res) => {
         transaction = { ...res.data }
      })
      .catch((e) => {
         console.log(e)
      })
}

getPayByLinkTransaction()
</script>

核心原因

reactive创建的响应式对象不能被直接替换:

  • ref本质是一个包装对象,你修改的是transaction.value,这个操作不会替换ref本身的引用,Vue能追踪到内部值的变化。
  • reactive是把原始对象转换成响应式代理,当你执行transaction = { ...res.data }时,等于把原来的响应式代理直接换成了普通新对象——模板绑定的还是最初的空响应式对象,自然不会更新DOM。控制台能看到数据是因为变量被重新赋值,但这个变量已经和原来的响应式代理解绑了。

修正后的reactive写法

有两种可行的解决方式:

方式1:合并属性而非替换对象(推荐)

<template>
   {{ transaction }}
</template>

<script setup>
import { reactive } from 'vue'

const transaction = reactive({})

const getPayByLinkTransaction = () => {
   axios({
      method: "get",
      url: "pay-by-link",
      params: {
         merchantUuid: import.meta.env.VITE_MERCHANT_UUID,
         uuid: route.params.uuid,
      },
   })
      .then((res) => {
         // 用Object.assign合并新属性到原有响应式对象
         Object.assign(transaction, res.data)
      })
      .catch((e) => {
         console.log(e)
      })
}

getPayByLinkTransaction()
</script>

方式2:用外层对象包裹,修改内层属性

<template>
   {{ transaction.data }}
</template>

<script setup>
import { reactive } from 'vue'

// 用reactive包装一个外层对象,内层存数据
const transaction = reactive({ data: {} })

const getPayByLinkTransaction = () => {
   axios({
      method: "get",
      url: "pay-by-link",
      params: {
         merchantUuid: import.meta.env.VITE_MERCHANT_UUID,
         uuid: route.params.uuid,
      },
   })
      .then((res) => {
         // 修改内层属性,不会破坏外层的响应式代理引用
         transaction.data = res.data
      })
      .catch((e) => {
         console.log(e)
      })
}

getPayByLinkTransaction()
</script>

ref与reactive的适用场景补充

  • ref:不仅适用于基本类型,也适用于对象/数组。优势是可以直接通过.value替换整个值,在需要频繁重新赋值引用类型时更灵活。
  • reactive:适用于复杂对象/数组,但必须保持对响应式代理的引用,只能修改内部属性。如果业务中需要频繁替换整个对象,用ref会更省心。

内容的提问来源于stack exchange,提问作者Dally

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 18:57:05