Vue3通过props传递对象后触发TypeError问题求助
Vue3组件访问Props属性时出现TypeError问题分析与解决
我有一个Vue3页面和Vue3组件,页面通过Firebase/Firestore的composable获取数据,将对象作为props传递给组件,但在组件的script setup中访问props属性时触发TypeError,相关代码和报错如下:
useProfiles 代码
import { ref } from 'vue' import { db } from "/@src/composable/useFirebase" import { doc, onSnapshot, } from "firebase/firestore"; export const profile = ref({}); export const getProfile = async (id) => { const unsub = onSnapshot(doc(db, "profiles", id), { includeMetadataChanges: true }, (doc) => { profile.value = doc.data() profile.value['id'] = doc.id }); };
Vue页面代码
<script setup lang="ts"> import { getProfile, profile } from '/@src/composable/useProfiles' const params = new URL(location.href).searchParams.get('id') onMounted(async () => { getProfile(params); }) </script> <template> <div class="page-content-inner"> <Core-MyProfile v-if="!params" /> <Core-UserProfile v-if="params" :profile="profile"/> </div> </template>
Vue组件代码
<script setup lang="ts"> import { computed, ref, onMounted } from "vue"; const props = defineProps({ profile: { type: Object, required: true, }, }) const sellPoints = props.profile.tribute.sell.current const timePoints = props.profile.tribute.time.current const usePoints = props.profile.tribute.use.current const totalPoints = computed( () => sellPoints+timePoints+usePoints ); </script>
报错信息
runtime-core.esm-bundler.js:38 [Vue warn]: Invalid prop: type check failed for prop "profile". Expected Object, got Undefined at <CoreUserProfile key=1 profile=undefined > at <Index onVnodeUnmounted=fn<onVnodeUnmounted> ref=Ref< undefined > key="/profile?id=FC9gNTJ7D2hMaBcna9ILTGyxB3E2" > at <BaseTransition mode="out-in" appear=false persisted=false ... > at <Transition name="fade-fast" mode="out-in" > at <RouterView> at <HomeLayout> at <Profile onVnodeUnmounted=fn<onVnodeUnmounted> ref=Ref< undefined > > at <BaseTransition mode="out-in" appear=false persisted=false ... > at <Transition name="fade-slow" mode="out-in" > at <RouterView> at <App> --- runtime-core.esm-bundler.js:38 [Vue warn]: Unhandled error during execution of setup function at <CoreUserProfile key=1 profile=undefined > at <Index onVnodeUnmounted=fn<onVnodeUnmounted> ref=Ref< undefined > key="/profile?id=0TCIgQZxy5YJqyXNMZQqkBQhZv13" > at <BaseTransition mode="out-in" appear=false persisted=false ... > at <Transition name="fade-fast" mode="out-in" > at <RouterView> at <HomeLayout> at <Profile onVnodeUnmounted=fn<onVnodeUnmounted> ref=Ref< undefined > > at <BaseTransition mode="out-in" appear=false persisted=false ... > at <Transition name="fade-slow" mode="out-in" > at <RouterView> at <App> --- Core-UserProfile.vue:12 Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'tribute') at setup (Core-UserProfile.vue:12:34) at callWithErrorHandling (runtime-core.esm-bundler.js:155:22) at setupStatefulComponent (runtime-core.esm-bundler.js:7165:29) at setupComponent (runtime-core.esm-bundler.js:7120:11) at mountComponent (runtime-core.esm-bundler.js:5473:13) at processComponent (runtime-core.esm-bundler.js:5448:17) at patch (runtime-core.esm-bundler.js:5038:21) at mountChildren (runtime-core.esm-bundler.js:5234:13) at mountElement (runtime-core.esm-bundler.js:5143:17) at processElement (runtime-core.esm-bundler.js:5115:13)
问题原因
- 异步加载导致初始值缺失:
getProfile依赖Firestore的异步回调,页面渲染时profile可能还未获取到数据,甚至出现undefined,导致prop类型校验失败。 - 直接访问深层属性无容错:组件在setup阶段直接读取
props.profile.tribute,但此时props.profile可能是空对象或undefined,触发读取属性的TypeError。 - 模板渲染条件不严谨:仅通过
params判断渲染组件,未考虑profile是否加载完成,导致组件在数据就绪前就已初始化。
解决步骤
1. 给profile设置合理初始结构
修改useProfiles,提前定义好数据结构,避免后续访问深层属性报错:
export const profile = ref({ tribute: { sell: { current: 0 }, time: { current: 0 }, use: { current: 0 } } });
2. 组件中增加属性访问容错
组件中改用可选链操作符,结合computed处理数据,避免直接访问深层属性:
<script setup lang="ts"> import { computed } from "vue"; const props = defineProps({ profile: { type: Object, required: true, default: () => ({ tribute: { sell: { current: 0 }, time: { current: 0 }, use: { current: 0 } } }) }, }) const totalPoints = computed(() => { const sell = props.profile?.tribute?.sell?.current || 0; const time = props.profile?.tribute?.time?.current || 0; const use = props.profile?.tribute?.use?.current || 0; return sell + time + use; }); </script>
3. 优化页面渲染条件
页面模板中增加profile加载完成的判断,确保数据就绪后再渲染组件:
<template> <div class="page-content-inner"> <Core-MyProfile v-if="!params" /> <div v-if="params && !profile.id">加载中...</div> <Core-UserProfile v-if="params && profile.id" :profile="profile"/> </div> </template>
4. 给composable添加加载状态(可选)
在useProfiles中增加加载状态,更精准控制页面渲染:
import { ref } from 'vue' import { db } from "/@src/composable/useFirebase" import { doc, onSnapshot } from "firebase/firestore"; export const profile = ref({ tribute: { sell: { current: 0 }, time: { current: 0 }, use: { current: 0 } } }); export const isLoading = ref(false); export const getProfile = async (id) => { if (!id) return; isLoading.value = true; try { const unsub = onSnapshot(doc(db, "profiles", id), { includeMetadataChanges: true }, (doc) => { profile.value = { ...doc.data(), id: doc.id }; isLoading.value = false; }); return unsub; } catch (err) { console.error('加载profile失败:', err); isLoading.value = false; } };
页面模板更新为:
<template> <div class="page-content-inner"> <Core-MyProfile v-if="!params" /> <div v-if="params && isLoading">加载中...</div> <Core-UserProfile v-if="params && !isLoading && profile.id" :profile="profile"/> </div> </template>
内容的提问来源于stack exchange,提问作者David Griffin
相关产品推荐
相关产品推荐

