阅读 161

性能优化之图片懒加载

为什么需要图片懒加载

如果一次性加载过多的图片会堵塞js解析,会影响页面启动,同时也会占用过多的网络资源利用图片懒加载,图片出现在可视区域的时候才去加载。

实现原理

方式一:实现的思路

  1. 设置图片的的路径为data-src ,获取到所有的图片节点
  2. 判断图片是否出现在可视区域,出现在可视区域就替换src为data-src
  3. 监听页面scroll事件,加上防抖throll函数,具体代码如下:
<div class="container">
  <div class="img-area">
    <img class="img" alt="loading" data-src="./img/img1.jpeg" />
  </div>
  <div class="img-area">
    <img class="img" alt="loading" data-src="./img/img2.jpeg" />
  </div>
  <div class="img-area">
    <img class="img" alt="loading" data-src="./img/img3.jpeg" />
  </div>
</div>
复制代码
  const imgNode = document.querySelectorAll('.img')
  const imgList = Array.from(imgNode)
  const clientHeight = document.documentElement.clientHeight
  //是否出现在可视区域
  function isInVisibale(scrollTop) {
    imgList.forEach((el) => {
      if (el.offsetTop - scrollTop <= clientHight) {
        loadImg(el)
      }
    })
  }
  //加载图片,替换src
  function loadImg(el) {
    if (!el.src) {
      let src = el.dataset.src
      el.src = src
    }
  }
  //防抖
  function throll(cb, time = 200) {
    let lastime = null
    return function (...arg) {
      let now = Date.now()
      if (now - lastime >= time || lastime === null) {
        cb.apply(this, arg)
        lastime = now
      }
    }
  }
  //监听滚动
  document.addEventListener('scroll', throll(isInVisibale))
复制代码

这里的难点是判断图片是否出现在可视区域,

1.主要是计算的方式为:

element.offsetTop - document.documentElement.scrollTop <= document.documentElement.clientHeight
复制代码

2.另外一种计算方式,利用getBoundingClientRect(),附上mdn的地址: developer.mozilla.org/zh-CN/docs/…

let bound = element.getBoundingClientRect()
bound.top <=docment.documentElement.clientHeight
复制代码

那么上面的是否出现在可视化区域的函数可以修改为

  function isInVisibale() {
    imgList.forEach((el) => {
      let bound = el.getBoundingClientRect()
      if (bound.top <= clientHeight) {
        loadImg(el)
      }
    })
  }

复制代码

方式二:利用IntersectionObserver() api可以自动观察元素是否在视口内,方式一的方式会不断的监听页面的滚动,其实对页面的性能有一定的影响,利用insterSetionObserver()来监听元素是否出现在可视区域会更好,不过兼容性不佳,Chrome 51+才支持。

const imgNode = document.querySelectorAll('.img')
const imgList = Array.from(imgNode)
//开启一个监听器
const Observe = new IntersectionObserver(function (observes) {
  observes.forEach((observe) => {
    let el = observe.target
    //根据intersectionRatio来判断
    if (observe.intersectionRatio > 0 && observe.intersectionRatio <= 1) {
      loadImg(el)
    }
    //图片加载和失败时候取消观察
    el.onload = el.onerror = () => Observe.unobserve(el)
  })
})
//对所有的图片进行观察
imgList.forEach((el) => {
  Observe.observe(el)
})
//替换图片
function loadImg(el) {
  if (!el.src) {
    let src = el.dataset.src
    el.src = src
  }
}
复制代码

vue中利用v-lazyLoad指令来实现懒加载

在vue框架中,我们会经常使用到v-html、v-text、v-if、v-show、v-for等指令来进行敏捷开发,那么我们也可以自定义我们自己的指令来方便我们日常的开发,具体的源码如下

思路分析:

  1. 自定义vue指令,在指令的inserted钩子函数中进行浏览器能力检测,判断是否支持IntersectionObserver函数,不支持就使用监听scroll事件
  2. 注册到局部组件directive:{},或者全局组件vue.directive()
  3. 页面使用直接v-lazy='xxx.jpg'
const lazyLoad = {
  // 被绑定的元素插入到父节点的时候
  inserted (el, binding) {
    let src = binding.value
    console.log(11, src)
    //判断是否支持IntersectionObserver()
    if (IntersectionObserver) {
      observe(el, src)
    } else {
      observeScroll(el, src)
    }
  }
}

//监听api
function observe (el, src) {
  const Observer = new IntersectionObserver(observes => {
    console.log(observes[0].isIntersecting)
    if (observes[0].isIntersecting && !el.src) {
      el.src = src
      el.removeAttribute('data-src')
      Observer.unobserve(el)
    }

  })
  Observer.observe(el)
}
//监听滚动api
function observeScroll (el, src) {
  document.addEventListener('scroll', throll(isInVisibale(el, src)))
}
const clientHeight = document.documentElement.clientHeight
//高阶函数
function isInVisibale (el, src) {
  return function () {
    let bound = el.getBoundingClientRect()
    if (bound.top <= clientHeight) {
      if (!el.src) {
        el.src = src
      }
    }
  }
}
//防抖:利用高阶函数和闭包
function throll (cb, time = 200) {
  let lastime = null
  return function (...arg) {
    let now = Date.now()
    if (now - lastime >= time || lastime === null) {
      cb.apply(this, arg)
      lastime = now
    }
  }
}
export default lazyLoad
复制代码

总结

  1. 为了提高网页性能,增加用户体验,图片的懒加载是必要的
  2. 可以监听scroll事件和利用interSectionObserve()来判断图片是否出现在可视区域
  3. vue框架可以编写一个指令v-loayLoad来实现图片懒加载
文章分类
前端
文章标签