豆包给的生成blurDataURL方案

Posted on 2026-01-01 14:39  kacoro  阅读(3)  评论(0)    收藏  举报

你希望在 Next.js 中为所有图片自动生成对应的 base64 占位符,无需手动逐个配置 blurDataURL,核心思路是通过「批量脚本预处理」(静态图片)或「Next.js 插件自动处理」(动态/静态图片)实现,以下是两种最优方案(按易用性和实用性排序):

方案一:sharp 库 + 批量脚本(推荐,本地静态图片专属)

利用 sharp(Node.js 高性能图片处理库)编写批量脚本,一次性遍历 public 目录下所有图片,自动生成缩略图并转换为 base64,保存为配置文件,后续组件统一读取该配置实现自动注入 blurDataURL,适合图片固定的项目。

步骤 1:安装依赖

# npm
npm install sharp --save-dev

# yarn
yarn add sharp --dev

# pnpm
pnpm add sharp -D

步骤 2:编写批量生成脚本

在项目根目录新建 scripts/generateImageBase64.js,脚本会自动遍历 public/images 目录(可自定义路径),生成所有图片的 base64 占位符,并保存为 src/utils/imageBase64Map.json 配置文件:

const sharp = require('sharp');
const fs = require('fs');
const path = require('path');

// 配置项
const SOURCE_DIR = path.join(__dirname, '../public/images'); // 本地图片目录
const OUTPUT_JSON = path.join(__dirname, '../src/utils/imageBase64Map.json'); // 输出的base64配置文件
const THUMBNAIL_SIZE = 16; // 缩略图尺寸(越小base64体积越小,建议16x16)

// 递归遍历目录下所有图片
function getAllImagePaths(dir) {
  const imagePaths = [];
  const files = fs.readdirSync(dir, { withFileTypes: true });

  for (const file of files) {
    const fullPath = path.join(dir, file.name);
    if (file.isDirectory()) {
      // 递归处理子目录
      imagePaths.push(...getAllImagePaths(fullPath));
    } else {
      // 过滤支持的图片格式
      const ext = path.extname(file.name).toLowerCase();
      if (['.jpg', '.jpeg', '.png', '.webp', '.avif'].includes(ext)) {
        imagePaths.push(fullPath);
      }
    }
  }
  return imagePaths;
}

// 生成单张图片的base64占位符
async function generateImageBase64(imagePath) {
  try {
    // 压缩为缩略图并转为webp格式(体积最小)
    const buffer = await sharp(imagePath)
      .resize(THUMBNAIL_SIZE, THUMBNAIL_SIZE, { fit: 'cover' })
      .webp({ quality: 20 }) // 低质量,确保模糊效果和小体积
      .toBuffer();

    // 转为base64字符串
    return `data:image/webp;base64,${buffer.toString('base64')}`;
  } catch (err) {
    console.error(`处理图片失败:${imagePath}`, err);
    return '';
  }
}

// 主流程
async function main() {
  try {
    // 1. 获取所有图片路径
    const imagePaths = getAllImagePaths(SOURCE_DIR);
    if (imagePaths.length === 0) {
      console.log('未找到任何图片');
      return;
    }

    // 2. 批量生成base64
    const imageBase64Map = {};
    for (const imagePath of imagePaths) {
      // 获取相对public目录的路径(如 /images/banner1.webp)
      const relativePath = path.relative(path.join(__dirname, '../public'), imagePath);
      const publicPath = `/${relativePath.replace(/\\/g, '/')}`; // 兼容Windows路径分隔符

      // 生成base64
      const base64 = await generateImageBase64(imagePath);
      if (base64) {
        imageBase64Map[publicPath] = base64;
        console.log(`已处理:${publicPath}`);
      }
    }

    // 3. 确保输出目录存在
    const outputDir = path.dirname(OUTPUT_JSON);
    if (!fs.existsSync(outputDir)) {
      fs.mkdirSync(outputDir, { recursive: true });
    }

    // 4. 保存为JSON文件
    fs.writeFileSync(OUTPUT_JSON, JSON.stringify(imageBase64Map, null, 2), 'utf-8');
    console.log(`\n所有图片base64生成完成!配置文件路径:${OUTPUT_JSON}`);
  } catch (err) {
    console.error('批量生成base64失败', err);
  }
}

