在 NextJS 中拥有后备图像的最佳方式是什么?

问题描述 投票:0回答:8

最近,我一直在 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

javascript reactjs next.js youtube-api nextjs-image
8个回答
56
投票

您可以创建一个自定义图像组件,扩展内置的

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
更改的重新渲染。


23
投票

这些答案很有帮助,但有一种方法可以实现这一目标,而无需每次都通过利用

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);
      }}
    />
  );
}

11
投票

有 @juliomalves 的打字稿解决方案

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

interface ImageWithFallbackProps extends ImageProps {
  fallbackSrc: string
}

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

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

export default ImageWithFallback


10
投票

@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 值的跟踪,这有助于更改图像,即使您之前有一个具有后备图像的图像。


6
投票

以上解决方案都不适合我。但是,当我使用“onErrorCapture”而不是“onError”时,它起作用了! =)


2
投票
<Image
    loader={({ src }) => src}
    src={imageUrl ? imageUrl : "/images/no-thumbnail.jpg"}
    width={500}
    height={500}
    alt={imageUrl ? imageUrl : "/images/no-thumbnail.jpg"}
    onError={(event) => {
        event.target.id = "/images/no-thumbnail.jpg";
        event.target.srcset = "/images/no-thumbnail.jpg";
    }}
 />

onError 可以设置图片srcset。通过 image srcset nextjs image 显示图像。


1
投票

这是我尝试使用

<Avatar>
<Avatar.Image>
子组件构建
<Avatar.Fallback>
父组件。

首先,我构建了

<Avatar.Image>
组件来显示我们的头像图像。

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

function AvatarImage({ src, alt, className, ...props }) {
  const [imageSuccessfullyLoaded, setImageSuccessfullyLoaded] = useState(true);

  return (
    <div>
      {imageSuccessfullyLoaded && (
        <Image
          src={src}
          alt={alt}
          placeholder="empty"
          onError={() => {
            setImageSuccessfullyLoaded(false);
          }}
          fill
          {...props}
        />
      )}
    </div>
  );
}

export default AvatarImage;

一旦

AvatarImage
组件加载失败,我就让
AvatarFallback
组件填充背景。

import React from 'react';
import classNames from 'classnames';

function AvatarFallback({ children, className }) {
  return (
    <div
      className={classNames(
        'text-lg font-medium text-neutral-600 dark:text-neutral-200',
        className
      )}
    >
      {children}
    </div>
  );
}

export default AvatarFallback;

父组件名为

Avatar

import React from 'react';
import classNames from 'classnames';
import AvatarImage from './AvatarImage/AvatarImage';
import AvatarFallback from './AvatarFallback/AvatarFallback';


function Avatar({ className, children }) {
  return (
    <div
      className={classNames(
        'relative flex items-center justify-center overflow-hidden rounded-full bg-neutral-200 dark:bg-neutral-600',
        className
      )}
    >
      {children}
    </div>
  );
}

Avatar.Image = AvatarImage;
Avatar.Fallback = AvatarFallback;

export default Avatar;

下面是我如何使用它的示例: (别忘了改变

${videoId}

<Avatar>
  <Avatar.Image src="https://i.ytimg.com/vi/${videoId}/maxresdefault.jpg" alt="Avatar image" />
  <Avatar.Fallback>
    <Image
      src="https://i.ytimg.com/vi/${videoId}/hqdefault.jpg"
      fill
      alt="Avatar image"
    />
  </Avatar.Fallback>
</Avatar>

0
投票
'use client'
import Image from 'next/image'
import React, { useState } from 'react'


const ImageToast = ({image}: any) => {
    const [imgSrc, setImgSrc] = useState(false);
    return (
        <Image  
            src={imgSrc ? image : '/img/loader.jpg'} 
            onError={() => setImgSrc(true)}
            alt = ''
            fill
        />
    )
}

export default ImageToast
© www.soinside.com 2019 - 2024. All rights reserved.