使用 Vuefire 渲染之前在组件中加载 Firestore 数据
<p>我正在尝试用姓名和个人资料图片填充人员表。该名称源自 Firestore 数据库,图片使用该名称在 Firebase 存储桶中查找相关图片。</p>
<p>我已经观看了几个小时的视频,并且已经浏览了近一百篇文章,因此我的示例代码包含了每个代码的片段,因为我一直在尝试每种组合并得到混合但不成功的结果。</p>
<p>在返回最少错误的当前状态下,我能够成功地使用名称填充表,但是在同一组件中,它无法提取个人资料图片。用于个人资料图片的值已更新,但它从占位符值更新为<code>未定义</code>。</p>
<p><strong>GamePlanner.vue</strong></p>
<pre class="brush:vue;toolbar:false;"><template>
<div>
<Field />
<Bench />
<!-- <Suspense>
<template #default> -->
<PlanSummary />
<!-- </template>
<template #fallback>
<div class="loading">Loading...</div>
</template>
</Suspense> -->
</div>
</template>
</pre>
<p><strong>PlanSummary.vue</strong></p>
<pre class="brush:vue;toolbar:false;"><template>
<div class="summaryContainer">
<div class="inningTableContainer">
<table class="inningTable">
<tbody>
<!-- <Suspense> -->
<!-- <template #default> -->
<PlayerRow v-for="player in players.value" :key="player.id" :player="(player as Player)" />
<!-- </template> -->
<!-- <template #fallback>
<tr data-playerid="0">
<img class="playerImage" src="https://www.gravatar.com/avatar/205e460b479e2e5b48aec07710c08d50" />
Loading players...
<span class="playerNumber">00</span>
</tr>
</template> -->
<!-- </Suspense> -->
</tbody>
</table>
</div>
</div>
</template>
<script setup lang="ts">
import { computed, onErrorCaptured, ref } from "vue";
import { useFirestore, useCollection } from "vuefire";
import { collection } from "firebase/firestore";
import { Player, Inning } from "@/definitions/GamePlanner";
import PlayerRow from "./PlayerRow.vue";
const db = useFirestore();
const gamePlanID = "O278vlB9Xx39vkZvIsdP";
// const players = useCollection(collection(db, `/gameplans/${gamePlanID}/participants`));
// const players = ref(useCollection(collection(db, `/gameplans/${gamePlanID}/participants`)));
// Unhandled error during execution of scheduler flush
// Uncaught (in promise) DOMException: Failed to execute 'insertBefore' on 'Node': The node before which the new node is to be inserted is not a child of this node.
const players = ref();
players.value = useCollection(collection(db, `/gameplans/${gamePlanID}/participants`));
// Seeminly infinite loop with "onServerPrefetch is called when there is no active component instance to be associated with."
// Renders 5 (??) undefined players
// One error shown: Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'substring')
// const players = computed(() => useCollection(collection(db, `/gameplans/${gamePlanID}/participants`)));
// onErrorCaptured((error, vm, info) => {
// console.log("Error loading Summary component: ", error, "vm: ", vm, "info: ", info);
// throw error;
// });
</script>
</pre>
<p><strong>PlayerRow.vue</strong></p>
<pre class="brush:vue;toolbar:false;"><template>
<tr :key="player2.id" :data-playerid="player2.id">
<td>
<img class="playerImage" :src="playerPictureURL" />
{{ player2.nickname || player2.firstName + " " + player2.lastName }}
<span class="playerNumber">{{ player2.playerNumber }}</span>
</td>
</tr>
</template>
<script lang="ts" setup>
import { ref, PropType, computed, onMounted, watch } from "vue";
import { useFirebaseStorage, useStorageFileUrl } from "vuefire";
import { ref as storageRef } from 'firebase/storage';
import { Player, Inning } from "@/definitions/GamePlanner";
const fs = useFirebaseStorage();
const props = defineProps({
'player': { type: Object as PropType<Player>, required: true },
// 'innings': Array<Inning>
});
const player2 = ref(props.player);
// const innings = computed(() => props.innings);
// const playerPictureURL = computed(() => {
// const playerPictureFilename = `${player2.value.firstName.substring(0,1)}${player2.value.lastName}.png`.toLowerCase();
// const playerPictureResource = storageRef(fs, `playerPictures/${playerPictureFilename}`);
// return useStorageFileUrl(playerPictureResource).url.value as string;
// });
// const playerPictureURL = ref(() => {
// const playerPictureFilename = `${player2.value.firstName.substring(0,1)}${player2.value.lastName}.png`.toLowerCase();
// const playerPictureResource = storageRef(fs, `playerPictures/${playerPictureFilename}`);
// return useStorageFileUrl(playerPictureResource).url.value as string;
// });
const playerPictureURL = ref("https://www.gravatar.com/avatar/205e460b479e2e5b48aec07710c08d50");
async function getPlayerPictureURL() {
console.log("PlayerRow.ts getPlayerPictureURL");
const playerPictureFilename = `${player2.value.firstName.substring(0,1)}${player2.value.lastName}.png`.toLowerCase();
const playerPictureResource = await storageRef(fs, `playerPictures/${playerPictureFilename}`);
playerPictureURL.value = await useStorageFileUrl(playerPictureResource).url.value as string;
}
onMounted(() => {
console.log("PlayerRow.ts onMounted");
getPlayerPictureURL();
});
watch(playerPictureURL, (newVal, oldVal) => {
console.log("PlayerRow.ts watch playerPictureURL");
console.log("newVal: " + newVal);
console.log("oldVal: " + oldVal);
});
</script>
</pre>
<p>我的印象是 <code><Suspense></code> 需要包装 <code><PlayerRow></code> 组件,因为我正在使用 <code>storageRef</code> 和 < code>useStorageUrl</code> 方法,但它似乎引入了更多问题。根据 vuefire 文档并检查代码本身的定义,它们似乎并不是异步的,但是尝试立即调用它们不会产生立即/实际结果。</p>
<p><strong>相关软件包版本</strong></p>
<pre class="brush:json;toolbar:false;">{
"vue": "^3.2.45"
"firebase": "^9.15.0",
"typescript": "^4.9.3",
"vite": "^4.0.0",
"vue-router": "^4.1.6",
"vue-tsc": "^1.0.11",
"vuefire": "3.0.0-beta.6"
}
</pre></p>
根据这个文档我们应该使用
useCollection
包含collection
作为参数,如下所示:我发现您正在以正确的方式使用它,但您可以将其直接分配给
players
变量,而不是分配给ref
。当您尝试使用此反应式对象作为ref
对象的值时,这是不受支持的。您可以通过更改此行来解决您的问题:
致:
有关此主题的更多信息,您可以浏览以下文档