Vue3+Go项目中页面刷新后响应式对象数据丢失及toRef取值undefined问题
Vue3 + Go 项目开发问题解决方案
我来帮你解决这两个实际开发中常见的问题,咱们一步步拆解:
问题1:刷新页面后项目数据丢失
背后的原因
页面刷新时,Vue的整个实例会被销毁,内存里reactive存储的数据直接清空。虽然你在onMounted里请求了API,但网络请求需要时间,这期间页面会处于无数据状态,看起来就像“数据丢失”。另外,原代码没处理加载状态,也没做任何数据持久化,导致刷新后必须等请求完成才能看到内容。
解决方案
我给你两个优化方向,结合起来用体验最好:
- 添加加载状态和错误提示:让用户明确知道页面正在加载,避免误以为数据没了;
- 可选本地缓存:把非敏感的项目数据存在
localStorage里,刷新后先展示缓存内容,再用最新API数据更新(适合不需要实时同步的场景)。
修改后的父组件代码:
<script> import { reactive, onMounted } from 'vue'; import { useRoute } from 'vue-router'; export default { components: { EditProject }, setup() { const route = useRoute(); // 初始化项目数据结构,避免子组件读取属性时出现undefined const project = reactive({ id_project: null, name: '', description: '', start_date: '', end_date: '', created_at: '', updated_at: '' }); const isLoading = reactive({ value: true }); const error = reactive({ value: null }); const fetchProject = async () => { try { isLoading.value = true; // 先读本地缓存(可选) const cachedProject = localStorage.getItem(`project_${route.params.id}`); if (cachedProject) { Object.assign(project, JSON.parse(cachedProject)); } // 请求最新数据 const res = await fetch(`http://localhost:8000/api/v1/projects/${route.params.id}`); if (!res.ok) throw new Error('获取项目数据失败'); const data = await res.json(); Object.assign(project, data); // 更新本地缓存(可选) localStorage.setItem(`project_${route.params.id}`, JSON.stringify(data)); error.value = null; } catch (err) { console.error(err); error.value = err.message; } finally { isLoading.value = false; } }; onMounted(() => { fetchProject(); }); return { project, isLoading, error }; }, }; </script>
问题2:子组件中toRef访问value返回undefined
背后的原因
这是时机+用法错误导致的:
- 父组件的
project初始是空对象,子组件挂载时props.project还没被API数据填充,此时调用toRef(props.project, 'name'),因为props.project.name根本不存在,所以Ref的value就是undefined; - 你原代码里用
const content = toRef(props, 'project')是多此一举,props.project本身就是对象,之后再用toRef(content.value, 'name'),初始时content.value是空对象,自然拿不到属性; - 刷新后父组件重新请求API,子组件挂载时
props.project为空的时间更长,所以问题更明显。
解决方案
正确的做法是用toRefs把props.project的属性转成响应式Ref,再用computed兜底默认值,确保属性不存在时不会返回undefined。另外要统一API返回的字段名(比如你原代码里用endProject,但API返回的是end_date,这里要对齐)。
修改后的子组件代码:
<script> import { ref, toRefs, computed } from 'vue'; import { useRoute, useRouter } from 'vue-router'; export default { props: { project: { type: Object, required: true, // 给props加默认结构,避免初始为空时的问题 default: () => ({ name: '', description: '', end_date: '', user: { id_user: '' } }) }, }, setup(props) { // 用toRefs把props.project的属性转成响应式Ref const { name: rawName, description: rawDescription, end_date: rawEndDate } = toRefs(props.project); // 用computed加默认值,彻底避免undefined const name = computed(() => rawName.value || ''); const description = computed(() => rawDescription.value || ''); // 把API返回的ISO日期转成YYYY-MM-DD格式,方便表单使用 const endProject = computed(() => rawEndDate.value ? rawEndDate.value.split('T')[0] : ''); // 可选链操作符避免user不存在时报错 const user = computed(() => props.project.user?.id_user || ''); const users = ref([]); const route = useRoute(); const router = useRouter(); const fetchUsers = async () => { try { const res = await fetch(`http://localhost:8000/api/v1/users/selected_user`); if (!res.ok) throw new Error('获取用户列表失败'); users.value = await res.json(); } catch (error) { console.error(error); } }; const editProject = async () => { // 校验必填字段 if (!name.value || !description.value || !endProject.value || !user.value) { console.log('请填写所有必填字段'); alert('请填写所有必填字段'); return; } try { const res = await fetch(`http://localhost:8000/api/v1/projects/${route.params.id}`, { method: 'PUT', body: JSON.stringify({ name: name.value, description: description.value, // 替换所有横杠,避免日期解析错误 end_date: new Date(endProject.value.replace(/-/g, '/')).toISOString(), user: { id_user: user.value }, }), headers: { 'Content-Type': 'application/json' }, }); if (!res.ok) throw new Error('更新项目失败'); // 更新本地缓存(如果父组件用了缓存) localStorage.setItem(`project_${route.params.id}`, JSON.stringify({ ...props.project, name: name.value, description: description.value, end_date: new Date(endProject.value.replace(/-/g, '/')).toISOString(), updated_at: new Date().toISOString() })); router.push({ name: 'Project' }); } catch (err) { console.error(err); alert('更新项目失败,请重试'); } }; fetchUsers(); return { name, description, endProject, user, users, editProject }; }, }; </script>
额外注意事项
- 字段名统一:一定要确保API返回的字段和你代码里用的字段一致(比如
end_datevsendProject),不然会出现属性找不到的问题; - 日期处理:
new Date()解析YYYY-MM-DD在部分浏览器可能有问题,所以用replace(/-/g, '/')转成YYYY/MM/DD更稳妥; - 响应式传递:父组件的
project是响应式的,子组件用toRefs或computed可以确保属性变化时子组件实时更新; - 错误处理:给所有API请求加错误捕获,不仅要打印控制台,还要给用户友好提示,提升体验。
内容的提问来源于stack exchange,提问作者cosmos multi
相关产品推荐
相关产品推荐

