等待所有图片加载完成后再显示组件

3
我正在使用Vue 3,希望能够在加载完所有图片(Album Card内)后再显示组件。以下是当前的效果和我的代码。
有没有人有实现这个想法的思路?
目前组件先显示,然后再加载图片,这不是一个完美的用户体验。 example
<template>
  <div class="content-container">
    <div v-if="isLoading" style="width: 100%">LOADING</div>
    <album-card
      v-for="album in this.albums"
      :key="album.id"
      :albumTitle="album.title"
      :albumId="album.id"
      :albumPhotos="album.thumbnailPhotos.map((photo) => photo)"
    ></album-card>
  </div>
</template>

<script lang="ts">
import { defineComponent } from "vue";
import albumCard from "@/components/AlbumCard.vue";

interface Album {
  userId: number;
  id: number;
  title: string;
  thumbnailPhotos: Array<Photo>;
}

interface Photo {
  albumId: number;
  id: number;
  title: string;
  url: string;
  thumbnailUrl: string;
}

export default defineComponent({
  name: "Albums",
  components: {
    albumCard,
  },
  data() {
    return {
      albums: [] as Album[],
      isLoading: false as Boolean,
    };
  },
  methods: {
    async getAlbums() {
      this.isLoading = true;
      let id_param = this.$route.params.id;
      fetch(
        `https://jsonplaceholder.typicode.com/albums/${
          id_param === undefined ? "" : "?userId=" + id_param
        }`
      )
        .then((response) => response.json())
        .then((response: Album[]) => {
          //api returns array, loop needed
          response.forEach((album: Album) => {
            this.getRandomPhotos(album.id).then((response: Photo[]) => {
              album.thumbnailPhotos = response;
              this.albums.push(album);
            });
          });
        })
        .then(() => {
          this.isLoading = false;
        });
    },
    getRandomPhotos(albumId: number): Promise<Photo[]> {
      var promise = fetch(
        `https://jsonplaceholder.typicode.com/photos?albumId=${albumId}`
      )
        .then((response) => response.json())
        .then((response: Photo[]) => {
          const shuffled = this.shuffleArray(response);
          return shuffled.splice(0, 3);
        });

      return promise;
    },
    /*
     Durstenfeld shuffle by stackoverflow answer: 
     https://dev59.com/IXE95IYBdhLWcg3wHqMV#12646864
    */
    shuffleArray(array: Photo[]): Photo[] {
      for (let i = array.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [array[i], array[j]] = [array[j], array[i]];
      }
      return array;
    },
  },
  created: function () {
    this.getAlbums();
  },
});
</script>

悬念可能是你正在寻找的东西... - Michal Levý
@MichalLevý 谢谢你的建议,但我用了不同的方法解决了这个问题,请看答案。 - jckob
1个回答

1
我解决这个问题的方法是在album-card组件内部的(img) html标签上使用on load事件函数。当图片正在加载时,会显示一个加载动画。在三张图片加载完成后,将组件显示在屏幕上。
<template>
  <router-link
    class="router-link"
    @click="selectAlbum(albumsId)"
    :to="{ name: 'Photos', params: { albumId: albumsId } }"
  >
    <div class="album-card-container" v-show="this.numLoaded == 3">
      <div class="photos-container">
        <img
          v-for="photo in this.thumbnailPhotos()"
          :key="photo.id"
          :src="photo.thumbnailUrl"
          @load="loaded()"
        />
      </div>
      <span>
        {{ albumTitle }}
      </span>
    </div>
    <div v-if="this.numLoaded != 3" class="album-card-container">
      <the-loader></the-loader>
    </div>
  </router-link>
</template>

<script lang="ts">
import { defineComponent } from "vue";
import { store } from "@/store";
export default defineComponent({
  name: "album-card",
  props: {
    albumTitle: String,
    albumsId: Number,
  },
  data: function () {
    return {
      store: store,
      numLoaded: 0,
    };
  },
  methods: {
    thumbnailPhotos() {
      return this.$attrs.albumPhotos;
    },
    selectAlbum(value: string) {
      this.store.selectedAlbum = value;
    },
    loaded() {
      this.numLoaded = this.numLoaded + 1;
    },
  },
});
</script>

使用这种方法的重要注意事项是在div上使用v-show而不是v-if。v-show将元素放置在html中(并设置display:none),而v-if不会在html中呈现元素,因此图像永远不会被加载。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接