前端懒加载和预加载

本文介绍了前端加载策略中的懒加载和预加载,旨在提高用户体验。懒加载通过延迟加载非首屏图片来优化网页性能,预加载则是提前加载所需资源以减少等待时间。文章详细讲解了两种加载方式的实现代码、细节和效果,包括如何判断元素是否进入可视区域,以及CSS和JavaScript的实现方法。

懒加载和预加载的目的都是为了提高用户的体验,二者行为是相反的,一个是延迟加载,另一个是提前加载。懒加载对缓解服务器压力有一定作用,预加载则会增长服务器前端压力缓存。

懒加载 lazyload

懒加载:又叫延迟加载、按需加载,当我们打开一个网页,优先展示的首屏图片就先加载,而其他的图片,等到需要去展示的时候再去请求图片资源。

目的:更好的加载页面的首屏内容,对于含有不少图片的比较长的网页来讲,能够加载的更快,避免了首次打开时,一次性加载过多图片资源,是对网页性能的一种优化方式。

原理:浏览器解析到img标签的src有值,才会去发起请求,那么就可以让图片需要展示时,才对其src赋值真正的图片地址。

实现代码

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset=UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>图片懒加载</title>
    <style>
      * {
        margin: 0;
        padding: 0;
      }
      img {
        margin: 50px;
        width: 400px;
        height: 200px;
      }
    </style>
  </head>
  <body>
    <div class="img-container">
      <img
        src="loading.gif"
        alt=""
        data-src="1.jpg"
        class="lazyload"
      />
    </div>
    <div class="img-container">
      <img
        src="loading.gif"
        alt=""
        data-src="2.jpg"
        class="lazyload"
      />
    </div>
    <div class="img-container">
      <img
        src="loading.gif"
        alt=""
        data-src="3.jpg"
        class="lazyload"
      />
    </div>
    <div class="img-container">
      <img
        src="loading.gif"
        alt=""
        data-src="4.jpg"
        class="lazyload"
      />
    </div>
    <div class="img-container">
      <img
        src="loading.gif"
        alt=""
        data-src="5.jpg"
        class="lazyload"
      />
    </div><script>
      const imgs = [...document.querySelectorAll(".lazyload")]; //img元素转换成数组
      lazyload();  //初始调用一次
      window.addEventListener("scroll", lazyload, false);  //监听滚动时也调用lazyload函数,冒泡阶段
      //懒加载
      function lazyload() {
        for (let i = 0; i < imgs.length; i++) {
          const $img = imgs[i];          //每次循环拿到每一个img元素
          if (isInVisibleArea($img)) {   //判断img元素是否在可视区域内
            $img.src = $img.dataset.src; //用data-src替换src属性
            imgs.splice(i, 1);           //对应该索引的元素删掉
            i--;  
          }
        }
      }
      // 判断DOM元素是否在可视区域内,返回true/false
      function isInVisibleArea($el) {
        const rect = $el.getBoundingClientRect();
        console.log(rect);
        return rect.bottom > 0 && rect.top < window.innerHeight && rect.right > 0 && rect.left < window.innerWidth;
      }
    </script>
  </body>
</html>


实现细节

1 如何加载图片

用img的data-src属性存放真正需要显示的图片的路径,当页面滚动直到图片出现在可视区域时,将data-src中的图片地址值赋值给src属性值。

2 如何判断一个元素出现在可视区域

监听滚动事件,用getBoundingClientRect()获取DOM元素在页面的位置,该函数返回rect对象,如下图所示,如果rect.bottom为负数,rect.top大于页面高度,或者rect.right为负数,或者rect.left大于页面宽度,则认为元素已不在页面的可视区域。

const rect = element.getBoundingClientRect();
  • rect.top:元素上边到视窗上边的距离;
  • rect.bottom:元素下边到视窗上边的距离;
  • rect.left:元素左边到视窗左边的距离;
  • rect.right:元素右边到视窗左边的距离;
  • window.innerHeight:视窗高度
  • window.innerWidth: 视窗宽度

在这里插入图片描述

实现效果

利用开发者工具我们可以看到如下截图:

  • 最初打开页面:只显示前两张图片,后面三张图片还没有出现在当前页面
    在这里插入图片描述

在这里插入图片描述

由于后面三张还没有出现,那么就保持loading.gif,这样就节省了加载的时间,当我们继续滚动直到出现页面底部,通过开发者工具看到如下的截图:

