NextJS:从缓存加载的图像不会触发 onLoad 事件

IT技术 javascript reactjs next.js
2021-04-27 10:17:49

出于搜索引擎优化和社交媒体链接的原因,我目前正在将 ReactJS 客户端呈现的应用程序转换为 NextJS 应用程序。

转换的组件之一,基本上是等待加载完成然后淡入的图像,在 NextJS 环境中使用后无法按预期工作。

它的行为方式如下:

缓存已启用:

  • 第一次加载图像并触发 onLoad 事件从而显示图像。
  • 第二次图像保持隐藏状态,因为从缓存加载图像时不会触发 onLoad 事件。

使用 devtools 禁用缓存:

  • onLoad 事件始终有效,因为图像永远不会从缓存中提供。

预期行为和之前仅使用 ReactJS 实现的行为:

  • onLoad 事件应该触发图像是否从缓存中加载。

当不使用 React 时,这个问题通常是因为有人src在定义onload函数之前设置了图像

let img = new Image()
img.src = "img.jpg"
img.onload = () => console.log("Image loaded.")

应该是:

let img = new Image()
img.onload = () => console.log("Image loaded.")
img.src = "img.jpg"

这是在 NextJS 中导致相同问题的简化代码:

import React, { useState } from "react"

const Home = () => {
    const [loaded, setLoaded] = useState(false)

    const homeStyles = {
        width: "100%",
        height: "96vh",
        backgroundColor: "black"
    }

    const imgStyles = {
        width: "100%",
        height: "100%",
        objectFit: "cover",
        opacity: loaded ? 1 : 0
    }

    const handleLoad = () => {
        console.log("Loaded")
        setLoaded(true)
    }

    return (
        <div className="Home" style={homeStyles}>
            <img alt=""
                onLoad={handleLoad}
                src="https://images.unsplash.com/photo-1558981001-5864b3250a69?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1350&q=80"
                style={imgStyles}
            />
        </div>
    )
}

export default Home
4个回答

ImageObject.complete由于有人的建议,我最终将其用作解决方法。

我曾经useRef参考图像并检查image.current.complete === true组件是否安装。

这是代码:

import React, { useEffect, useRef, useState } from "react"

const Home = () => {
    const [loaded, setLoaded] = useState(false)

    const image = useRef()

    const homeStyles = {
        width: "100%",
        height: "96vh",
        backgroundColor: "black"
    }

    const imgStyles = {
        width: "100%",
        height: "100%",
        objectFit: "cover",
        opacity: loaded ? 1 : 0
    }

    const handleLoad = () => setLoaded(true)

    useEffect(() => {
        if (image.current.complete) setLoaded(true)
    }, [])

    return (
        <div className="Home" style={homeStyles}>
            <img alt=""
                ref={image}
                onLoad={handleLoad}
                src="https://images.unsplash.com/photo-1558981001-5864b3250a69?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1350&q=80"
                style={imgStyles}
            />
        </div>
    )
}

export default Home

如果您使用的Image是 nextjs 10 中不支持前向引用的新组件,您可以修改解决方法

const [loaded, setLoaded] = useState(false)
const ref = useRef<HTMLDivElement>(null)
useEffect(() => {
  if ((ref.current?.firstChild?.firstChild as HTMLImageElement | undefined)?.complete) {
    setLoaded(true)
  }
}, [])

return <div ref={ref}>
         <Image src={src} 
           onLoad={() => setLoaded(true)}/>
       </div>

从 Next.js v11.0.2-canary.4开始,可以直接使用onLoadingCompleteprop。

<Image src={src} onLoadingComplete={() => setLoaded(true)} />

这与其他选项相比如何?

  • 您可以继续使用很棒的next/image组件而不是未优化的img标签,而无需任何额外的代码或自己处理 refs。

我只想添加到@mohamed-seif-khalid 的答案中,如果 ref 包含任何内容,我必须在 useEffect 中添加一个检查,因为我的渲染继续中断。

  useEffect(() => {
    if (image.current && image.current.complete) {
      handleImageLoad()
    }
  }, []);