NextJS:从缓存中加载的图像不会触发 onLoad 事件
NextJS: Images loaded from cache don't trigger the onLoad event
出于搜索引擎优化和社交媒体链接的原因,我目前正在将 ReactJS 客户端呈现的应用程序转换为 NextJS 应用程序。
其中一个转换的组件(基本上是一个等待加载完成然后淡入的图像)在 NextJS 环境中使用后无法按预期工作。
它的行为方式如下:
启用缓存:
- 第一次加载图片并触发 onLoad 事件从而显示图片。
- 第二次图像保持隐藏状态,因为从缓存加载图像时未触发 onLoad 事件。
使用 devtools 禁用缓存:
- onLoad 事件始终有效,因为图像永远不会从缓存中提供。
预期行为和之前仅使用 ReactJS 实现的行为:
- onLoad 事件应该触发图像是否从缓存中加载。
当不使用 React 时,这个问题通常是由于有人在定义 onload
函数之前设置图像 src
引起的:
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
由于某人的建议,我最终使用 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
如果您使用的是来自 nextjs 10 的新 Image
组件,它不支持前向引用,您可以使用
修改解决方法
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 onwards, one can directly use onLoadingComplete
prop.
<Image src={src} onLoadingComplete={() => setLoaded(true)} />
这与其他选项相比如何?
- 您可以继续使用很棒的
next/image
组件而不是未优化的 img
标签,无需任何额外代码或自己处理 refs。
我只想添加到 @mohamed-seif-khalid 的回答中,如果 ref 包含任何内容,我必须在 useEffect 中添加一个检查,因为我的渲染继续中断。
useEffect(() => {
if (image.current && image.current.complete) {
handleImageLoad()
}
}, []);
出于搜索引擎优化和社交媒体链接的原因,我目前正在将 ReactJS 客户端呈现的应用程序转换为 NextJS 应用程序。
其中一个转换的组件(基本上是一个等待加载完成然后淡入的图像)在 NextJS 环境中使用后无法按预期工作。
它的行为方式如下:
启用缓存:
- 第一次加载图片并触发 onLoad 事件从而显示图片。
- 第二次图像保持隐藏状态,因为从缓存加载图像时未触发 onLoad 事件。
使用 devtools 禁用缓存:
- onLoad 事件始终有效,因为图像永远不会从缓存中提供。
预期行为和之前仅使用 ReactJS 实现的行为:
- onLoad 事件应该触发图像是否从缓存中加载。
当不使用 React 时,这个问题通常是由于有人在定义 onload
函数之前设置图像 src
引起的:
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
由于某人的建议,我最终使用 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
如果您使用的是来自 nextjs 10 的新 Image
组件,它不支持前向引用,您可以使用
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 onwards, one can directly use onLoadingComplete
prop.
<Image src={src} onLoadingComplete={() => setLoaded(true)} />
这与其他选项相比如何?
- 您可以继续使用很棒的
next/image
组件而不是未优化的img
标签,无需任何额外代码或自己处理 refs。
我只想添加到 @mohamed-seif-khalid 的回答中,如果 ref 包含任何内容,我必须在 useEffect 中添加一个检查,因为我的渲染继续中断。
useEffect(() => {
if (image.current && image.current.complete) {
handleImageLoad()
}
}, []);