MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Next.js图片懒加载优化性能的方法

2022-03-063.0k 阅读

Next.js 图片懒加载基础

在前端开发中,图片懒加载是提升页面性能的重要手段。Next.js 作为一款流行的 React 框架,提供了方便的图片懒加载机制。

Next.js 内置的 next/image 组件默认开启了懒加载功能。这意味着,只有当图片进入浏览器的视口(viewport)时,才会真正去加载图片资源,从而避免了一次性加载大量图片,特别是那些在页面初始渲染时用户不可见的图片,进而节省了带宽并提升了页面加载速度。

基础使用示例

以下是一个简单的使用 next/image 组件进行图片懒加载的示例代码:

import Image from 'next/image';

function MyImageComponent() {
  return (
    <div>
      <Image
        src="/path/to/your/image.jpg"
        alt="An example image"
        width={500}
        height={300}
      />
    </div>
  );
}

export default MyImageComponent;

在上述代码中,通过 next/image 组件,我们只需传入图片的 srcalt 以及指定 widthheight 属性,就实现了图片的懒加载。widthheight 属性对于正确布局图片至关重要,它们可以避免图片在加载过程中导致的页面重排(reflow)。

优化图片加载优先级

虽然 Next.js 的 next/image 组件默认的懒加载策略已经能满足大部分场景,但在一些特定情况下,我们可能需要对图片加载的优先级进行更精细的控制。

关键图片优先加载

有些图片对于页面的核心内容展示非常关键,比如首屏的产品图片、重要的品牌标志等。对于这类图片,我们希望它们能够在页面加载时优先加载,而不是等到进入视口才加载。

next/image 组件中,可以通过设置 priority 属性来实现这一点。示例代码如下:

import Image from 'next/image';

function HeroImageComponent() {
  return (
    <div>
      <Image
        src="/path/to/hero/image.jpg"
        alt="Hero image"
        width={800}
        height={400}
        priority
      />
    </div>
  );
}

export default HeroImageComponent;

通过设置 priority 属性为 true,该图片会在页面加载时被优先处理,确保核心内容快速呈现在用户面前。

延迟加载非关键图片

与关键图片相反,对于一些次要的图片,比如文章中的配图、装饰性的图标等,可以进一步延迟它们的加载,以优化页面的初始加载性能。

虽然 next/image 组件默认已经是懒加载,但我们可以通过一些额外的配置来实现更“懒”的加载策略。例如,结合 IntersectionObserver API 可以实现更加定制化的延迟加载。

首先,创建一个自定义的图片组件,利用 IntersectionObserver 来控制图片的加载时机:

import React, { useRef, useEffect } from'react';
import Image from 'next/image';

const LazyImage = ({ src, alt, width, height }) => {
  const imageRef = useRef(null);
  useEffect(() => {
    const observer = new IntersectionObserver((entries) => {
      entries.forEach((entry) => {
        if (entry.isIntersecting) {
          observer.unobserve(imageRef.current);
        }
      });
    });
    if (imageRef.current) {
      observer.observe(imageRef.current);
    }
    return () => {
      if (imageRef.current) {
        observer.unobserve(imageRef.current);
      }
    };
  }, []);

  return (
    <div ref={imageRef}>
      <Image
        src={src}
        alt={alt}
        width={width}
        height={height}
        // 这里的 loading="lazy" 进一步强调懒加载
        loading="lazy"
      />
    </div>
  );
};

export default LazyImage;

在上述代码中,我们使用了 IntersectionObserver 来观察图片元素是否进入视口。当图片进入视口时,才停止观察并允许图片加载。这样,对于非关键图片,可以更精确地控制其加载时机,进一步优化页面性能。

图片格式与压缩优化

除了懒加载策略,图片本身的格式和压缩程度也对性能有着重大影响。

选择合适的图片格式

不同的图片格式适用于不同的场景。在 Next.js 项目中,常见的图片格式有 JPEG、PNG、WebP 等。

  • JPEG:适用于照片等色彩丰富的图像。它采用有损压缩算法,能够在保持较高视觉质量的同时大幅减小文件大小。例如,对于展示自然风光、人物照片等场景,JPEG 是一个不错的选择。
  • PNG:适合具有透明度的图像,如 logo、图标等。PNG 分为 PNG - 8 和 PNG - 24,PNG - 8 适用于简单的图标和具有较少颜色的图像,它采用无损压缩,文件大小相对较小;PNG - 24 则用于更复杂、色彩丰富且需要透明度的图像,但文件大小通常较大。
  • WebP:这是一种现代的图片格式,由 Google 开发。WebP 格式在压缩比上优于 JPEG 和 PNG,同时支持有损和无损压缩,并且支持透明度。在支持 WebP 的浏览器中,使用 WebP 格式可以显著减小图片文件大小,提升加载速度。

