Vue3中使用解构无法从组合式函数获取数据的问题
Vue组合式函数从Firebase取数无报错但页面不展示的问题
我在Vue组件(Book.vue)中尝试从Firebase获取图书数据,组件内部写函数时运行正常,但抽离为组合式函数后无法正常工作——控制台无报错,但页面始终不展示数据。
以下代码可正常运行(
showBooks作为组件内函数)
Book.vue
<template> <!-- books list --> <div v-for="book in books" :key="book.id" class="book"> <span class="material-icons"> favorite </span> <h2>{{ book.title }}</h2> <div> <span class="material-icons"> done </span> <span class="material-icons"> edit </span> <span class="material-icons"> delete </span> </div> </div> </template> <script setup> import { ref } from "@vue/reactivity"; import { collection, getDocs } from "firebase/firestore"; import { db } from "../firebase/config"; import { onMounted } from "@vue/runtime-core"; const books = ref([]); async function showBooks() { const colRef = collection(db, "books"); const docs = await getDocs(colRef); docs.forEach((doc) => { const book = { ...doc.data(), id: doc.id }; books.value.push(book); }); } onMounted(() => { showBooks(); }); </script>
以下代码无法运行(
showBooks抽离到组合式函数中)
getBooks.js (组合式函数)
import { ref } from "@vue/reactivity"; import { collection, getDocs } from "firebase/firestore"; import { db } from "../firebase/config"; async function showBooks() { const books = ref([]); const colRef = collection(db, "books"); const docs = await getDocs(colRef); docs.forEach((doc) => { const book = { ...doc.data(), id: doc.id }; books.value.push(book); }); return { books } } export { showBooks }
Books.vue
<template> <!-- books list --> <div v-for="book in books" :key="book.id" class="book"> <span class="material-icons"> favorite </span> <h2>{{ book.title }}</h2> <div> <span class="material-icons"> done </span> <span class="material-icons"> edit </span> <span class="material-icons"> delete </span> </div> </div> </template> <script setup> import { ref } from "@vue/reactivity"; import { onMounted } from "@vue/runtime-core"; import { showBooks } from "../use/getBooks.js"; const { books } = showBooks(); onMounted(() => { showBooks(); }); </script>
问题根源
showBooks是异步函数,直接解构const { books } = showBooks()得到的是Promise对象,不是实际的响应式ref,模板无法绑定响应式数据。onMounted中再次调用showBooks(),会创建一个全新的ref实例,和模板绑定的books不是同一个,数据更新不会同步到页面。
修复方案
方案1:调整组合式函数结构(推荐)
将响应式ref提前创建并返回,异步逻辑单独封装为方法,确保组件始终绑定同一个ref实例:
修改getBooks.js:
import { ref } from "@vue/reactivity"; import { collection, getDocs } from "firebase/firestore"; import { db } from "../firebase/config"; function useBooks() { // 提前创建响应式ref const books = ref([]); async function fetchBooks() { const colRef = collection(db, "books"); const docsSnap = await getDocs(colRef); // 用map替代forEach,直接赋值更高效 books.value = docsSnap.docs.map(doc => ({ ...doc.data(), id: doc.id })); } return { books, fetchBooks }; } export { useBooks };
修改Books.vue:
<template> <!-- books list --> <div v-for="book in books" :key="book.id" class="book"> <span class="material-icons"> favorite </span> <h2>{{ book.title }}</h2> <div> <span class="material-icons"> done </span> <span class="material-icons"> edit </span> <span class="material-icons"> delete </span> </div> </div> </template> <script setup> import { onMounted } from "@vue/runtime-core"; import { useBooks } from "../use/getBooks.js"; // 获取同一个ref实例和异步方法 const { books, fetchBooks } = useBooks(); onMounted(() => { fetchBooks(); }); </script>
方案2:使用await结合Suspense
如果需要异步返回数据,可在组件中使用await,同时在外层组件包裹Suspense处理加载状态:
修改getBooks.js:
import { ref } from "@vue/reactivity"; import { collection, getDocs } from "firebase/firestore"; import { db } from "../firebase/config"; async function useBooks() { const books = ref([]); const colRef = collection(db, "books"); const docsSnap = await getDocs(colRef); books.value = docsSnap.docs.map(doc => ({ ...doc.data(), id: doc.id })); return { books }; } export { useBooks };
修改Books.vue:
<template> <!-- books list --> <div v-for="book in books" :key="book.id" class="book"> <span class="material-icons"> favorite </span> <h2>{{ book.title }}</h2> <div> <span class="material-icons"> done </span> <span class="material-icons"> edit </span> <span class="material-icons"> delete </span> </div> </div> </template> <script setup> import { useBooks } from "../use/getBooks.js"; // await异步组合式函数 const { books } = await useBooks(); </script>
在父组件中添加Suspense:
<Suspense> <template #default> <Books /> </template> <template #fallback> <div>加载图书数据中...</div> </template> </Suspense>
内容的提问来源于stack exchange,提问作者IndabCoding
相关产品推荐
相关产品推荐

