图片懒加载与预加载的实现

图片懒加载与预加载是前端优化中比较常见的方法,也是前端面试中会被问到的问题。懒加载就是当你打开一个页面时,没有出现在屏幕中的图片先不加载,等滑到该图片位置时再加载,而预加载就是一次性把图片加载完。

如果不做懒加载和预加载,浏览器的回流重绘很快,而图片的加载是需要发送网络请求的,当图片一百甚至一千张时,一次性发很多请求就会导致网络的堵塞,影响用户体验,接下来就让我们来实现一下懒加载以及预加载的效果。

懒加载

懒加载原理就是监听屏幕滚动事件,然后判断图片是否出现在屏幕内,等图片要出现在屏幕内再加载。可以使用js中的getBoundingClientRect()方法,获取元素的集合属性,打印如下。

js 复制代码
<body>
  <img src="" data-src="https://t7.baidu.com/it/u=1732966997,2981886582&fm=193&f=GIF" alt="">
  <script>
    let img = document.querySelector('img')
    let rect = img.getBoundingClientRect()  // 获取元素几何属性
    console.log(rect);
  </script>
</body>

left:左边框到窗口左边的距离,right:右边框到窗口左边的距离,bottom:下边框到窗口上边的距离,top:上边框到窗口上边的距离。

然后判断图片是否在容器内,图片的top小于窗口的高度,图片的bottom大于0。

为了让图片先不展示,就不能把图片的url放在src属性上,于是我们可以在img标签中自己定义一个属性data-src,将图片地址放进去,然后当图片要展示时,令图片的src等于data-src属性,展示过后的图片再移除掉data-src属性。

js 复制代码
<img src="" data-src="https://t7.baidu.com/it/u=17329669972981886582&fm=193&f=GIF" alt="">
js 复制代码
<script>
    let height = window.innerHeight

    function lazyLoad() {
      const imgs = document.querySelectorAll('[data-src]')
      // console.log(imgs);
      for (let i = 0; i < imgs.length; i++) {
        let rect = imgs[i].getBoundingClientRect() // 获取元素的集合属性
        if (rect.bottom > 0 && rect.top < height) {
          imgs[i].src = imgs[i].getAttribute('data-src')

          imgs[i].onload = function () {   // 图片被浏览器加载完毕
            imgs[i].src = imgs[i].getAttribute('src')
          }

          imgs[i].removeAttribute('data-src')
        }
      }
    }

    lazyLoad()

    window.addEventListener('scroll', lazyLoad)
  </script>

当然里面的imgs[i].src = imgs[i].getAttribute('data-src')可以换成如下:

js 复制代码
// imgs[i].src = imgs[i].getAttribute('data-src')
// imgs[i].onload = function () {     // 图片被浏览器加载完毕
//     imgs[i].src = imgs[i].getAttribute('src')
//  }
 
// 换成

let newImg = new Image()    
newImg.src = imgs[i].getAttribute('data-src')   // 读取 url
newImg.onload = function () { // 图片被浏览器加载完毕
    imgs[i].src = newImg.getAttribute('src')     
}

目的是为了将图片的加载过程异步化,以此来减轻主线程的压力。这是通过创建一个新的Image对象,然后设置其src属性来预加载图片,一旦图片加载完成(onload事件触发),再将图片的src属性设置到原来的img元素上。

效果如下:

预加载

用户一打开页面,全部图片先加载,然后后用户滑动就会非常地丝滑,但是就会导致同一时间加载图片过多,对服务器压力过大。

原理:在加载页面的同时,创建第二个线程去加载图片,当图片加载完毕后,将图片资源交给第一个线程去展示,从而实现图片的预加载。

代码如下;

js 复制代码
  <div id="pic"></div>

  <script>
    let pic = document.getElementById('pic')

    let arr = [xxxxx]   // 图片资源
    // 创建一个新的线程
    const worker = new Worker('worker.js')
    // 将数据发送给子线程
    worker.postMessage(arr)
  </script>

worker函数是js自带的一个函数,作用就是创建一个新线程,但是这个新线程不能操作dom结构。这两个线程可以通信,通过postMessage进行通讯。

然后worker.js里面去加载图片,直接通过http请求去加载资源,回来的是blob类型的文件。

js 复制代码
// worker.js   self就是worker

self.onmessage = function (e) {
  // console.log(e.data);   // 传过来的图片资源
  // 将数组中的地址资源加载出来
  let arr = e.data;
  for (let i = 0; i < arr.length; i++) {
    let xhr = new XMLHttpRequest();
    xhr.open("get", arr[i], true);
    xhr.responseType = 'blob'  // 文件类型
    xhr.send();
    xhr.onload = function () {
      if (xhr.readyState === 4 && xhr.status === 200) {
        // console.log(xhr.response);
        self.postMessage(xhr.response);   //将图片发送给主线程
      }
    }
  }
}

然后主线程去接收资源再进行渲染;

js 复制代码
<body>
  <div id="pic"></div>

  <script>
    let pic = document.getElementById('pic')

    let arr = [xxxxx]   // 图片资源
    // 创建一个新的线程
    const worker = new Worker('worker.js')
    // 将数据发送给子线程
    worker.postMessage(arr)
    // 接收子线程发送的数据
    worker.onmessage = function (e) {
      console.log(e.data);
      const img = new Image()
      // console.log(window.URL.createObjectURL(e.data));
      img.src = window.URL.createObjectURL(e.data)
      pic.appendChild(img)
    }
  </script>
</body>

window.URL.createObjectURL(e.data)是window自带的方法,作用就是将blob资源转换成url。

小结

  • 懒加载原理:当页面加载完毕后,判断在可视区域内的图片先加载,当用户滚动时,判断图片是否进入可视区域,如果进入可视区,则将图片的src替换为真实图片路径,从而实现懒加载的原理。
  • 预加载原理:在加载页面的同时,开一个新线程去加载图片,当图片加载完毕后,将图片资源交给主线程去展示,从而实现图片的预加载。
相关推荐
秋月华星30 分钟前
【flutter】TextField输入框工具栏文本为英文解决(不用安装插件版本
前端·javascript·flutter
—Qeyser1 小时前
用Deepseek写一个 HTML 和 JavaScript 实现一个简单的飞机游戏
javascript·游戏·html
千里码aicood1 小时前
[含文档+PPT+源码等]精品基于Python实现的校园小助手小程序的设计与实现
开发语言·前端·python
青红光硫化黑2 小时前
React基础之React.memo
前端·javascript·react.js
大麦大麦2 小时前
深入剖析 Sass:从基础到进阶的 CSS 预处理器应用指南
开发语言·前端·css·面试·rust·uni-app·sass
GDAL3 小时前
better-sqlite3之exec方法
javascript·sqlite
匹马夕阳3 小时前
基于Canvas和和原生JS实现俄罗斯方块小游戏
javascript·canva可画
m0_616188493 小时前
Vue3 中 Computed 用法
前端·javascript·vue.js
weixin_460783873 小时前
Flutter解决TabBar顶部页面切换导致页面重载问题
android·javascript·flutter