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

Vue.js 3中无法访问ref的.value属性问题求助

问题分析与解决

问题现象

在自定义组合式函数中使用ref()定义的error变量,内部可正常访问.value属性,但在组件setup()中导入该组合式函数并调用异步的fetchDoc后,立即访问error.value得到null,而控制台展开RefImpl对象时却能看到内部value为404。

组合式函数代码:

import { ref, reactive } from 'vue'

import { doc, getDoc } from 'firebase/firestore'
import { db } from '../configs/firebase'

export default function () {
  const docCol = reactive({})
  const error = ref(null)

  const fetchDoc = async (collectionName, collectionID) => {
    try {
      const tourCol = doc(db, collectionName, collectionID)

      const docSnap = await getDoc(tourCol)

      if (docSnap.exists()) {
        console.log('==> Document data exists!')
        Object.assign(docCol, { id: docSnap.id, ...docSnap.data() })
      } else {
        error.value = 404
        console.log('No such document!')
      }
    } catch (err) {
      error.value = err
      console.log(err)
    }
  }
  return { docCol, error, fetchDoc }
}

组件setup()代码:

setup() {
    const route = useRoute()
    const router = useRouter()
    const tour = ref({})

    const { docCol, error, fetchDoc } = useDoc()
    fetchDoc('tours', route.params.id)
    tour.value = docCol

    console.log('🚀 ~ error:', error)
    console.log('🚀 ~ error.value:', error.value)
    if (error.value === 404) {
      router.push({ name: 'NotFound' })
    }

    return { tour, error }
}

用户尝试的无效写法:

const err = ref(error) 
console.log(err.value)

核心原因

这是异步操作的时序问题加上浏览器控制台的惰性求值特性导致的:

  1. fetchDoc是异步函数,调用后会立即返回Promise,后续的console.log和路由跳转是同步执行的,此时fetchDoc内部的数据库请求还未完成,error.value仍保持初始值null。
  2. 浏览器控制台显示的RefImpl对象是惰性求值的,当你展开对象时才会读取最新的value,所以会看到内部是404,但同步打印时的error.value确实是null。

解决方法

方案1:使用watch监听error变化

利用Vue的响应式监听,在error.value变化时执行路由跳转:

setup() {
  const route = useRoute()
  const router = useRouter()
  const tour = ref({})

  const { docCol, error, fetchDoc } = useDoc()
  fetchDoc('tours', route.params.id)
  tour.value = docCol

  // 监听error的变化
  watch(error, (newError) => {
    if (newError === 404) {
      router.push({ name: 'NotFound' })
    }
  })

  return { tour, error }
}

方案2:await异步操作完成

将setup改为异步函数,等待fetchDoc执行完毕后再访问error.value:

async setup() {
  const route = useRoute()
  const router = useRouter()
  const tour = ref({})

  const { docCol, error, fetchDoc } = useDoc()
  // 等待fetchDoc的异步操作完成
  await fetchDoc('tours', route.params.id)
  tour.value = docCol

  console.log('🚀 ~ error.value:', error.value)
  if (error.value === 404) {
    router.push({ name: 'NotFound' })
  }

  return { tour, error }
}

注意:使用异步setup时,需要在父组件中用<Suspense>包裹该组件,否则会抛出组件渲染错误。

方案3:新增加载状态(推荐)

在组合式函数中添加加载状态,结合watch监听加载完成和错误状态,逻辑更清晰:
修改组合式函数:

import { ref, reactive } from 'vue'
import { doc, getDoc } from 'firebase/firestore'
import { db } from '../configs/firebase'

export default function () {
  const docCol = reactive({})
  const error = ref(null)
  const isLoading = ref(true) // 新增加载状态标记

  const fetchDoc = async (collectionName, collectionID) => {
    isLoading.value = true
    error.value = null // 每次请求前重置错误
    try {
      const tourCol = doc(db, collectionName, collectionID)
      const docSnap = await getDoc(tourCol)

      if (docSnap.exists()) {
        console.log('==> Document data exists!')
        Object.assign(docCol, { id: docSnap.id, ...docSnap.data() })
      } else {
        error.value = 404
        console.log('No such document!')
      }
    } catch (err) {
      error.value = err
      console.log(err)
    } finally {
      isLoading.value = false // 请求结束后标记加载完成
    }
  }
  return { docCol, error, fetchDoc, isLoading }
}

组件中使用:

setup() {
  const route = useRoute()
  const router = useRouter()
  const tour = ref({})

  const { docCol, error, fetchDoc, isLoading } = useDoc()
  fetchDoc('tours', route.params.id)
  tour.value = docCol

  // 监听加载完成和错误状态
  watch([isLoading, error], () => {
    if (!isLoading.value && error.value === 404) {
      router.push({ name: 'NotFound' })
    }
  })

  return { tour, error, isLoading }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 23:25:30