在这里插入图片描述

在这里插入图片描述

预加载 preload

预加载提前加载所需要的图片资源,加载完毕后会缓存到本地,当需要时可以立刻显示出来。

在这里插入图片描述

目的:看完一张图片,再看下一张时,会有一段空白的加载时间,如果网络不是很好,加载的时间就会比较久,预加载可以让用户无需等待,获得直接预览的良好体验。

应用场景:看漫画时,如果我们看完 2 了 ,想看 33 却还没加载完就会大大降低体验感,而如果能在我们预览 2 这段时间里就提前加载好 3 , 等到我们看 3 时就直接里面显示。那么就体验会好很多。参考视频讲解:进入学习

实现方法

1 通过CSS

步骤

  • 创建用来预加载的标签
  • 给标签使用背景图,背景图的路径是需要预加载的图片资源,并将图片移到看不见的地方,或缩小到看不见。
  • 当使用到已经预加载好的图片时,会直接使用缓存好的图片资源,而不需要向服务器发送请求。
<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
  <title>CSS 预加载</title>
  <style>
    ul,li{ 
      padding:0;
      margin:0;
      list-style:none;
    }
    img {
      width: 300px;
    }
    #preImg1 {
      background-image: url('1.jpg');
      width: 0;
      height: 0;  /* 隐藏用来预加载的标签 */
    }
    #preImg2 {
      background-image: url('2.jpg');
      width: 0;
      height: 0;
    }
    #preImg3 {
      background-image: url('3.jpg');
      width: 0;
      height: 0;
    }
  </style>
</head>
<body>
  <button onclick="show()">点击显示图片</button>
  <ul>
    <li id="preImg1"/>
    <li id="preImg2"/>
    <li id="preImg3"/>
  </ul>
</body>
<script>
  const show = function () {
    document.querySelector("ul").innerHTML = `
      <li><img src="1.jpg"></li>
      <li><img src="2.jpg"></li>
      <li><img src="3.jpg"></li>`
  }
</script>
</html>


2 通过JavaScript

步骤

  • 将需要预加载的图片资源的 URL 保存在数组里
  • 循环遍历 URL 数组执行以下步骤,直到结束
  • 创建一个 image 对象 new Image()
  • 将 image 对象的 src 属性的值指定为预加载图片的 URL
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>图片预加载</title>
    <style>
      .img-container {
        display: flex;
        align-items: center;
        height: 100vh;
        background-color: rgba(0, 0, 0, 0.5);
      }
      img {
        width: 100%;
      }
      * {
        margin: 0;
        padding: 0;
      }
    </style>
  </head>
  <body>
    <div class="img-container">
      <img src="1.jpg" alt="" id="img" />
    </div>

    <script>
      const imgs = [
        "2.jpg",
        "3.jpg",
        "4.jpg",
        "5.jpg",
      ];
      let i = 0;
      const $img = document.getElementById("img");

      // 页面一开始调用preload,加载数组的第一个元素
      preload(imgs[i])
        .then((data) => {})
        .catch(() => {});

      // 点击切换
      $img.addEventListener(
        "click",
        () => {
          if (i < imgs.length) {
            $img.src = imgs[i];  // 将数组元素的src赋值给页面元素
            i++;
            if (i < imgs.length) { 
              preload(imgs[i]);  // 当索引小于数组length,预加载下一个图片
            }
          } else {
            console.log("已经是最后一张了!");  // 当 索引和 数组length相同 则数组内没元素了
          }
        },
        false
      );

      // 预加载
      function preload(src) {
        return new Promise((resolve, reject) => {
          const image = new Image();  // 创建一个新的图片标签
          image.addEventListener("load", () => resolve(image), false);  // 图片加载完成调用成功状态
          image.addEventListener("error", () => reject(), false);   // 图片加载失败调用失败状态
          image.src = src;  // 将传进来的src赋值给新的图片
        });
      }
    </script>
  </body>
</html>

实现效果

在这里插入图片描述

在这里插入图片描述

总结对比