// 执行脚本
main();

步骤 3:运行脚本生成配置

package.json 中添加脚本命令,方便运行:

{
  "scripts": {
    "generate:image-base64": "node scripts/generateImageBase64.js"
  }
}

执行脚本:

npm run generate:image-base64

执行成功后,会在 src/utils/imageBase64Map.json 生成如下配置(键为图片 public 路径,值为对应的 base64):

{
  "/images/banner1.webp": "data:image/webp;base64,UklGRkoAAABXRUJQVlA4WAoAAAAQAAAADwAABwAAQUxQSDIAAAARL0AmbZurmr57yyIiqE8oiG0bejIYEQTgqiDA9vqnsUSI6H+oAERp2HZ65qP/VIAWAFZQOCBCAAAA8AEAnQEqEAAIAAVAfCWkAALp8sF8rgRgAP7o9FDvMCkMde9PK7euH5M1m6VWoDXf2FkP3BqV0ZYbO6NA/VFIAAAA",
  "/images/product/1.webp": "data:image/webp;base64,UklGRlQAAABXRUJQVlA4WAoAAAAQAAAADwAABwAAQUxQSDIAAAARL0AmbZurmr57yyIiqE8oiG0bejIYEQTgqiDA9vqnsUSI6H+oAERp2HZ65qP/VIAWAFZQOCBCAAAA8AEAnQEqEAAIAAVAfCWkAALp8sF8rgRgAP7o9FDvMCkMde9PK7euH5M1m6VWoDXf2FkP3BqV0ZYbO6NA/VFIAAAA"
}

步骤 4:封装全局 Image 组件(自动注入 base64)

src/components/GlobalImage.tsx 封装通用 Image 组件,自动读取 imageBase64Map.json,为所有图片注入对应的 blurDataURL,无需手动配置:

import Image, { ImageProps } from 'next/image';
import imageBase64Map from '@/utils/imageBase64Map.json';

// 全局 Image 组件,自动注入 blurDataURL
export default function GlobalImage(props: ImageProps) {
  const { src, placeholder = 'blur', ...restProps } = props;

  // 仅对静态图片(public目录,字符串格式src)自动注入 base64
  let blurDataURL = '';
  if (typeof src === 'string' && src.startsWith('/')) {
    // 从配置中获取对应 base64
    blurDataURL = imageBase64Map[src] || '';
  }

  return (
    <Image
      src={src}
      placeholder={blurDataURL ? 'blur' : placeholder} // 有base64则启用模糊占位
      blurDataURL={blurDataURL || props.blurDataURL} // 自动注入base64
      {...restProps}
    />
  );
}

步骤 5:使用全局 Image 组件(所有图片自动带 base64 占位)

替换项目中所有 next/imageGlobalImage,无需手动添加 blurDataURL,自动生效:

import GlobalImage from '@/components/GlobalImage';
import AspectRatio from '@/components/AspectRatio';

export default function YourComponent({ _ }) {
  return (
    <AspectRatio ratio={32 / 15} className="bg-muted">
      {/* 无需手动配置 blurDataURL,自动注入对应 base64 */}
      <GlobalImage
        src="/images/banner1.webp"
        alt={_.alt}
        fill
        priority={true}
        className="object-cover"
      />
    </AspectRatio>
  );
}

方案二:Next.js 插件(next-blurhash,自动按需生成,支持动态图片)

如果你的项目有动态新增图片的需求(无需手动运行脚本),可使用 next-blurhash 插件,它会在 Next.js 构建/开发时,自动为 Image 组件生成 blurhash(可转为 base64 占位符),无需手动处理,更便捷。

步骤 1:安装依赖

# npm
npm install next-blurhash blurhash --save

# yarn
yarn add next-blurhash blurhash

# pnpm
pnpm add next-blurhash blurhash

