用 Vite 插件自动化优化图片

前言

在前端开发中,图片优化一直是一个令人头疼的问题。无论是压缩图片大小,还是将图片格式转换为更高效的 WebP 格式,手动操作不仅繁琐,还容易出错。最近,我在使用 Vite 打包工具时,发现了一个非常实用的插件,可以自动完成图片压缩和格式转换,彻底解放双手。

需求分析

在开发中,我们通常希望实现以下功能:

    1. 自动压缩图片:能够根据配置调整图片质量,减少文件大小。
    1. 自动转 WebP 格式:将图片转换为 WebP 格式,提升加载速度。
    1. 路径替换 :将图片引用路径的后缀自动替换为 .webp
    1. 支持开发和生产环境:在开发环境中实时预览优化效果,在生产环境中高效打包。

技术实现

为了实现这些功能,我选择了一个基于 Vite 的插件,它使用了 sharp 库来处理图片压缩和格式转换。以下是具体实现思路。

生产环境优化

在生产环境中,我们主要通过 Vite 的 generateBundle 钩子函数来处理图片。

    1. 图片压缩 在打包过程中,插件会遍历所有图片资源,使用 sharp 对图片进行压缩。以下是核心代码:
    javascript 复制代码
    export default function ImageOptimizer() {
        return {
            async generateBundle(options, bundle) {
                for (const key in bundle) {
                    const chunk = bundle[key];
                    const { ext } = path.parse(key);
    
                    if (!/(png|jpg|jpeg|webp)$/.test(ext)) {
                        continue;
                    }
    
                    if (chunk.source instanceof Buffer) {
                        const compressedBuffer = await compressImage(chunk.source);
                        chunk.source = compressedBuffer;
                    }
                }
            }
        };
    }
    
    async function compressImage(buffer) {
        return sharp(buffer)
            .resize(800)
            .toFormat("webp", { quality: 80 })
            .toBuffer();
    }

    这段代码会将图片压缩为 WebP 格式,并设置质量为 80,同时限制最大宽度为 800 像素。

    1. 路径替换 为了将图片路径的后缀替换为 .webp,我们需要在打包后的 CSS 和 JS 文件中进行替换。以下是实现逻辑:
    javascript 复制代码
    function replaceWebpPaths(str, map) {
        let result = str;
        for (const key in map) {
            result = result.replace(new RegExp(key, "g"), map[key]);
        }
        return result;
    }
    
    export default function ImageOptimizer() {
        return {
            async generateBundle(options, bundle) {
                const pathMap = {};
    
                for (const key in bundle) {
                    const chunk = bundle[key];
                    const { ext } = path.parse(key);
    
                    if (/(png|jpg|jpeg)$/.test(ext)) {
                        const webpName = key.replace(ext, ".webp");
                        pathMap[key] = webpName;
                    }
    
                    if (/(js|css)$/.test(key)) {
                        if (/(js)$/.test(key)) {
                            chunk.code = replaceWebpPaths(chunk.code, pathMap);
                        } else if (/(css)$/.test(key)) {
                            chunk.source = replaceWebpPaths(chunk.source, pathMap);
                        }
                    }
                }
            }
        };
    }

    这段代码会收集所有图片的原始路径和 WebP 路径的映射关系,并在 CSS 和 JS 文件中替换路径。

开发环境优化

在开发环境中,我们希望实时看到优化效果,同时避免每次修改都重新处理图片。为此,插件在 Vite 的 configureServer 钩子中加入了缓存机制。

    1. 实时优化在开发服务器中,插件会拦截图片请求,动态处理图片并返回优化后的结果:
    javascript 复制代码
    export default function ImageOptimizer() {
        return {
            configureServer(server) {
                server.middlewares.use(async (req, res, next) => {
                    const url = req.url || "";
                    if (!url.endsWith((".png", ".jpg", ".jpeg"))) {
                        return next();
                    }
    
                    try {
                        const filePath = path.resolve(
                            process.cwd(),
                            url.split("?")[0].slice(1)
                        );
                        const buffer = fs.readFileSync(filePath);
                        const optimizedBuffer = await compressImage(buffer);
    
                        res.setHeader("Content-Type", "image/webp");
                        res.end(optimizedBuffer);
                    } catch (e) {
                        next();
                    }
                });
            }
        };
    }
    1. 缓存机制为了避免重复处理相同的图片,插件会为每张图片生成一个唯一的缓存键,并将优化后的图片存储在缓存目录中:
    javascript 复制代码
    function generateCacheKey(filePath, options) {
        const hash = crypto
            .createHash("md5")
            .update(fs.readFileSync(filePath))
            .update(JSON.stringify(options))
            .digest("hex");
    
        const { name, ext } = path.parse(filePath);
        return `${name}_${hash.slice(0, 8)}${ext}`;
    }
    
    export default function ImageOptimizer() {
        return {
            configureServer(server) {
                server.middlewares.use(async (req, res, next) => {
                    const url = req.url || "";
                    if (!url.endsWith((".png", ".jpg", ".jpeg"))) {
                        return next();
                    }
    
                    const filePath = path.resolve(
                        process.cwd(),
                        url.split("?")[0].slice(1)
                    );
                    const cacheKey = generateCacheKey(filePath, { quality: 80 });
                    const cachePath = path.join(
                        "node_modules/.cache",
                        cacheKey
                    );
    
                    if (fs.existsSync(cachePath)) {
                        res.end(fs.readFileSync(cachePath));
                        return;
                    }
    
                    const buffer = fs.readFileSync(filePath);
                    const optimizedBuffer = await compressImage(buffer);
                    fs.writeFileSync(cachePath, optimizedBuffer);
    
                    res.setHeader("Content-Type", "image/webp");
                    res.end(optimizedBuffer);
                });
            }
        };
    }

总结

通过这个插件,我们可以在开发和生产环境中轻松实现图片的自动化优化。它不仅支持图片压缩和格式转换,还能自动替换路径后缀,极大提升了开发效率。如果你对这个插件感兴趣,可以尝试集成到自己的项目中,体验一把"解放双手"的快感!

相关推荐
雪碧聊技术7 分钟前
前端VUE3项目部署到linux服务器(CentOS 7)
前端·linux部署vue3项目
酒尘&6 小时前
JS数组不止Array!索引集合类全面解析
开发语言·前端·javascript·学习·js
学历真的很重要6 小时前
VsCode+Roo Code+Gemini 2.5 Pro+Gemini Balance AI辅助编程环境搭建(理论上通过多个Api Key负载均衡达到无限免费Gemini 2.5 Pro)
前端·人工智能·vscode·后端·语言模型·负载均衡·ai编程
用户47949283569158 小时前
"讲讲原型链" —— 面试官最爱问的 JavaScript 基础
前端·javascript·面试
用户47949283569158 小时前
2025 年 TC39 都在忙什么?Import Bytes、Iterator Chunking 来了
前端·javascript·面试
大怪v9 小时前
【Virtual World 04】我们的目标,无限宇宙!!
前端·javascript·代码规范
狂炫冰美式9 小时前
不谈技术,搞点文化 🧀 —— 从复活一句明代残诗破局产品迭代
前端·人工智能·后端
xw510 小时前
npm几个实用命令
前端·npm
!win !10 小时前
npm几个实用命令
前端·npm
代码狂想家10 小时前
使用openEuler从零构建用户管理系统Web应用平台
前端