懒加载预加载
定义延迟加载、按需加载提前加载、不需要也提前加载
目的更好更快地加载页面首屏内容,网页性能优化让用户无需等待,获得直接预览的良好体验
缺点需要监听图片是否显示,耗费浏览器性能占用较多的后台资源,可能一次性加载较多的图片
应用场景电商搜索产品时图片展示观看漫画时,每次切换的下一张图片提前加载
### 懒加载的实现方式 懒加载的核心思想是延迟加载资源,直到用户真正需要时才进行加载。在前端开发中,最常见的懒加载方式是针对图片资源,但也可以扩展到其他类型的资源,如视频、脚本或模块。 1. **原生实现**: - 使用 `IntersectionObserver` API 是目前最推荐的方式,它可以在不阻塞主线程的情况下检测元素是否进入视口。 - 示例代码如下: ```javascript const images = document.querySelectorAll('img[data-src]'); const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { const img = entry.target; img.src = img.dataset.src; observer.unobserve(img); } }); }, { rootMargin: '0px 0px 200px 0px' }); // 提前加载视口下方200px的图片 images.forEach(img => observer.observe(img)); ``` 通过设置 `rootMargin`,可以在图片进入视口之前就开始加载,从而提升用户体验[^2]。 2. **框架集成**: - 在 Vue 或 React 等现代前端框架中,懒加载可以通过自定义指令或组件来实现。 - 例如,在 Vue 中,可以使用 `v-lazy` 指令来实现图片懒加载: ```vue <template> <img v-lazy="imageUrl" /> </template> ``` Vue 的 `v-lazy` 指令会自动处理图片的延迟加载,并且可以与 `IntersectionObserver` 结合使用以获得更好的性能表现。 3. **小程序实现**: - 在微信小程序等环境中,懒加载通常通过自定义组件或 API 实现。 - 例如,微信小程序提供了 `wx.createIntersectionObserver` 接口来监听元素是否进入视口,从而触发图片加载。 ### 预加载的实现方式 预加载是指在用户尚未请求某些资源时,提前将这些资源加载到浏览器缓存中。这样可以加快后续页面或组件的加载速度,提升用户体验。 1. **手动预加载**: - 可以使用 JavaScript 创建 `Image` 对象并设置其 `src` 属性来预加载图片。 - 示例代码如下: ```javascript const preloadImage = (url) => { const img = new Image(); img.src = url; }; preloadImage('https://example.com/image.jpg'); ``` 这种方法简单有效,适用于少量资源的预加载。 2. **使用 `<link rel="prefetch">` 或 `<link rel="preload">`**: - `<link rel="prefetch">` 用于预加载未来可能需要的资源,适用于跨页面的资源加载。 - `<link rel="preload">` 用于预加载当前页面所需的资源,确保这些资源尽早加载完成。 - 示例代码如下: ```html <link rel="prefetch" href="https://example.com/next-page.html" /> <link rel="preload" href="https://example.com/important-image.jpg" as="image" /> ``` 这些标签可以帮助浏览器更好地管理资源加载顺序,从而提升页面性能[^3]。 3. **框架库的支持**: - 在 React 中,可以使用 `useEffect` 钩子在组件挂载时预加载资源。 - 在 Vue 中,可以在 `mounted` 生命周期钩子中执行预加载逻辑。 - 此外,一些第三方库(如 `lazysizes`)也提供了预加载功能,能够根据用户的滚动行为智能地预加载即将进入视口的资源。 ### 应用场景 1. **懒加载的应用场景**: - **长页面或无限滚动页面**:对于包含大量图片或内容的页面(如社交媒体、电商平台),懒加载可以显著减少初始加载时间,提升用户体验。 - **移动端优化**:在移动设备上,网络带宽有限,懒加载可以帮助节省流量并加快页面加载速度。 - **低优先级资源**:对于页面中不立即可见的资源(如下方的图片或视频),懒加载可以避免不必要的资源请求。 2. **预加载的应用场景**: - **关键资源**:对于页面中必须立即显示的关键资源(如首屏图片、CSS、JavaScript 文件),预加载可以确保这些资源尽快加载完成。 - **用户即将访问的内容**:在用户可能点击的链接或即将进入的页面中,提前预加载相关资源可以减少后续加载的延迟。 - **静态资源优化**:对于静态资源(如字体、图标、样式表),预加载可以避免这些资源成为页面渲染的瓶颈。 ### 总结 懒加载预加载前端性能优化中两种互补的技术。懒加载通过延迟加载非关键资源,减少了初始加载时间服务器压力;而预加载则通过提前加载关键资源,提升了后续页面或组件的加载速度。开发者应根据具体的业务场景用户行为,灵活选择合适的加载策略,以实现最佳的性能优化效果[^4]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值