步骤 2:配置 next.config.js

import type { NextConfig } from "next";
import createNextIntlPlugin from "next-intl/plugin";
import withBlurHash from "next-blurhash";

const withNextIntl = createNextIntlPlugin({
  experimental: {
    createMessagesDeclaration: "./messages/en.json",
  },
});

const nextConfig: NextConfig = {
  // 你的原有配置...
  output: process.env.NEXT_OUTPUT_STANDALONE === 'true' ? "standalone" : undefined,
  allowedDevOrigins: ["local-origin.dev", "*.local-origin.dev", "https://static.areaflysolar.com"],
  images: {
    formats: ["image/webp"],
    deviceSizes: [375, 640, 750, 828, 1080, 1200, 1920],
    imageSizes: [64, 128, 256, 375, 512, 1024],
  },
};

// 组合插件
export default withBlurHash(withNextIntl(nextConfig));

步骤 3:封装全局 Image 组件(自动生成 base64 占位)

import Image, { ImageProps } from 'next/image';
import { getBlurHashBase64 } from 'next-blurhash';

export default function GlobalImage(props: ImageProps) {
  const { src, placeholder = 'blur', ...restProps } = props;

  // 自动生成 blurhash base64 占位符
  let blurDataURL = '';
  if (typeof src === 'string' && src.startsWith('/')) {
    // getBlurHashBase64 自动为静态图片生成 base64 模糊占位
    blurDataURL = getBlurHashBase64(src) || '';
  }

  return (
    <Image
      src={src}
      placeholder={blurDataURL ? 'blur' : placeholder}
      blurDataURL={blurDataURL || props.blurDataURL}
      {...restProps}
    />
  );
}

步骤 4:使用组件

与方案一一致,直接替换 next/image 即可,所有图片自动生成 base64 占位符:

<AspectRatio ratio={32 / 15} className="bg-muted">
  <GlobalImage
    src="/images/banner1.webp"
    alt={_.alt}
    fill
    priority={true}
    className="object-cover"
  />
</AspectRatio>

关键补充说明

1. 两种方案对比

方案 优点 缺点 适用场景
sharp 批量脚本 完全可控(base64 尺寸/质量)、无运行时依赖、性能优 新增图片需重新运行脚本、仅支持本地静态图片 图片固定的项目、追求极致性能
next-blurhash 插件 自动按需生成、支持动态图片、无需手动脚本 有额外依赖、base64 效果不可自定义 图片动态新增的项目、追求便捷性

2. 兼容远程图片

  • 方案一:不支持远程图片(仅本地 public 目录)。
  • 方案二:可通过 getBlurHashBase64 传入远程图片 URL(需配置 images.domains 允许跨域),自动生成 base64 占位。
  • 额外方案:远程图片可通过后端接口返回缩略图 base64,前端组件接收后注入 blurDataURL

3. 性能优化

  • base64 体积控制:缩略图尺寸建议 16x16,格式优先 WebP/AVIF,质量 20 左右,确保单个 base64 < 1KB。
  • 避免重复生成:脚本方案可在 package.jsonbuild 命令前添加 npm run generate:image-base64,实现构建前自动更新配置。
  • 兼容现有优化:两种方案均兼容 priorityfillCDN缓存 等原有配置,不影响 LCP 优化。

4. 新增图片处理

  • 方案一:新增本地图片后,重新运行 npm run generate:image-base64 即可更新配置。
  • 方案二:新增图片无需额外操作,插件会自动在构建/开发时生成 base64 占位。

总结

  1. 若图片固定,优先选择 sharp 批量脚本方案,可控性强、性能优,无需额外依赖。
  2. 若图片动态新增,选择 next-blurhash 插件方案,便捷高效,自动按需生成。
  3. 核心关键:封装全局 GlobalImage 组件,统一处理 base64 占位注入,实现所有图片无需手动配置,自动生效。
  4. 兼容性:两种方案均完全保留你的 AspectRatio + Image 组件结构,兼容原有 LCP 优化、CDN 配置等功能。