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

时间:2017-03-05 23:59:20

标签: javascript reactjs

如何在React.js中预加载图像? 我有下拉选择组件,其功能类似菜单,但我必须预先加载项目的图像图标,因为有时它们在首次打开时不可见。

我试过了:

https://github.com/sambernard/react-preload

https://github.com/wizardzloy/react-img-preload

第一个有很好的API易于理解和使用,但是是spaming控制台,警告图像没有加载,即使它们是。 第二个有奇怪的API,但我尝试了例子,它没有预加载任何东西。

所以我可能需要自己实现一些东西,但不知道从哪里开始。或者另一种可能性是用webpack加载它们。

10 个答案:

答案 0 :(得分:18)

假设您有pictures: string[]; - 在您的组件的道具中定义的图片网址数组。 您应该在组件中定义componentDidMount()方法,然后为要预加载的每个图片创建新的Image对象:

componentDidMount() {
    this.props.pictures.forEach((picture) => {
        const img = new Image();
        img.src = picture.fileName;
    });
}

它强制浏览器加载所有图像。 此示例使用TypeScript编写。

答案 1 :(得分:3)

如果某些图像在缓存后仍在重新加载,请尝试将它们存储在window对象中:

componentDidMount() {
    const imagesPreload = [image1, image2, image3];
    imagesPreload.forEach((image) => {
        const newImage = new Image();
        newImage.src = image;
        window[image] = newImage;
    });
}

(您无需从window对象调用图像)

答案 2 :(得分:2)

如果只提供一些小的“图标” - (为什么不使用字体?) - 如果服务器提供gzip文件,你可以使用base64。

否则,如果select不能立即显示,您还可以将img标签(带display: none)添加到之前的HTML中。另一种方法是将Image对象附加到DOM并在显示组件之前等待.onload(这种方法由您提到的库使用)。

据我所知,webpack或者反应不能为你做任何特别的事。这是客户端的一些东西,这些只是实现自己的预加载API的工具(甚至使用JS / TS,React,Angular中的现有API ......)

答案 3 :(得分:2)

重要的是将图像变量保存为PERSISTANT。

在持久性上下文中,在全局变量中或在显示图像之前不会卸载的组件中:

window.preloadedPictures = []

在我的组件中:

var preloadedData = pictures.map((picture) => {
    const img = new Image();
    img.src = picture.fileName;
    return img
});

myContextOrGlobalVar.preloadedPictures = preloadedData ///IMPORTANT 

答案 4 :(得分:0)

这有效:

import im1 from 'img/im1.png'
import im2 from 'img/im2.png'
import im3 from 'img/im3.png'

componentDidMount() {
    imageList = [im1, im2, im3]
    imageList.forEach((image) => {
        new Image().src = image
    });
}

答案 5 :(得分:0)

我的朋友,您可以选择链接rel预加载/ prefetch和webpack延迟加载块。

更多信息:

https://github.com/GoogleChromeLabs/preload-webpack-plugin

答案 6 :(得分:0)

假设您有一个images数组,其中包含每个图像的数据,则可以一次性完成

componentDidMount() {
  this.props.images.forEach(image => (new Image().src = image.src));
}

答案 7 :(得分:0)

反应钩子方式

useEffect(() => {
  //preloading image
  faceArray.forEach((face) => {
    const img = new Image();
    img.src = image;
  });
}, []);

答案 8 :(得分:0)

这里有一种方法可以将此功能放入自定义挂钩中,您可以在任何功能组件中使用该挂钩。

一些答案​​遗漏的一个重要部分是在诸如 window 对象之类的东西中存储对预加载图像的引用。如果您不这样做,您可能会发现浏览器会在实际需要图像时重新请求它们,尽管它们已经被预加载。

第一次使用此钩子时,会在 window 中添加一个名为 usePreloadImagesData 的键,并将其初始化为 {}

每次在组件中使用钩子时,都会向 window.usePreloadImagesData 添加一个新键。密钥的名称是随机生成的。它被初始化为 []

钩子采用图像源字符串数组。对于每个图像源,都会创建一个 new Image() 并将其添加到 window.usePreloadImagesData[randomStr] 数组中。

当组件被卸载,或者传递给自定义钩子的数组发生变化时,window.usePreloadImagesData[randomStr]会被删除以避免内存泄漏。

这个例子是用 Typescript 编写的。

import { useEffect } from 'react';

declare global {
  interface Window {
    usePreloadImagesData?: Record<string, unknown[]>;
  }
}

export const usePreloadImages = (imageSrcs: string[]): void => {
  useEffect(() => {
    const randomStr = Math.random().toString(32).slice(2) + Date.now();
    window.usePreloadImagesData = window.usePreloadImagesData ?? {};
    window.usePreloadImagesData[randomStr] = [];
    for (const src of imageSrcs) {
      // preload the image
      const img = new Image();
      img.src = src;
      // keep a reference to the image
      window.usePreloadImagesData[randomStr].push(img); 
    }
    return () => {
      delete window.usePreloadImagesData?.[randomStr];
    };
  }, [ imageSrcs ]);
};

使用此钩子时,确保传递给钩子的图像源字符串数组是常量;否则会导致不必要的重新渲染。即,要么在组件外创建它,要么使用 React.useMemo 之类的东西。

例如

import React from 'react';
import { usePreloadImages } from '../hooks/usePreloadImages';

import img1 from './image-1.jpg';
import img2 from './image-2.jpg';

const preload = [ img1, img2 ]; // create constant array here, outside the component

export const MyComponent: React.FC = () => {

  usePreloadImages(preload); // correct
  // usePreloadImages([ img1, img2 ]); // incorrect

  return (
    // ...
  );
};

答案 9 :(得分:-3)

我认为最好的方法是对图像使用数据URI

componentWillMount() {
   this.setState({image: "data:image/gif;base64,R0lGODlhEAAQAMQA..."})
}

render() {
   <img src={this.state.image} />
}