如何实现一个懒加载?

96 阅读4分钟

什么是懒加载?

懒加载是一种延迟加载策略,意味着资源或数据不会在应用初始化时就加载,而是只有在需要时才进行加载。在 Web 前端开发中,懒加载通常指的是在用户滚动或其他交互触发时才加载图像、模块、组件等资源。这样可以减少不必要的初始加载时间,提高页面渲染速度。

为什么需要懒加载?

  1. 性能优化:懒加载可以避免在页面加载时一次性加载所有资源,减小页面初次加载的体积。
  2. 带宽节省:用户可能并不会访问页面上的所有内容,通过懒加载可以减少无用资源的加载,节省带宽。
  3. 提升用户体验:让用户在最短的时间内看到页面的主要内容,其他资源可以在用户需要时再加载。

懒加载的常见实现方式

  1. 图片懒加载:图片懒加载是最常见的应用场景,只有当图片即将出现在视口时才会加载。
  2. 组件懒加载:在 SPA 中,可以通过路由懒加载或按需加载 Vue、React 等框架中的组件。
  3. 模块懒加载:通过动态 import() 加载模块,减少初始加载的 JS 文件大小。

如何实现图片懒加载?

下面通过一个简单的图片懒加载示例来演示如何实现懒加载。

1. 基本原理

图片懒加载的原理是:默认情况下,图片的 src 属性不会直接指向真实的图片地址,而是指向一个占位图或空白图。图片的真实地址会设置在 data-srcdata-lazy 等自定义属性上。当图片即将进入视口时,才会通过 JavaScript 设置图片的 src 属性,加载真实的图片。

2. HTML 结构

我们将使用 data-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>
        .img-container {
            width: 100%;
            height: 300px;
            margin: 20px 0;
        }
        img {
            width: 100%;
            height: 100%;
            object-fit: cover;
        }
    </style>
</head>
<body>
    <div class="img-container">
        <img data-src="image1.jpg" src="placeholder.jpg" alt="image1" class="lazy-load">
    </div>
    <div class="img-container">
        <img data-src="image2.jpg" src="placeholder.jpg" alt="image2" class="lazy-load">
    </div>
    <div class="img-container">
        <img data-src="image3.jpg" src="placeholder.jpg" alt="image3" class="lazy-load">
    </div>

    <script src="lazyload.js"></script>
</body>
</html>

3. JavaScript 实现

下面的 JavaScript 使用了原生 IntersectionObserver API 来判断图片是否进入视口,当图片即将出现在视口时,懒加载该图片。

// lazyload.js
document.addEventListener("DOMContentLoaded", function () {
    // 创建 IntersectionObserver 实例
    const observer = new IntersectionObserver((entries, observer) => {
        entries.forEach(entry => {
            // 判断图片是否进入视口
            if (entry.isIntersecting) {
                const img = entry.target;
                // 将 data-src 的真实图片地址赋值给 src 属性
                img.src = img.getAttribute('data-src');
                // 图片加载完成后,停止观察该图片
                observer.unobserve(img);
            }
        });
    }, {
        // 设置视口的边距,提前加载
        rootMargin: "50px 0px"
    });

    // 获取所有带有 lazy-load 类的图片
    const images = document.querySelectorAll('.lazy-load');
    // 开始观察每一张图片
    images.forEach(img => {
        observer.observe(img);
    });
});

4. 代码解释

  1. HTML 结构

    • 每张图片使用 data-src 属性存储真实的图片地址,而 src 属性则指向一个占位图(如 placeholder.jpg)。这样一开始图片是不可见的。
    • 每个图片都带有一个 lazy-load 类,方便我们在 JavaScript 中选择。
  2. JavaScript 代码

    • IntersectionObserver 用于监听图片是否进入视口。entries 是一组 IntersectionObserverEntry 对象,代表了每一个被观察的元素的状态。
    • entries 中,我们判断 entry.isIntersecting 是否为 true,即图片是否进入了视口。如果进入了视口,就加载图片。
    • 通过 img.getAttribute('data-src') 获取图片的真实地址,并赋值给 src 属性,这样浏览器就开始加载该图片。
    • 一旦图片加载完毕,调用 observer.unobserve(img) 停止观察该图片,以提高性能。

5. 代码优化

可以通过使用 requestIdleCallback 来优化懒加载的性能,或者利用第三方库(如 lazysizes)来增强懒加载功能,支持更复杂的场景和更好的兼容性。

如何实现组件懒加载?

在现代 JavaScript 框架(如 Vue 和 React)中,组件懒加载通常通过动态导入(import())来实现。下面展示在 Vue 和 React 中如何实现组件懒加载。

Vue 组件懒加载

在 Vue 中,懒加载组件非常简单,可以通过 Vue 的 defineAsyncComponent 实现:

<template>
  <div>
    <h1>欢迎来到 Vue 应用</h1>
    <lazy-loaded-component />
  </div>
</template>

<script>
import { defineAsyncComponent } from 'vue';

export default {
  components: {
    // 懒加载组件
    'lazy-loaded-component': defineAsyncComponent(() =>
      import('./LazyLoadedComponent.vue')
    )
  }
}
</script>

在上面的代码中,LazyLoadedComponent.vue 组件会被懒加载,只有在需要时才会被加载。

React 组件懒加载

在 React 中,可以使用 React.lazySuspense 来实现懒加载:

import React, { Suspense } from 'react';

// 使用 React.lazy 实现懒加载
const LazyLoadedComponent = React.lazy(() => import('./LazyLoadedComponent'));

function App() {
  return (
    <div>
      <h1>欢迎来到 React 应用</h1>
      <Suspense fallback={<div>加载中...</div>}>
        <LazyLoadedComponent />
      </Suspense>
    </div>
  );
}

export default App;

在上面的代码中,LazyLoadedComponent 会被懒加载,Suspense 组件用于显示加载中的占位符。