用 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);
                });
            }
        };
    }

总结

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

相关推荐
juruiyuan11118 分钟前
FFmpeg3.4 libavcodec协议框架增加新的decode协议
前端
Peter 谭40 分钟前
React Hooks 实现原理深度解析:从基础到源码级理解
前端·javascript·react.js·前端框架·ecmascript
LuckyLay2 小时前
React百日学习计划——Deepseek版
前端·学习·react.js
gxn_mmf2 小时前
典籍知识问答重新生成和消息修改Bug修改
前端·bug
hj10432 小时前
【fastadmin开发实战】在前端页面中使用bootstraptable以及表格中实现文件上传
前端
乌夷2 小时前
axios结合AbortController取消文件上传
开发语言·前端·javascript
晓晓莺歌3 小时前
图片的require问题
前端
码农黛兮_463 小时前
CSS3 基础知识、原理及与CSS的区别
前端·css·css3
水银嘻嘻4 小时前
web 自动化之 Unittest 四大组件
运维·前端·自动化
(((φ(◎ロ◎;)φ)))牵丝戏安4 小时前
根据输入的数据渲染柱形图
前端·css·css3·js