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

Vue3组合式函数mounted钩子中无法获取数据的问题

Vue3组合式函数onMounted中无法获取异步数据的问题分析与解决

核心原因拆解

  1. 异步请求的时序问题
    useFetch中的watchEffect会在组件挂载时触发,但内部的fetch是异步操作。onMounted钩子执行时,网络请求还处于pending状态,此时data.value还是初始的null,所以JSON.stringify(data)会输出null。

  2. Ref对象的打印特性
    直接console.log(data)时,浏览器会显示Ref对象的实时引用状态——初始显示null,当请求完成data.value更新后,浏览器会自动刷新这个对象的显示,所以你会看到“先显示null后填充数据”的现象;但JSON.stringify(data)是序列化执行瞬间的Ref对象,此时value属性为null,所以输出固定为null。

  3. Ref值的访问错误
    直接访问data.object是错误的:data是Vue的Ref包装对象,不是原始数据,必须通过.value才能获取内部的实际值。当data.value还是null时,访问null.object就会触发类似“Cannot read properties of null”的错误(你看到的“data is undefined”应该是表述偏差,本质是访问了null的属性)。

解决方案

1. 监听数据变化再执行逻辑

如果需要在数据就绪后处理逻辑,不要在onMounted里硬取,而是用watch监听data的变化:

<script setup>
import { ref, computed, watch } from 'vue'
import { useFetch } from '../composables/http.js'

const baseUrl = 'https://jsonplaceholder.typicode.com/todos/'
const id = ref('1')
const url = computed(() => baseUrl + id.value)

const { data, error, retry } = useFetch(url)

// 监听data,数据更新时触发
watch(data, (newData) => {
  if (newData) {
    console.log(JSON.stringify(newData))
    console.log(newData)
    // 安全访问属性
    console.log(newData.title)
  }
}, { immediate: true }) // immediate: true 会在监听初始化时执行一次
</script>

2. 正确访问Ref值

任何时候操作Ref的实际数据,都必须通过.value属性:

onMounted(() => {
  // 打印当前的data值
  console.log(JSON.stringify(data.value))
  // 安全访问属性
  if (data.value) {
    console.log(data.value.title)
  }
})

3. 让useFetch返回就绪Promise(可选)

如果需要在特定时机(比如onMounted)等待数据加载完成,可以修改useFetch,添加一个返回Promise的接口:

// composables/http.js
import { ref, watchEffect, toValue } from 'vue'

export function useFetch(url) {
    const data = ref(null)
    const error = ref(null)
    let resolveReady = null
    // 创建一个用于等待数据就绪的Promise
    const ready = new Promise(resolve => {
      resolveReady = resolve
    })

    watchEffect(async () => {
        data.value = null
        error.value = null
        const urlValue = toValue(url)
        try {
            const res = await fetch(urlValue)
            data.value = await res.json()
            resolveReady(data.value)
        } catch (e) {
            error.value = e
            resolveReady(null)
        }
    })

    return { data, error, ready }
}

然后在组件中使用:

<script setup>
import { ref, computed, onMounted } from 'vue'
import { useFetch } from '../composables/http.js'

const baseUrl = 'https://jsonplaceholder.typicode.com/todos/'
const id = ref('1')
const url = computed(() => baseUrl + id.value)

const { data, error, ready } = useFetch(url)

onMounted(async () => {
  // 等待数据就绪
  const loadedData = await ready
  if (loadedData) {
    console.log(JSON.stringify(loadedData))
  }
})
</script>

补充说明

Vue的mounted阶段仅表示组件DOM已挂载完成,不保证异步请求已经结束。异步操作的完成时机取决于网络速度,和组件生命周期没有绑定关系,所以不要假设mounted时异步数据一定就绪。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 21:37:11