Vue 3 组合式函数Ref行为异常:异步请求数据无法正常访问
问题分析:Vue组合式函数useAxiosGet中data.value返回null的原因
问题重现
你封装的useAxiosGet组合式函数代码:
// inside useAxiosGet.js import { ref, watchEffect } from 'vue'; import axios from 'axios'; export default function (url) { const data = ref(null); watchEffect(async () => { axios({ url: url }) .then((res) => { data.value = res.data; }) .catch((err) => console.log(err)); }); return { data }; }
使用时的代码及现象:
<script setup> const { data } = useAxiosGet('my/path/to/backend/'); console.log(data); // 输出Ref包装对象,可在_rawValue中找到数据 console.log(isRef(data)) // 输出true,解构后未失去响应性 console.log(data.value); // 输出'null' </script>
核心原因
1. 异步请求的时序问题
axios的GET请求是异步操作,不会阻塞后续代码执行。当你执行console.log(data.value)时,后端请求还在处理中,还未返回数据,此时data的初始值null还没被更新。
2. 控制台的“实时更新”误导
你在控制台的Ref对象里看到_rawValue有数据,是因为当你展开查看这个对象时,异步请求已经完成,data.value已经被回调函数更新了——控制台会实时展示对象的最新状态,而不是执行console.log那一刻的快照。
3. watchEffect的执行逻辑
watchEffect会在组件初始化时立即触发,但里面的axios请求是异步的,then回调要等请求完成后才会执行,这时候你同步编写的console.log早就执行完毕了。
验证与解决方法
方法1:监听数据变化
用watch监听data的更新,请求完成后就能拿到数据:
<script setup> import { watch } from 'vue'; const { data } = useAxiosGet('my/path/to/backend/'); watch(data, (newData) => { console.log(newData); // 请求完成后会打印后端返回的数据 }); </script>
方法2:在模板中直接使用
Vue模板会自动解包Ref,并且会响应式更新,可直接在模板中使用(推荐搭配可选链操作符避免报错):
<template> <div>{{ data?.username }}</div> </template>
方法3:优化组合式函数(推荐)
给函数添加加载状态、错误处理,还能避免组件卸载时的请求泄漏:
// 优化后的useAxiosGet.js import { ref, watchEffect } from 'vue'; import axios from 'axios'; export default function (url) { const data = ref(null); const loading = ref(true); const error = ref(null); watchEffect((onCleanup) => { const cancelToken = axios.CancelToken.source(); loading.value = true; error.value = null; axios({ url, cancelToken: cancelToken.token }) .then(res => data.value = res.data) .catch(err => { if (!axios.isCancel(err)) { error.value = err; console.error('请求失败:', err); } }) .finally(() => loading.value = false); // 组件卸载或依赖变化时取消请求 onCleanup(() => cancelToken.cancel('请求已取消')); }); return { data, loading, error }; }
使用优化后的函数:
<script setup> const { data, loading, error } = useAxiosGet('my/path/to/backend/'); // 可以根据loading状态处理加载提示 if (loading.value) console.log('请求中...'); if (error.value) console.log('请求出错:', error.value); </script>
内容的提问来源于stack exchange,提问作者haligh
相关产品推荐
相关产品推荐

