如何在 React 中缓存图像?

IT技术 javascript reactjs caching dom
2021-05-05 17:51:55

假设我有一个 url 列表,如下所示:

[ '/images/1', '/images/2', ... ]

我想预取n这些,以便图像之间的转换更快。我现在正在做的componentWillMount是以下内容:

componentWillMount() {
      const { props } = this;
      const { prefetchLimit = 1, document = dummyDocument, imgNodes } = props;
      const { images } = document;
      const toPrefecth = take(prefetchLimit, images);
      const merged = zip(toPrefecth, imgNodes);

      merged.forEach(([url, node]) => {
        node.src = url;
      });
    }

imgNodes像这样被定义:

imgNodes: times(_ => new window.Image(), props.prefetchLimit),

times, zip,take来自ramda

现在,当我在react中使用这些网址时:

<img src={url} />

无论 url 在哪里使用,它都会根据EtagExpire标签访问浏览器缓存我还计划在n我们n - 1进入视图内部使用它来预取下一个图像imgNodes,以相同的方式重用

我的问题是:

  • 这是否是一个有效的想法,提供 100 多个将使用此想法的组件,但一次只能看到 1 个?

  • 这样做我会遇到内存问题吗?我假设imgNodes在卸载组件时将被垃圾收集。

我们正在使用,redux所以我可以将这些图像保存在商店中,但这似乎是我在处理缓存而不是利用浏览器的自然缓存。

这是一个多么糟糕的主意?

2个回答

您不需要在所有组件中都这样做。下载图像后,它就会被浏览器缓存,并且可以在所有组件中访问,因此您只能在高级组件中的某处执行此操作一次。

我不知道您试图通过缓存图像来创建什么样的用户体验,但是,您的代码仅启动下载图像,但不知道图像是否正在下载、是否已成功下载或什至失败。因此,例如,您想显示一个按钮来更改图像或仅在图像下载后(以使其流畅)向组件添加一个类,您当前的代码可能会让您失望。

你可能想用 Promises 来解决这个问题。

// create an utility function somewhere
const checkImage = path =>
    new Promise(resolve => {
        const img = new Image()
        img.onload = () => resolve(path)
        img.onerror = () => reject()

        img.src = path
    })

...

// then in your component
class YourComponent extends Component {

    this.state = { imagesLoaded: false }

    componentDidMount = () =>
        Promise.all(
            R.take(limit, imgUrls).map(checkImage)
        ).then(() => this.setState(() => ({ imagesLoaded: true })),
               () => console.error('could not load images'))

    render = () =>
        this.state.imagesLoaded
            ? <BeautifulComponent />
            : <Skeleton />
}

关于内存消耗——我认为不会发生任何不好的事情。浏览器通常会限制并行 xhr 请求的数量,因此您将无法创建一个巨大的堆使用高峰来使任何内容崩溃,因为未使用的图像将被垃圾收集(但仍保留在浏览器缓存中)。

Redux 商店是一个存储应用程序状态的地方,而不是应用程序资产,但无论如何你将无法在那里存储任何实际图像。

这很简单并且工作正常:

//arraySrcs=["myImage1.png","myImage2.jpg", "myImage3.jpg", etc]

{arraySrcs.map((e) => (
    <img src={e} style={{ display: "none" }} />
  ))}