10

最近,我一直在 NextJS 的一个项目中工作,它使用 YoutubeAPI 来获取视频信息,包括缩略图 URL。

全分辨率图像的缩略图 URL 如下所示: https://i.ytimg.com/vi/${videoId}/maxresdefault.jpg

但是,有时 YouTube 无法生成全分辨率图像,在这种情况下,图像不会显示在我的网页上。

如果带有 URL 的图像https://i.ytimg.com/vi/${videoId}/maxresdefault.jpg不存在,我希望使用另一个 URL,例如https://i.ytimg.com/vi/${videoId}/hqdefault.jpg

处理此问题的最佳方法是什么next/image

4

3 回答 3

20

您可以创建一个自定义图像组件,该组件扩展内置next/image并在图像加载失败时通过触发onError回调添加回退逻辑。

import React, { useState } from 'react';
import Image from 'next/image';

const ImageWithFallback = (props) => {
    const { src, fallbackSrc, ...rest } = props;
    const [imgSrc, setImgSrc] = useState(src);

    return (
        <Image
            {...rest}
            src={imgSrc}
            onError={() => {
                setImgSrc(fallbackSrc);
            }}
        />
    );
};

export default ImageWithFallback;

然后,您可以直接使用自定义组件,而不是next/image如下:

<ImageWithFallback
    key={videoId}
    layout="fill"
    src={`https://i.ytimg.com/vi/${videoId}/maxresdefault.jpg`}
    fallbackSrc={`https://i.ytimg.com/vi/${videoId}/hqdefault.jpg`}
/>

传递一个key道具以触发更改时的重新渲染videoId

于 2021-04-05T12:48:51.370 回答
8

@juliomalves 给出了 99% 的答案,但我想补充一下。在他的解决方案中更改 src 时出现问题,因为图像不会更新,因为它正在获取未更新的 imgSrc 值。这是我对他的回答的补充:

import React, { useState } from 'react';
import Image from 'next/image';

const ImageFallback = (props) => {
    
    const { src, fallbackSrc, ...rest } = props;
    const [imgSrc, setImgSrc] = useState(false);
    const [oldSrc, setOldSrc] = useState(src);
    if (oldSrc!==src)
    {
        setImgSrc(false)
        setOldSrc(src)
    }
    return (
        <Image
            {...rest}
            src={imgSrc?fallbackSrc:src}
            onError={() => {
                setImgSrc(true);
            }}
        />
    );
};

export default ImageFallback;

现在 imgSrc 仅用作一个标志,并且有 src 值的跟踪,这有助于更改图像,即使您之前有一个具有后备图像的图像。

于 2021-04-22T12:32:34.240 回答
2

这些答案很有帮助,但是有一种方法可以通过利用钩子来实现这一点,而无需key每次都通过:useEffect

useEffect(() => {
    set_imgSrc(src);
}, [src]);

此外,onError对于某些图像,该事件似乎不会触发(我相信layout='fill'在某些情况下不会触发它),对于那些我一直在使用的情况onLoadingComplete,然后我检查图像的宽度是否为 0

onLoadingComplete={(result) => {
    if (result.naturalWidth === 0) {  // Broken image
        set_imgSrc(fallbackSrc);
    }
}}

完整代码:

import Image from "next/image";
import { useEffect, useState } from "react";

export default function ImageFallback({ src, fallbackSrc, ...rest }) {
  const [imgSrc, set_imgSrc] = useState(src);

  useEffect(() => {
    set_imgSrc(src);
  }, [src]);

  return (
    <Image
      {...rest}
      src={imgSrc}
      onLoadingComplete={(result) => {
        if (result.naturalWidth === 0) {
          // Broken image
          set_imgSrc(fallbackSrc);
        }
      }}
      onError={() => {
        set_imgSrc(fallbackSrc);
      }}
    />
  );
}
于 2021-12-31T16:28:28.040 回答