你希望在 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/image 为 GlobalImage,无需手动添加 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.json的build命令前添加npm run generate:image-base64,实现构建前自动更新配置。 - 兼容现有优化:两种方案均兼容
priority、fill、CDN、缓存等原有配置,不影响 LCP 优化。
4. 新增图片处理
- 方案一:新增本地图片后,重新运行
npm run generate:image-base64即可更新配置。 - 方案二:新增图片无需额外操作,插件会自动在构建/开发时生成 base64 占位。
总结
- 若图片固定,优先选择 sharp 批量脚本方案,可控性强、性能优,无需额外依赖。
- 若图片动态新增,选择 next-blurhash 插件方案,便捷高效,自动按需生成。
- 核心关键:封装全局
GlobalImage组件,统一处理 base64 占位注入,实现所有图片无需手动配置,自动生效。 - 兼容性:两种方案均完全保留你的
AspectRatio + Image组件结构,兼容原有 LCP 优化、CDN 配置等功能。
浙公网安备 33010602011771号