如何在React JS组件中预加载图像?

9

当出现signInError时,我当前正在渲染一个子组件。如果signInError不为null,则会在父组件中存储,然后渲染<SignInError/>组件,如下面的代码所示:

ParentComponent.js

  // Some code...

  render() {
    return(
      <div>
        <SignInForm
          doSignIn={this.doSignIn}
          resetSignInError={this.resetSignInError}
          signInError={this.state.signInError}
        />
        {this.state.signInError && <SignInError/>}
      </div>
    );
  }

到目前为止,一切都很好,这是子组件SignInError.js
import React from 'react';
import RoundImage from '../../../UI/Common/RoundImage';
import Newman from '../../../../assets/newman-min.png';

class SignInError extends React.Component {
  constructor(props){
    super(props);
  }

    componentDidMount(){
    const img = new Image();
    img.src = Newman;
  }

  render(){
    return(
      <div>
      <div>
        <RoundImage src={img.src}/> // <--- img is undefined here!!!
      </div>
      <div>
        Hello... Newman!
      </div>
    </div>
    );
  }
}

export default SignInError;

RoundImage.js

import React from 'react';

const RoundImage = (props) => {
  return (
    <div>
      <img src={props.src}/>
    </div>
  );
}

export default RoundImage;

如何在React.js中预加载图片?

这个问题的答案(链接如上)告诉我要在componentDidMount()方法内创建img对象以强制浏览器加载它。因此,正如您从上面的代码中可以看到的那样,我已经这样做了。但是现在,在我的render方法中尝试将其作为prop传递给我的孙子组件时,我无法访问img,因为它是在另一个方法内定义的。

有什么好的解决方法吗?我只需要加载图像并与错误消息一起显示。否则,如果您的浏览器还没有缓存它,错误消息将在图像之前显示。谢谢帮助。


"RoundImage"是什么? - Diodeus - James MacFarlane
如果RoundImage需要一个路径,为什么不直接使用:<RoundImage src={Newman}/> - Diodeus - James MacFarlane
我刚刚编辑添加了RoundImage.js的代码。谢谢。 - cbdeveloper
@Diodeus-JamesMacFarlane 我试过了。它可以工作,但它不会预加载我的图像。在图像之前会显示“Hello Newman!”然后,一旦浏览器下载它,它才会加载... - cbdeveloper
如果你想要预加载它,你需要在应用程序的更高位置创建新的Image()。到组件加载时,它仍然没有图像。你不需要引用这个图像,你只是想强制浏览器获取它。当你在组件中使用它时,它已经被缓存了。 - Diodeus - James MacFarlane
3个回答

27

图片下载在浏览器中进行。DOM 渲染也在浏览器中进行。

你所说的 预加载,是指只有当图片准备好时组件才会渲染吗?

如果是的话,可以像这样做:


componentDidMount() {
  const img = new Image();
  img.onload = () => {
    // when it finishes loading, update the component state
    this.setState({ imageIsReady: true });
  }
  img.src = Newman; // by setting an src, you trigger browser download

}

render() {
  const { imageIsReady } = this.state;

  if (!imageIsReady) {
    return <div>Loading image...</div>; // or just return null if you want nothing to be rendered.
  } else {
    return <img src={Newman} /> // along with your error message here
  }
}

这正是我一直在寻找的。只需将你的回答中的 this.state({ imageIsReady: true }); 更改为 this.setState(...),它就能像我预期的那样工作了。非常感谢。 - cbdeveloper
1
啊,是的,感谢您的纠正。我已经修复了我的回答中的拼写错误。 - Jackyef
1
img.src 应该放在 img.onload 之后。否则,如果图像在浏览器中被缓存,就有可能导致 onload 不触发。 - Eksapsy

11

有一种不同的方法,但如果您提前拥有图像源,则可以将图像源作为具有预加载选项的链接引用添加到主HTML文件中。浏览器将预加载图像(具有相对较低的优先级),并且在您的应用程序加载图像时,它应该被缓存在浏览器内存中。

<head>
..
..
<link rel="preload" href="<your_image_source_here>" as="image">
...
</head>

在这种方法中,我们将预加载过程与代码分开。当您事先拥有图像源(而不是动态生成)并且不需要缓存大量图像时,这更加相关(虽然可能会在html头部添加大量链接列表,但有点混乱)。

您可以在此处了解有关链接预加载的更多信息:使用rel =“preload”预加载内容


谢谢你的帮助。我真的需要在组件内部处理它。 - cbdeveloper
我已经使用import Newman from '../../../../assets/newman-min.png';导入了它。换成require会有任何区别吗? - cbdeveloper
不,它实际上是等价的 (: - Dan Porat

0
在我的情况下,我从我的图像开始使用初始的src属性,并希望在浏览器加载图像后延迟更改它们,因此我用Typescript编写了以下钩子:
import { useEffect, useState } from 'react';

export const useImageLoader = (initialSrc: string, currentSrc: string) => {
  const [imageSrc, _setImageSrc] = useState(initialSrc);

  useEffect(() => {
    const img = new Image();
    img.onload = () => {
      _setImageSrc(currentSrc);
    };
    img.src = currentSrc;
  }, [currentSrc]);

  return [imageSrc];
};

你能否发布一下你提出的解决方案的示例? - Marcello DeSales

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接