











你希望在 Next.js 中为所有图片自动生成对应的 base64 占位符,无需手动逐个配置 blurDataURL,核心思路是通过「批量脚本预处理」(静态图片)或「Next.js 插件自动处理」(动态/静态图片)实现,以下是两种最优方案(按易用性和实用性排序):
利用 sharp(Node.js 高性能图片处理库)编写批量脚本,一次性遍历 public 目录下所有图片,自动生成缩略图并转换为 base64,保存为配置文件,后续组件统一读取该配置实现自动注入 blurDataURL,适合图片固定的项目。
# npm
npm install sharp --save-dev
# yarn
yarn add sharp --dev
# pnpm
pnpm add sharp -D
在项目根目录新建 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();
在 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"
}
在 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}
/>
);
}
替换项目中所有 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-blurhash 插件,它会在 Next.js 构建/开发时,自动为 Image 组件生成 blurhash(可转为 base64 占位符),无需手动处理,更便捷。
# npm
npm install next-blurhash blurhash --save
# yarn
yarn add next-blurhash blurhash
# pnpm
pnpm add next-blurhash blurhash
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));
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}
/>
);
}
与方案一一致,直接替换 next/image 即可,所有图片自动生成 base64 占位符:
<AspectRatio ratio={32 / 15} className="bg-muted">
<GlobalImage
src="/images/banner1.webp"
alt={_.alt}
fill
priority={true}
className="object-cover"
/>
</AspectRatio>
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| sharp 批量脚本 | 完全可控(base64 尺寸/质量)、无运行时依赖、性能优 | 新增图片需重新运行脚本、仅支持本地静态图片 | 图片固定的项目、追求极致性能 |
| next-blurhash 插件 | 自动按需生成、支持动态图片、无需手动脚本 | 有额外依赖、base64 效果不可自定义 | 图片动态新增的项目、追求便捷性 |
getBlurHashBase64 传入远程图片 URL(需配置 images.domains 允许跨域),自动生成 base64 占位。blurDataURL。package.json 的 build 命令前添加 npm run generate:image-base64,实现构建前自动更新配置。priority、fill、CDN、缓存 等原有配置,不影响 LCP 优化。npm run generate:image-base64 即可更新配置。GlobalImage 组件,统一处理 base64 占位注入,实现所有图片无需手动配置,自动生效。AspectRatio + Image 组件结构,兼容原有 LCP 优化、CDN 配置等功能。此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。