如何实现图片懒加载

204 阅读2分钟

什么是图片栏加载

图片懒加载的作用就是在页面打开之后,不要一次性把全部图片显示出来,而是只显示当前视口内的图片,一般在移动端用的居多(Pc端主要是前端分页后端分页)

为什么需要懒加载

对于一个页面的加载速度影响最大的因素就是图片资源,假如说没有懒加载的情况下,在一个页面图片过多如(某宝,某东等)一个页面的图片大小资源非常大多得几百兆,如果一次性全部获取资源,加载速度则会非常的慢,对于用户体验是非常不好的!

所以懒加载是在项目中是一种不可缺少的部分,对于页面没有在可视区域内就可以先不做加载处理,将映入可视区域内的先做处理,这样由于视口加载的图片资源较少,用户请求的时候速度会大大提升,用户体验就会好

懒加载实现的原理

由于浏览器会自动对页面的img标签的src属性发送请求下载图片,我们就可以利用这一特点来实现懒加载,可以将img设置一个data-src属性(自定义属性也可以是别的名字) 将资源路径先放到data-src中,在用户点击或者滚动到指定区域需要显示的时候,再将data-src的值重新赋值到img的src中,这样就实现了图片懒加载

懒加载

这里添加了许多p标签当滚动到图片的位置

当滚动到图片刚显示的时候 arr[0].isIntersecting 为true 这时候就可以将data-src属性的值赋值给src

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>

</head>
<body>
    <div>
        <p style="padding: 30px;">1</p>
        <p style="padding: 30px;">2</p>
        <p style="padding: 30px;">3</p>
        <p style="padding: 30px;">4</p>
        <p style="padding: 30px;">5</p>
        <p style="padding: 30px;">6</p>
        <p style="padding: 30px;">7</p>
        <p style="padding: 30px;">8</p>
        <p style="padding: 30px;">9</p>
        <p style="padding: 30px;">1</p>
        <p style="padding: 30px;">2</p>
        <p style="padding: 30px;">3</p>
        <p style="padding: 30px;">4</p>
        <p style="padding: 30px;">5</p>
        <p style="padding: 30px;">6</p>
        <p style="padding: 30px;">7</p>
        <p style="padding: 30px;">8</p>
        <p style="padding: 30px;">9</p> <p style="padding: 30px;">1</p>
        <p style="padding: 30px;">2</p>
        <p style="padding: 30px;">3</p>
        <p style="padding: 30px;">4</p>
        <p style="padding: 30px;">5</p>
        <p style="padding: 30px;">6</p>
        <p style="padding: 30px;">7</p>
        <p style="padding: 30px;">8</p>
        <p style="padding: 30px;">9</p> <p style="padding: 30px;">1</p>
        <p style="padding: 30px;">2</p>
        <p style="padding: 30px;">3</p>
        <p style="padding: 30px;">4</p>
        <p style="padding: 30px;">5</p>
        <p style="padding: 30px;">6</p>
        <p style="padding: 30px;">7</p>
        <p style="padding: 30px;">8</p>
        <p style="padding: 30px;">9</p> <p style="padding: 30px;">1</p>
        <p style="padding: 30px;">2</p>
        <p style="padding: 30px;">3</p>
        <p style="padding: 30px;">4</p>
        <p style="padding: 30px;">5</p>
        <p style="padding: 30px;">6</p>
        <p style="padding: 30px;">7</p>
        <p style="padding: 30px;">8</p>
        <p style="padding: 30px;">9</p> <p style="padding: 30px;">1</p>
        <p style="padding: 30px;">2</p>
        <p style="padding: 30px;">3</p>
        <p style="padding: 30px;">4</p>
        <p style="padding: 30px;">5</p>
        <p style="padding: 30px;">6</p>
        <p style="padding: 30px;">7</p>
        <p style="padding: 30px;">8</p>
        <p style="padding: 30px;">9</p>
        <img height="200" src="https://gimg2.baidu.com/image_search/src=http%3A%2F%2Ffzn.cc%2Fwp-content%2Fuploads%2F2020%2F04%2F640-8.jpg&refer=http%3A%2F%2Ffzn.cc&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1641365183&t=b5d7bdae0fe3f2c4831b52e3985abdf1" />
        <!-- <img height="200" data-src="https://gimg2.baidu.com/image_search1/src=http%3A%2F%2Ffzn.cc%2Fwp-content%2Fuploads%2F2020%2F04%2F640-8.jpg&refer=http%3A%2F%2Ffzn.cc&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1641365183&t=b5d7bdae0fe3f2c4831b52e3985abdf1" /> -->
        <p style="padding: 30px;">1</p>
        <p style="padding: 30px;">2</p>
        <p style="padding: 30px;">3</p>
        <p style="padding: 30px;">4</p>
        <p style="padding: 30px;">5</p>
        <p style="padding: 30px;">6</p>
        <p style="padding: 30px;">7</p>
        <p style="padding: 30px;">8</p>
        <p style="padding: 30px;">9</p>
    </div>

    <script>
        var img = document.querySelector("img")
        var observer = new IntersectionObserver((arr)=>{
            // 当滚动到图片刚显示的时候 arr[0].isIntersecting   为true
            console.log(arr[0].isIntersecting)
            console.log(arr[0].intersectionRatio)
            
            // if(arr[0].isIntersecting) {
            //     img.src = img.getAttribute('data-src')
            //     img.onerror = function(){
            //         img.src="https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fhbimg.b0.upaiyun.com%2Fcdb58e6860bcf06028c4b40e47aa17fd7ffa2e6f67cc-UQZRFK_fw658&refer=http%3A%2F%2Fhbimg.b0.upaiyun.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1641366439&t=07c8d22d1c35def62dcaaf04d94e1255"
            //     }
            // }
        })

        observer.observe(img)
    </script>
</body>
</html>

封装懒加载组件

传入两个参数 第一个参数为图片路径 第二个参数为类名

图片路径为必传参数

import classnames from 'classnames'
import { useEffect, useRef, useState } from 'react'
import Icon from '../Icon'
import styles from './index.module.scss'

/**
 * 拥有懒加载特性的图片组件
 * @param {String} props.src 图片地址
 * @param {String} props.className 样式类
 */
type Props = {
  src: string
  className?: string
}
const Image = ({ src, className }: Props) => {
  // 对图片元素的引用
  const imgRef = useRef<HTMLImageElement>(null)
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState(false)
  useEffect(() => {
    const ob = new IntersectionObserver((entries) => {
      console.log(entries, 'entries')

      //   console.log('working....')
      //   console.log(entries[0].isIntersecting)

      if (entries[0].isIntersecting) {
        imgRef.current!.src = src
        ob.unobserve(imgRef.current!)
      }
    })
    ob.observe(imgRef.current!)
    // console.log(ob, 999)
  }, [])

  return (
    <div className={classnames(styles.root, className)}>
      {/* 正在加载时显示的内容 */}
      {
        loading && <div className="image-icon">
          <Icon type="iconphoto" />
        </div>
      }

      {/* 加载出错时显示的内容 */}
      {
        error && <div className="image-icon">
          <Icon type="iconphoto-fail" />
        </div>
      }

      {/* 加载成功时显示的内容 */}
      {<img alt="" data-src={src} ref={imgRef} onLoad={() => { setLoading(false) }} onError={() => { setError(true) }} />}
    </div>
  )
}

export default Image