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

总结

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

相关推荐
哟哟耶耶14 分钟前
React-04React组件状态(state),构造器初始化state以及数据读取,添加点击事件并更改state状态值
前端·javascript·react.js
kiramario20 分钟前
用IconContext.Provider修改react-icons的icon样式
前端·javascript·react.js
destinyol22 分钟前
React首页加载速度优化
前端·javascript·react.js·webpack·前端框架
程序员小续22 分钟前
React 多个 HOC 嵌套太深,会带来哪些隐患?
java·前端·javascript·vue.js·python·react.js·webpack
大猫会长1 小时前
用AbortController取消事件绑定
前端
程序员小杰@1 小时前
AI前端组件库Ant DesIgn X
开发语言·前端·人工智能
致微2 小时前
Vue项目 bug 解决
前端·vue.js·bug
慕斯策划一场流浪2 小时前
fastGPT—nextjs—mongoose—团队管理之部门相关api接口实现
前端·javascript·html·fastgpt部门创建·fastgpt团队管理·fastgpt部门成员更新·fastgpt部门成员创建
我自纵横20233 小时前
事件处理程序
开发语言·前端·javascript·css·json·ecmascript
坊钰3 小时前
【MySQL 数据库】数据类型
java·开发语言·前端·数据库·学习·mysql·html