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

Ľub*_*mír 13 javascript reactjs

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

我试过了:

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

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

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

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

小智 25

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

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

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

  • 一线式:`new Image()。src = picture.fileName;`而不是两行 (4认同)
  • 如何在标记中使用 new Image() 创建的图像? (4认同)
  • 如果您使用JS,请将"let"替换为"const".在这种情况下不要使用"var" (3认同)
  • @ kojow7 TL; DR如果知道要声明的变量不会更改并免费获得特权,请使用“ const” :)一些优点。使用const可以使编译器进行一些优化,因为类型和值不会更改。因为您知道值不会改变,所以它也提高了代码的可读性。如果您在代码中的其他位置更改值,则会产生TypeError,这可能有助于避免在更改值时不应该更改的错误 (3认同)
  • @mistarsv 除非你做了一些不安全的事情,否则你不能做出反应。它确实将图像加载到缓存中,因此您可以像平常一样使用具有相同源的 img 标签,并且它将立即加载。 (2认同)

Jo *_* E. 17

TypeScript这是在组件内预加载图像的React Hooks(带有 )方式的半完整示例。您也可以将其作为钩子。

在这些示例中,我们在这里使用useEffectand useState,但实际的预加载工作是在preloadImage()我们拥有的函数内部。另请注意,导入图像时您将获得一个字符串。

直接添加到组件内部

import React, { useEffect, useState } from 'react'

import Image1 from 'images/image1.png'
import Image2 from 'images/image2.jpg'
import Image3 from 'images/image3.svg'

const preloadSrcList: string[] = [
  Image1,
  Image2,
  Image3,
]

function preloadImage (src: string) {
  return new Promise((resolve, reject) => {
    const img = new Image()
    img.onload = function() {
      resolve(img)
    }
    img.onerror = img.onabort = function() {
      reject(src)
    }
    img.src = src
  })
}

export default function Component() {
  const [assetsLoaded, setAssetsLoaded] = useState<boolean>(false)

  useEffect(() => {
    let isCancelled = false

    async function effect() {
      if (isCancelled) {
        return
      }

      const imagesPromiseList: Promise<any>[] = []
      for (const i of preloadSrcList) {
        imagesPromiseList.push(preloadImage(i))
      }
  
      await Promise.all(imagesPromiseList)

      if (isCancelled) {
        return
      }

      setAssetsLoaded(true)
    }

    effect()

    return () => {
      isCancelled = true
    }
  }, [])

  if (!assetsLoaded) {
    return <p>Preloading Assets</p>
  }

  return <p>Assets Finished Preloading</p>
}
Run Code Online (Sandbox Code Playgroud)

更好地作为钩子:useImagePreloader()

import { useEffect, useState } from 'react'

function preloadImage (src: string) {
  return new Promise((resolve, reject) => {
    const img = new Image()
    img.onload = function() {
      resolve(img)
    }
    img.onerror = img.onabort = function() {
      reject(src)
    }
    img.src = src
  })
}

export default function useImagePreloader(imageList: string[]) {
  const [imagesPreloaded, setImagesPreloaded] = useState<boolean>(false)

  useEffect(() => {
    let isCancelled = false

    async function effect() {
      console.log('PRELOAD')

      if (isCancelled) {
        return
      }

      const imagesPromiseList: Promise<any>[] = []
      for (const i of imageList) {
        imagesPromiseList.push(preloadImage(i))
      }
  
      await Promise.all(imagesPromiseList)

      if (isCancelled) {
        return
      }

      setImagesPreloaded(true)
    }

    effect()

    return () => {
      isCancelled = true
    }
  }, [imageList])

  return { imagesPreloaded }
}
Run Code Online (Sandbox Code Playgroud)

在组件内使用 useImagePreloader()

import React, { useEffect, useState } from 'react'
import useImagePreloader from 'hooks/useImagePreloader'

import Image1 from 'images/image1.png'
import Image2 from 'images/image2.jpg'
import Image3 from 'images/image3.svg'

const preloadSrcList: string[] = [
  Image1,
  Image2,
  Image3,
]

export default function Component() {
  const { imagesPreloaded } = useImagePreloader(preloadSrcList)

  if (!imagesPreloaded) {
    return <p>Preloading Assets</p>
  }

  return <p>Assets Finished Preloading</p>
}
Run Code Online (Sandbox Code Playgroud)

  • 这是一个很好的用例示例,其中 useEffect 是生命周期方法的一个巨大倒退。过去就是这么简单。组件DidMount,完成。现在却变得冗长、混乱。这不是 Jo E 的错,而是 React 的错。 (2认同)

小智 9

反应钩子方式

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


小智 6

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

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

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

  • 谢谢你!!这救了我,但为什么需要它呢? (2认同)

Dam*_*ito 5

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

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

window.preloadedPictures = []
Run Code Online (Sandbox Code Playgroud)

在我的组件中:

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

myContextOrGlobalVar.preloadedPictures = preloadedData ///IMPORTANT 
Run Code Online (Sandbox Code Playgroud)

  • :) 只是有点混乱!重要=非常重要或不重要 (3认同)

Tim*_*Tim 3

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

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

据我想象,webpack 或 React 不能为你做任何特别的事情。这是客户端的事情,这些只是实现你自己的预加载 API 的工具(甚至使用 JS/TS、React、Angular 等中现有的 API)

  • 使用 svgs 而不是字体有[**充足**的原因](https://css-tricks.com/icon-fonts-vs-svg/)。 (4认同)