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

总结

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

相关推荐
donecoding14 小时前
类型与语法的“直觉对齐”:TS 切入的 Go 语言初体验
前端·typescript·go
web守墓人14 小时前
【linux】Mubuntu v1.0.7发布:支持codex cli完整运行
前端·codex
WYiQIU14 小时前
宇树科技Web前端岗(AI方向),这不算泄题吧......
前端·vue.js·人工智能·笔记·科技·面试·职场和发展
Januea14 小时前
Chrome的Fetch/XHR是什么?
前端·chrome
betazhou14 小时前
TDSQL-PG创建测试表并定时插入数据模拟生产
前端·javascript·数据库·tdsql·tdsql-pg
W.A委员会14 小时前
地址栏输入url到显示画面
前端·网络
xuankuxiaoyao14 小时前
Vue.js实践-组件基础上
前端·javascript·vue.js
甄心爱学习14 小时前
【项目实训】法律文书智能摘要系统3
前端·人工智能
冲浪中台14 小时前
从追逐技术到回归业务本质,吃互联网红利罢了
服务器·前端·人工智能·低代码
小马_xiaoen14 小时前
前端虚拟列表(Virtual List)从原理到实战:海量数据渲染终极方案
前端·数据结构·list