使用原生js/jquery,实现高效率的图片占位加载,懒加载等功能。



在自己写这个功能之前,先试了下jquery lazyload,也看了一眼vue lazyload的文档。试了以后发现有2个问题

  • 默认是会渲染全页面的img标签,当我的页面翻页次数多了以后,会出现明显卡顿,cpu占用高的情况。

  • 监听onerror失效,因为jquery lazyload第一次加载完成占位图片后,就不再监听onerror了。vue Lazyload图片如果加载失败,可以触发error,但是只能传入error图片的参数,替换图片显示内容,不能执行代码。

于是我参考了一下这篇文章

js原生实现高性能懒加载(分步解析) - CSDN博客

https://blog.csdn.net/weixin_40821790/article/details/78745796

 

从中理解到懒加载的基本款就包括以下几个步骤

  • 初次加载使用占位符

  • 监听当前浏览页面的滚动条的高度,来判断真实图片加载的时机

  • 实现懒加载,替换src参数(必须发生在img的load步骤结束以后,否则占位图还未渲染,img还没有高度,占位会失效)

 



首先我直接用jquery写了一个最简单的图片占位功能。


1、首先默认只去加载 3x2像素 灰色的占位图片

  1. <img id="img666" src="1.jpg" alt="Park" onload="lazyLoad('666')" onerror="removeImg('666')">


2、当触发onload时再替换src

  1. function lazyLoad(id){
  2. var url = 'http://xxxx/'+id+'.jpg';
  3. $('#img'+id).attr('src',url);
  4. }


前面这样写还有一个小坑,正确的是,因为改变src会引起二次加载,二次加载成功还会触发onload,就会无限循环,所以加上判断可以在第二次循环时中断操作。

  1. function lazyLoad(id){
  2.     // 因为改变src会引起二次加载,二次加载成功还会触发onload,就会无限循环,所以加上判断可以在第二次循环时中断操作。
  3.     if($('#img'+id).attr('src') == '1.jpg'){
  4.      var url = 'http://xxxx/'+id+'.jpg';
  5.      $('#img'+id).attr('src',url);
  6.     }
  7. }


3、当触发onerror直接移除整个img(css里实现了下一张图片自动往前移填充空缺)

  1. function removeImg(id){
  2.     $('#img'+id).hide();
  3. }


当然也可以触发替换为404图片

  1. function errorImg(id){
  2.     $('#img'+id).attr('src','404.jpg');
  3. }


那么同理,当404.jpg无法加载成功时,例如网络问题、图片url问题,也会触发onerror,同样会造成无限循环,所以保险的写法是

  1. function errorImg(id){
  2. $('#img'+id).attr('src','404.jpg');
  3. $('#img'+id).onerror = null;
  4. }


因为我一开始加载图片就是滚动到页面底部,才加载一批新的,所以不用再来一遍监听高度,显示图片了,但我还是大概看了一下CSDN大神hawkey7的完整代码。大致意思是监听滚动条的位置和图片的位置,来判断是否触发懒加载。而且必须发生在window.onload之后,否则第一批占位img还没加载完就执行懒加载,占位的效果就会失效,不美观。

  1. window.onload = function(){
  2.     var scrollTop = window.scrollY;
  3.     var imgs = Array.from(document.querySelectorAll('img'));
  4.     lazyLoad();
  5.     // 采用了节流函数
  6.     window.addEventListener('scroll',throttle(lazyLoad,500,1000));
  7.  
  8.     function throttle(fun, delay, time) {
  9.         var timeout,
  10.             startTime = new Date();
  11.         return function() {
  12.  
  13.             var context = this,
  14.                 args = arguments,
  15.                 curTime = new Date();
  16.             clearTimeout(timeout);
  17.             // 如果达到了规定的触发时间间隔,触发 handler
  18.             console.log(curTime - startTime)
  19.             if (curTime - startTime >= time) {
  20.                 fun();
  21.                 startTime = curTime;
  22.                 // 没达到触发间隔,重新设定定时器
  23.             } else {
  24.                 timeout = setTimeout(fun, delay);
  25.             }
  26.         };
  27.     };
  28.     // 实际想绑定在 scroll 事件上的 handler
  29.     // 需要访问到imgs ,  scroll 
  30.     function lazyLoad(){
  31.         scrollTop = window.scrollY;
  32.         imgs.forEach((item,index)=>{
  33.             if( scrollTop===0 && item.dataset.src !== '' && item.offsetTop < window.innerHeight + scrollTop ){
  34.                 // alert()
  35.                 item.setAttribute('src',item.dataset.src)
  36.                 item.setAttribute('data-src','')
  37.             }else if( item.dataset.src !== '' && item.offsetTop < window.innerHeight + scrollTop && item.offsetTop > scrollTop ){
  38.                 item.setAttribute('src',item.dataset.src)
  39.                 item.setAttribute('data-src','')
  40.             }
  41.         })
  42.     }
  43. }