在 Next.js 中,可以通过配置来自动转换图片格式。例如,使用 next - image - optimizer 插件,它可以在构建过程中自动将图片转换为 WebP 格式(前提是浏览器支持)。首先安装插件:

npm install next - image - optimizer

然后在 next.config.js 文件中进行配置:

const withImageOptimizer = require('next - image - optimizer');

module.exports = withImageOptimizer({
  images: {
    deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
    imageSizes: [16, 32, 48, 64, 96, 128, 256, 384],
    loader: 'default',
    path: '/_next/static/images',
    publicPath: '/_next/static/images',
    optimizeImages: true,
    optimizeImagesInDev: true,
    mozjpeg: {
      quality: 80,
    },
    optipng: {
      enabled: false,
    },
    pngquant: {
      quality: [0.8, 0.9],
      speed: 4,
    },
    webp: {
      quality: 75,
    },
  },
});

通过上述配置,在构建过程中,插件会自动将图片转换为 WebP 格式,并根据设置的压缩参数进行优化。

图片压缩

即使选择了合适的图片格式,进一步的图片压缩也能显著提升性能。压缩图片可以减小文件大小,从而加快图片的加载速度。

在 Next.js 项目中,可以使用各种图片压缩工具。例如,image - webpack - loader 是一个常用的 Webpack 加载器,它可以在构建过程中对图片进行压缩。首先安装:

npm install image - webpack - loader --save - dev

然后在 next.config.js 文件中配置:

const withImages = require('next - images');
const path = require('path');

module.exports = withImages({
  webpack(config, options) {
    config.module.rules.push({
      test: /\.(png|jpg|gif|svg)$/,
      use: [
        options.defaultLoader,
        {
          loader: 'image - webpack - loader',
          options: {
            mozjpeg: {
              progressive: true,
              quality: 65,
            },
            // optipng.enabled: false will disable optipng
            optipng: {
              enabled: false,
            },
            pngquant: {
              quality: [0.65, 0.90],
              speed: 4,
            },
            gifsicle: {
              interlaced: false,
            },
            // the webp option will enable WEBP
            webp: {
              quality: 75,
            },
          },
        },
      ],
    });
    return config;
  },
});

通过上述配置,image - webpack - loader 会在构建时对项目中的图片进行压缩,根据设置的参数调整压缩质量,以平衡图片质量和文件大小。

图片占位符优化用户体验

在图片加载过程中,为了提升用户体验,可以使用占位符来填充图片位置,避免页面出现空白闪烁。

模糊占位符

模糊占位符是一种常见的方式,它可以在图片加载前显示一个模糊的低分辨率版本,给用户一个图片大致内容的预览。

在 Next.js 的 next/image 组件中,可以通过 blurDataURL 属性来设置模糊占位符。首先,需要生成一个模糊版本的图片数据 URL。可以使用工具如 blurhash 来生成。安装 blurhash

npm install blurhash

然后编写一个函数来生成 blurDataURL

import { encode } from 'blurhash';
import { readFileSync } from 'fs';
import { resolve } from 'path';
import sharp from'sharp';

async function generateBlurHash(filePath) {
  const buffer = readFileSync(resolve(filePath));
  const { width, height } = await sharp(buffer).metadata();
  const { data } = await sharp(buffer).raw().toBuffer({ resolveWithObject: true });
  const blurHash = encode(new Uint8ClampedArray(data), width, height, 4, 4);
  return `data:image/jpeg;base64,${Buffer.from(blurHash).toString('base64')}`;
}

在组件中使用:

import Image from 'next/image';
import { generateBlurHash } from './generateBlurHash';

function MyImageWithBlur() {
  const blurDataURL = generateBlurHash('/path/to/your/image.jpg');
  return (
    <div>
      <Image
        src="/path/to/your/image.jpg"
        alt="An example image"
        width={500}
        height={300}
        blurDataURL={blurDataURL}
        placeholder="blur"
      />
    </div>
  );
}

export default MyImageWithBlur;

在上述代码中,通过 generateBlurHash 函数生成模糊占位符的 blurDataURL,然后在 next/image 组件中设置 blurDataURLplaceholder="blur",这样在图片加载前会显示模糊占位符。

纯色占位符

除了模糊占位符,纯色占位符也是一种简单有效的方式。可以通过设置 placeholder="empty" 并结合 CSS 来实现纯色占位符。

import Image from 'next/image';

function MyImageWithSolidPlaceholder() {
  return (
    <div>
      <Image
        src="/path/to/your/image.jpg"
        alt="An example image"
        width={500}
        height={300}
        placeholder="empty"
        style={{ backgroundColor: '#ccc' }}
      />
    </div>
  );
}

