如何让 Vuex 在 Vue 中添加来自 Firebase getDownloadURL 承诺的 url

How to get Vuex to add a urls from a Firebase getDownloadURL promise in Vue

我有一个使用 Firestore Storage JavaScript 版本 9、Vuex 4 和 Vue 3 的应用程序。它需要从 Firestore Storage 下载图像 url 以显示在图像标签上。

我的 Firestore 存储结构 listings/${userID}/ 中包含该用户 ID 的图像文件。

我有一个组件需要图像 url 并试图从 Firestore 获取它们(第 35 行)。

Listing.vue

<template>
  <div class="listing__container">
    <div class="listing__container_info">
      <h1 class="listing__header">{{ listing.title }}</h1>
      <p class="listing__paragraph">{{ listing.description }}</p>
      <div v-if="!isNaN(listing.stars)">
        <icon-star-line v-for="star in Number(listing.stars)" class="listing__icon" />
      </div>
    </div>
    <div class="listing__container_image">
      <img class="listing__image" :src="listingImageUrls[0]" alt="an image" />
    </div>
    <div class="listing__container_image">
      <img :src="listingImageUrls[1]" alt />
    </div>
  </div>
</template>

<script setup>
const props = defineProps({
  listing: Object,
})
import { computed, ref, toRefs } from 'vue'
import IconStarLine from '~icons/clarity/star-solid'
import { useStore } from 'vuex'

const store = useStore()
const { listing } = toRefs(props)

const userId = 'vlM46kRbhym3a3PW76t4'
store.dispatch('fetchUser', userId)
const user = computed(() => store.state.user)

store.dispatch('fetchListingImageUrls', user.value.id)
const listingImageUrls = computed(() => store.state.listingImageUrls)
</script>

<style lang="postcss">
.listing {
  @apply border-2 border-ui-100 rounded-3xl flex;
  &__header {
    @apply text-primary-900 text-3xl font-bold;
  }
  &__paragraph {
    @apply font-normal;
  }
  &__icon {
    @apply text-primary-500 inline;
  }
  &__container {
    @apply flex;
    &_info {
    }
    &_image {
      @apply w-52 h-36 overflow-hidden;
      .listing__image {
        /* @apply relative top-1/2 left-1/2; */
      }
    }
  }
}
</style>

我有另一个处理状态并正在调用 getDownloadURL 的 Vuex 脚本(第 57 行到文件末尾)

store/index.js

import { createStore } from 'vuex'
import { initializeApp } from 'firebase/app'
import { getFirestore, collection, doc, addDoc, getDoc, getDocs, Timestamp } from 'firebase/firestore'
import { getStorage, ref, getDownloadURL, list } from 'firebase/storage'

const firebaseApp = initializeApp({
  apiKey: '********',
  authDomain: '********',
  projectId: '********',
  storageBucket: '********',
  messagingSenderId: '********',
  appId: '********',
  measurementId: '********'
})

const db = getFirestore()
const storage = getStorage()

export const store = createStore({
  state: {
    user: {},
    listings: [],
    listingImageUrls: []
  },
  mutations: {
    addUser(state, user) {
      state.user = user
    },
    addListings(state, listings) {
      state.listings = listings
    },
    addListingImageUrls(state, urls) {
      state.listingImageUrls = urls
    }
  },
  actions: {
    async fetchUser({ commit }, userId) {
      const userRef = doc(db, `/users/${userId}`)
      const userSnap = await getDoc(userRef)
      commit('addUser', userSnap.data())
      if (!userSnap.exists()) {
        // doc.data() will be undefined in this case
        console.log("No such document!")
      }
    },
    async fetchListings({ commit }, userId) {
      const listingsRef = collection(db, `/users/${userId}/listings`)
      const listingsSnap = await getDocs(listingsRef)
      const listingsClean = []
      listingsSnap.forEach(doc => {
        let docClean = doc.data()
        listingsClean.push(docClean)
      })
      const listings = listingsClean
      commit('addListings', listings)
    },
    async fetchListingImageUrls({ commit }, userId) {
      const folderRef = await ref(storage, `listings/${userId}`)
      const imagesList = await list(folderRef)
      commit('addListingImageUrls', await getUrls(imagesList))
    },
  }
})

const getUrls = async (imagesList) => {
  const urls = imagesList.items.map(async imageRef => {
    const url = await getDownloadURL(imageRef)
    console.log(url)
    return url
  })
  return urls
}

这是我的 package.json

{
  "name": "someApp",
  "version": "0.0.0",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "serve": "vite preview"
  },
  "dependencies": {
    "firebase": "^9.4.1",
    "postcss-import": "^14.0.2",
    "vue": "^3.2.16",
    "vue-router": "^4.0.12",
    "vuex": "^4.0.2"
  },
  "devDependencies": {
    "@iconify-json/clarity": "^1.0.1",
    "@vitejs/plugin-vue": "^1.9.3",
    "autoprefixer": "^10.4.0",
    "eslint": "^8.2.0",
    "eslint-config-prettier": "^8.3.0",
    "eslint-plugin-vue": "^8.0.3",
    "postcss": "^8.4.4",
    "postcss-nested": "^5.0.6",
    "prettier": "^2.4.1",
    "tailwindcss": "^2.2.19",
    "unplugin-icons": "^0.12.23",
    "vite": "^2.6.4"
  }
}

Listing.vue 正在从 store/index.js(第 57 行及以下)调用第 35 行的 fetchListingImageURls 以从 Firebase 存储获取图像 url。使用此代码,图像不会显示。如果我在 listings.vue 中记录 listingImageUrls.value 它是一个目标为空数组的代理。如果我从 getURLs 中的商店登录 urls,它会将 url 显示为已履行的承诺。

这里有什么问题?我对 Vuex 和 Firestore 还很陌生,所以我想我可能对其中一个做错了什么,或者像往常一样,我在用我的异步调用进行屠杀。

感谢任何帮助:)

想通了:

是 map 语句没有等待所有承诺从 getDownloadURL 解析。我需要像这样 Promise.all() 那样

export const fetchImageUrls = async (userId) => {
  const folderRef = ref(storage, `listings/${userId}`)
  const imagesList = await list(folderRef)
  return Promise.all(imagesList.items.map(async imageRef => await getDownloadURL(imageRef)))
}

然后效果很好