Как я могу предотвратить временную загрузку изображения-заполнителя в этом приложении Vue 3?

Я работаю над SPA с API Vue 3, TypeScript и The Movie Database (TMDB).

Я хочу отобразить постер фильма на странице сведений о фильме, если есть постер, в противном случае я хочу отобразить заполнитель (generic-poster-big.png).

Для этого в src\components\MovieDetails.vue у меня есть:

<template>
  <div class = "poster-container text-center text-sm-start my-3">
    <img
      :src = "genericPoster"
      :alt = "movie?.title"
      class = "img-fluid shadow-sm"
    />
  </div>
</template>

<script lang = "ts">
import { defineComponent } from "vue";
import { useRoute } from "vue-router";
import axios from "axios";
import env from "../env";
import { Movie } from "../models/Movie";

export default defineComponent({
  name: "MovieDetails",

  data() {
    return {
      route: useRoute(),
      genericPosterBig: require("../assets/generic-poster-big.png"),
      movie: null as Movie | null
    };
  },

  mounted() {
    this.getMovieDetails();
  },

  methods: {
    getMovieDetails() {
      axios
        .get(
          `${env.api_url}/movie/${this.route.params.id}?api_key=${env.api_key}`
        )
        .then((response) => {
          this.movie = response.data;
        })
        .catch((err) => console.info(err));
    },

  computed: {
    genericPoster() {
      return !this.movie?.poster_path
        ? this.genericPosterBig
        : `https://image.tmdb.org/t/p/w500/${this.movie?.poster_path}`;
    },
  }
});
</script>

Проблема

Когда есть постер фильма, перед его загрузкой на долю секунды появляется общий постер, но видимый.

Stackblitz

Вы можете увидеть Stackblitz ЗДЕСЬ.

Вопросы

  1. Что я делаю не так?
  2. Какой самый надежный способ решить эту проблему?

Вы должны загружать общий постер только в том случае, если изображение не загружается или если у вас нет URL-адреса для этого фильма, и поместить загрузчик до того, как URL-адрес изображения станет доступен. Поместите v-if = "movie" на свой тег img и поместите загрузчик вместо v-else. Затем загрузите свое изображение или общий постер, если у вас нет URL-адреса изображения, но есть фильм. Вы можете добавить прослушиватель @error поверх этого, чтобы поместить общий постер в img в случае ошибки с вашим постером фильма.

Lk77 10.05.2023 08:59
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
1
78
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Что-то вроде следующего должно помочь, добавлены строки, отмеченные **********

<template>
  <div class = "poster-container text-center text-sm-start my-3">
    <!-- ************ -->
    <img v-if = "loaded"
      :src = "genericPoster"
      :alt = "movie?.title"
      class = "img-fluid shadow-sm"
    />
  </div>
</template>

<script lang = "ts">
import { defineComponent } from "vue";
import { useRoute } from "vue-router";
import axios from "axios";
import env from "../env";
import { Movie } from "../models/Movie";

export default defineComponent({
  name: "MovieDetails",

  data() {
    return {
      //************************
      loaded: false, 
      route: useRoute(),
      genericPosterBig: require("../assets/generic-poster-big.png"),
      movie: null as Movie | null
    };
  },

  mounted() {
    this.getMovieDetails();
  },

  methods: {
    getMovieDetails() {
      axios
        .get(
          `${env.api_url}/movie/${this.route.params.id}?api_key=${env.api_key}`
        )
        .then((response) => {
          this.movie = response.data;
        })
        .catch((err) => console.info(err))
        //****************
        .finally(() => this.loaded = true); 
    },

  computed: {
    genericPoster() {
      return !this.movie?.poster_path
        ? this.genericPosterBig
        : `https://image.tmdb.org/t/p/w500/${this.movie?.poster_path}`;
    },
  }
});
</script>

Такое поведение ожидается с учетом вашего решения, но я понимаю, что вы хотите улучшить его по причинам UX.

Проблема в том, что ваш компонент монтируется до того, как данные станут доступны, поэтому вам нужно установить логику того, что делать во время его загрузки.

Я вижу два решения для этого:

  1. Устанавливайте замещающее изображение только в том случае, если основное изображение не загружается (т. е. если URL-адрес неверный или пустой). Таким образом, ваш заполнитель будет загружаться только после попытки загрузить основное изображение, а не раньше.

Пока он загружается, используйте счетчик загрузки на вашем изображении во время его загрузки, а не заполнитель изображения (который должен появиться только после того, как будут получены детали постера). Это полезно, чтобы избежать мерцания макета: он резервирует пространство, необходимое для изображения.

<template>
  <div class = "poster-container text-center text-sm-start my-3">
    <div v-if = "loading">Loading spinner...</div>
    <img
      v-else
      :src = "movie.poster_path"
      :alt = "movie.title"
      class = "img-fluid shadow-sm"
      @error = "setPlaceholder"
    />
  </div>
</template>

<script>
{
  data() {
    return {
       movie: null,
       loading: true,
       genericPosterBig: null,
    }
  }
  mounted() {
    this.getMovieDetails()
  },
  methods: {
    getMovieDetails() {
      this.loading = true
      axios.get(`${env.api_url}/movie/${this.route.params.id}?api_key=${env.api_key}`)
        .then(({ data }) => {
          this.movie = data;
        })
        .catch((err) => console.info(err))
        .finally(() => {
          this.loading = false
        });
    },
    setPlaceholder(event) {
       // This loads the image only if it's not already assigned
       this.genericPosterBig ||= require("../assets/generic-poster-big.png")
       event.target.src = this.genericPosterBig
    }
  }
}
</script>
  1. Используя инфраструктуру Nuxt, вы можете предварительно загрузить сведения о фильмах перед монтированием страницы сведений о фильме, используя компонуемый useAsyncData() (или для option API), поэтому вы уверены, что у вас уже есть все данные о фильмах при создании страница сведений (поэтому не мерцает).
{
  // This will block the page rendering until the promise is settled.
  async asyncData() {
     const movieDetails = await axios
        .get(`${env.api_url}/movie/${this.route.params.id}?api_key=${env.api_key}`)
        .then((response) => response.data)
     return { movieDetails }
  }
}

поскольку значение загрузки «по умолчанию» равно false, не приведет ли это к «вспышке» пустого тега <img>? так как вы также удалили определение genericPoster, так что же будет делать :src = "genericPoster"?

Jaromanda X 10.05.2023 09:31

Я имею в виду, что это просто пример реализации, а не копируемое решение. Я обновил его, чтобы код стал более правильным, но вы действительно должны изменить несколько вещей.

Kapcash 10.05.2023 12:47

Другие вопросы по теме

Похожие вопросы