export default MyImageWithSolidPlaceholder;

在上述代码中,设置 placeholder="empty" 后,通过 style 属性设置背景颜色为灰色(#ccc),从而在图片加载前显示一个纯色占位符。

处理不同设备与屏幕分辨率

在当今多样化的设备环境下,确保图片在不同设备和屏幕分辨率下都能正确加载并优化性能至关重要。

响应式图片

Next.js 的 next/image 组件通过 widthheight 属性结合 CSS 的 max - widthmax - height 可以实现响应式图片效果。同时,next/image 组件还支持 layout 属性来进一步控制图片的布局方式。

  • layout="fill":使图片填充其父元素的空间,同时保持宽高比。这种布局方式适用于需要铺满整个容器的图片,如全屏背景图等。示例代码如下:
import Image from 'next/image';

function FullScreenImage() {
  return (
    <div style={{ position:'relative', width: '100vw', height: '100vh' }}>
      <Image
        src="/path/to/fullscreen/image.jpg"
        alt="Full screen image"
        layout="fill"
        objectFit="cover"
      />
    </div>
  );
}

export default FullScreenImage;

在上述代码中,通过设置 layout="fill" 和父元素的 position: relative,图片会填充整个父元素,objectFit="cover" 确保图片在填充过程中保持合适的显示比例。

  • layout="responsive":这是默认的布局方式,图片会根据其固有尺寸和容器大小进行自适应调整。例如:
import Image from 'next/image';

function ResponsiveImage() {
  return (
    <div style={{ width: '80%' }}>
      <Image
        src="/path/to/responsive/image.jpg"
        alt="Responsive image"
        width={1000}
        height={600}
        layout="responsive"
      />
    </div>
  );
}

export default ResponsiveImage;

在这个例子中,图片会根据父容器宽度的 80% 进行自适应调整,同时保持宽高比。

不同分辨率图片适配

对于高分辨率屏幕(如视网膜屏),需要提供更高分辨率的图片以确保显示质量。在 Next.js 中,可以通过 next/image 组件的 srcSet 属性来实现。

srcSet 允许提供多个不同分辨率的图片源,浏览器会根据设备的像素密度自动选择合适的图片。示例代码如下:

import Image from 'next/image';

function HighResImage() {
  return (
    <div>
      <Image
        src="/path/to/lowres/image.jpg"
        srcSet="/path/to/lowres/image.jpg 1x, /path/to/highres/image.jpg 2x"
        alt="High res image"
        width={500}
        height={300}
      />
    </div>
  );
}

export default HighResImage;

在上述代码中,srcSet 提供了两个图片源,1x 表示普通分辨率图片,2x 表示适用于高分辨率屏幕(像素密度为 2 倍)的图片。浏览器会根据设备的实际情况自动选择加载合适的图片,从而在不同分辨率设备上都能提供良好的显示效果,同时避免加载不必要的高分辨率图片导致性能浪费。

服务器端渲染与图片懒加载协同优化

Next.js 支持服务器端渲染(SSR)和静态站点生成(SSG),在这些场景下,图片懒加载需要特别注意以确保性能优化。

SSR 中的图片懒加载

在服务器端渲染过程中,由于图片懒加载依赖于浏览器的视口检测,在服务器端无法直接实现类似的功能。然而,Next.js 的 next/image 组件在 SSR 场景下依然可以正常工作。

当页面在服务器端渲染时,next/image 组件会生成带有占位符的 HTML 结构,并且在客户端 hydration 完成后,懒加载功能会自动生效。例如:

import Image from 'next/image';

function SSRImageComponent() {
  return (
    <div>
      <Image
        src="/path/to/ssr/image.jpg"
        alt="SSR image"
        width={500}
        height={300}
      />
    </div>
  );
}

export default SSRImageComponent;

在上述代码中,服务器端渲染会生成包含图片占位符的 HTML,客户端加载完成后,图片会按照懒加载策略在进入视口时加载。

SSG 中的图片懒加载

静态站点生成(SSG)同样可以与图片懒加载很好地协同工作。在 SSG 模式下,Next.js 在构建过程中生成静态 HTML 文件。next/image 组件在这个过程中同样会生成占位符结构。

当页面被请求时,静态 HTML 被发送到客户端,客户端 hydration 后,图片懒加载功能生效。例如:

import Image from 'next/image';

export async function getStaticProps() {
  return {
    props: {},
    revalidate: 60,
  };
}

function SSGImageComponent() {
  return (
    <div>
      <Image
        src="/path/to/ssg/image.jpg"
        alt="SSG image"
        width={500}
        height={300}
      />
    </div>
  );
}

export default SSGImageComponent;

在上述代码中,通过 getStaticProps 函数进行静态站点生成配置,next/image 组件在生成的静态 HTML 中包含占位符,客户端加载后实现图片懒加载。

监测与性能分析

为了确保图片懒加载真正提升了性能,需要对其进行监测和性能分析。

使用 Lighthouse 进行性能评估

Lighthouse 是一款由 Google 开发的开源工具,可以在 Chrome 浏览器中使用。它可以对网页进行全面的性能评估,包括图片加载性能。

在 Chrome 浏览器中打开需要评估的页面,然后按下 Ctrl + Shift + I(Windows / Linux)或 Command + Option + I(Mac)打开开发者工具。切换到 Lighthouse 标签页,点击 Generate report 按钮,Lighthouse 会对页面进行一系列测试并生成报告。

在报告中,关注 Performance 指标下的 Largest Contentful Paint(LCP)和 Time to Interactive(TTI)等指标。如果图片懒加载配置正确,这些指标应该会有较好的表现。例如,合理的图片懒加载可以使 LCP 时间缩短,因为关键图片会在合适的时机加载,不会阻塞页面的主要内容渲染。

自定义性能监测

除了使用 Lighthouse 这样的工具,还可以在项目中添加自定义的性能监测代码。例如,通过在图片加载事件中添加日志记录来分析图片加载的时机和性能。

import Image from 'next/image';

function MyMonitoredImage() {
  const handleLoad = () => {
    console.log('Image loaded');
  };
  const handleError = () => {
    console.log('Image load error');
  };
  return (
    <div>
      <Image
        src="/path/to/monitored/image.jpg"
        alt="Monitored image"
        width={500}
        height={300}
        onLoad={handleLoad}
        onError={handleError}
      />
    </div>
  );
}

export default MyMonitoredImage;

在上述代码中,通过 onLoadonError 回调函数,可以在图片加载成功或失败时记录日志,从而进一步分析图片加载过程中的性能和问题。

应对复杂场景下的图片懒加载

在一些复杂的页面结构或特定业务场景下,图片懒加载可能需要更定制化的处理。

动态生成图片路径

在某些应用中,图片路径可能是动态生成的,例如根据用户的角色、偏好或数据库中的数据来决定加载哪张图片。在这种情况下,使用 next/image 组件时需要注意正确设置 src 属性。

import Image from 'next/image';
import { getImagePath } from './imagePathUtils';

function DynamicImageComponent() {
  const userRole = 'admin'; // 假设根据用户角色动态获取图片路径
  const imagePath = getImagePath(userRole);
  return (
    <div>
      <Image
        src={imagePath}
        alt="Dynamic image"
        width={500}
        height={300}
      />
    </div>
  );
}

export default DynamicImageComponent;

在上述代码中,通过 getImagePath 函数根据用户角色动态获取图片路径,并将其设置为 next/image 组件的 src 属性。确保 getImagePath 函数返回的路径是正确且可访问的,这样图片才能正常进行懒加载。

图片懒加载与无限滚动

在无限滚动的列表页面中,图片懒加载需要与滚动机制紧密配合。每次新的数据加载并渲染到页面上时,新的图片也需要进行懒加载。

首先,使用一个库如 react - infinite - scroll - component 来实现无限滚动功能。安装该库:

npm install react - infinite - scroll - component

然后结合 next/image 组件实现图片在无限滚动列表中的懒加载:

import React, { useState } from'react';
import Image from 'next/image';
import InfiniteScroll from'react - infinite - scroll - component';

const data = Array.from({ length: 100 }, (_, i) => ({
  id: i,
  imagePath: `/path/to/image${i}.jpg`,
}));

function InfiniteScrollImageList() {
  const [items, setItems] = useState(data.slice(0, 10));
  const fetchMoreData = () => {
    setItems([...items, ...data.slice(items.length, items.length + 10)]);
  };
  return (
    <InfiniteScroll
      dataLength={items.length}
      next={fetchMoreData}
      hasMore={items.length < data.length}
      loader={<div key="loader">Loading...</div>}
    >
      {items.map((item) => (
        <div key={item.id}>
          <Image
            src={item.imagePath}
            alt={`Image ${item.id}`}
            width={300}
            height={200}
          />
        </div>
      ))}
    </InfiniteScroll>
  );
}

export default InfiniteScrollImageList;

在上述代码中,通过 react - infinite - scroll - component 实现无限滚动,每次滚动到页面底部时加载更多数据。next/image 组件确保新添加到页面的图片也能正常进行懒加载,从而优化了无限滚动列表中图片的加载性能。

通过以上全面深入的方法,在 Next.js 项目中可以有效地实现图片懒加载并优化性能,为用户提供更流畅、高效的浏览体验。无论是基础的懒加载配置,还是针对不同场景的优化策略,都能在提升页面性能和用户体验方面发挥重要作用。