Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

懒加载 #60

Open
bibi7 opened this issue Dec 24, 2019 · 0 comments
Open

懒加载 #60

bibi7 opened this issue Dec 24, 2019 · 0 comments

Comments

@bibi7
Copy link
Owner

bibi7 commented Dec 24, 2019

年底相关活动页需要一些比较密集的图片显示,且好几条业务线都有类似的需求,用高阶组件的形式来搞吧,正好前几天看上了个新api,写一下相关总结

正常来讲,懒加载往往是通过clientHeightscrollTopoffsetTop来计算当前元素是否出现在视口中,也就是要监听scroll方法。如下:

startLazyLoad = () => {
  //比如所有的img元素的class都是lazy
  let count = 0;
  const lazy = document.querySelectorAll('.lazy');
  const clientHeight = document.documentElement.clientHeight;
  const scrollTop = document.documentElement.scrollTop;
  for (let i = 0; i < lazy.length; i++) {
    if (lazy[i].offsetTop < clientHeight + scrollTop) {
      lazy[i].src = lazy[i].getAttribute('data-src');
      count++
    }
  }
  if (count === lazy.length) {
    window.removeEventListener('scroll', this.startLazyLoad)
  }
}

window.addEventListener('scroll', this.startLazyLoad); //有条件的加个节流

ps:判断是否到底的话可以这么用element.scrollHeight - element.scrollTop === element.clientHeight

IntersectionObserver,MDN

调用IntersectionObserver时,需要给它传一个回调函数 / 对象。当监听的dom元素进入可视区域或者从可视区域离开时,回调函数就会被调用。

const io = new IntersectionObserver((entries) => {
  console.log(entries);
});

io.observe(dom);

关于调用次数

在new的时候会调用一次,这点可以解决初始进入页面时候的懒加载问题
在监听元素进入/移出的时候会调用,callback中会传入一串数组,有这么几个属性:

  1. target: 被观察的目标元素,是一个 DOM 节点对象

  2. isIntersecting: 是否进入可视区域

  3. intersectionRatio: 相交区域和目标元素的比例值,进入可视区域,值大于0,否则等于0(这个基本上用处没有第二点多

基于以上几点,我们可以实现这么一个懒加载,包含了自动监听滚动时间,判断视口,且节流。

startObserveImg = () => {
  const lazy = document.querySelectorAll('.lazy');

  const observer = new IntersectionObserver(observers => {
    //observers表示此刻移出或者进入视口的所有img列表
    for (let i = 0; i < lazy.length; i++) {
      const ob = observers[i];
      if (ob.isIntersecting) {
        const element = ob.target
        setTimeout(() => {
          element.src = element.getAttribute('data-src')
        }, 1000)
        observer.unobserve(element)
      }
    }
  })

  lazy.forEach(item => observer.observe(item))
}

兼容层面来说不是很理想,但是大部分可用,在safari或者hybrid下的WKwebview好像还不太行,安卓还没试过,最好需要判断一下当前环境。

完全版,一些react的语法就不添加了:

import ImgLazyLoadHOC from './B.js'
//A.js
render() {
  const imgList = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24]

  function imgDom(props) {
    const { imgLazyContainer } = props;

    return (
      <div className="imgsDiv">
        {imgLazyContainer}
      </div>
    )
  }

  const ImgDomComponent = ImgLazyLoadHOC(imgDom, {
    imgList: imgList
  })
 
  return(
    <ImgDomComponent />
  )
}
//B.js
import React, { Component } from 'react'

const ImgLazyLoadHOC = (WarpComponent, config = {}) => {
  const {
    imgList
  } = config;
  const imgLazyMapList = imgList && imgList.map(() => {
    return <img className="lazy" src="default.png" data-src="target.png"/>
  })

  return class ImgLazyLoadFuncHOC extends Component {
    componentDidMount() {
      this.checkObserverAbility()
        ? this.startObserveImg()
        : this.startLazyLoadImg()
    }

    //use IntersectionObserver or common way
    checkObserverAbility = () => {
      return !!window.IntersectionObserver
    }

    startLazyLoadImg = () => {
      this.startLazyLoad()
      window.addEventListener('scroll', this.startLazyLoad);
    }

    startLazyLoad = () => {
      let count = 0;
      const lazy = document.querySelectorAll('.lazy');
      const clientHeight = document.documentElement.clientHeight;
      const scrollTop = document.documentElement.scrollTop;
      for (let i = 0; i < lazy.length; i++) {
        if (lazy[i].offsetTop < clientHeight + scrollTop) {
          lazy[i].src = lazy[i].getAttribute('data-src');
          count++
        }
      }
      if (count === lazy.length) {
        window.removeEventListener('scroll', this.startLazyLoad)
      }
    }

    startObserveImg = () => {
      const lazy = document.querySelectorAll('.lazy');

      const observer = new IntersectionObserver(observers => {
        for (let i = 0; i < lazy.length; i++) {
          const ob = observers[i];
          if (ob.isIntersecting) {
            const element = ob.target
            setTimeout(() => {
              element.src = element.getAttribute('data-src')
            }, 1000)
            observer.unobserve(element)
          }
        }
      })

      lazy.forEach(item => observer.observe(item))
    }

    render() {
      return <WarpComponent imgLazyContainer={imgLazyMapList}/>
    }
  }
}


export default ImgLazyLoadHOC

参考

感谢深红大大:IntersectionObserve初试

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant