前端图片自动化压缩:一个支持 CLI 与 Vite 构建集成的无损优化工具

32 阅读4分钟

在日常前端开发中,图片资源往往是网页体积的“大头”。之前开发的太阳系仿真系统(代码已开源:gitee.com/zach2019/so… TinyPNG、Squoosh),但它们通常依赖手动上传 → 下载 → 替换的操作流程——不仅重复枯燥,还难以融入现代前端工程化的自动化流水线。 作为开发者,我自然是希望能自动绝不手动,代码能解决的问题绝不麻烦别人。无论是处理存量静态资源,还是在构建时自动压缩被引用的图片,都应无需人工干预。正因如此,我开发了开源工具 @zhaoshijun/compress —— 一套集 CLI 批量处理与 Vite 构建集成于一体的图片自动化压缩方案,真正做到解放前端开发的双手。

npm包地址:www.npmjs.com/package/@zh…

源码地址:gitee.com/zach2019/im…

✨ 核心亮点

  • 「🛠 双模驱动」

    • 「CLI 模式」:一键扫描并批量压缩本地存量图片。
    • 「Vite 插件模式」:在 vite build 构建时自动拦截并压缩引用的图片,开发环境零感知。
  • 「⚡️ 极速性能」:底层基于 Node.js 高性能图片处理库 「Sharp」,速度飞快,质量无损。

  • 「📦 智能缓存」:Vite 插件内置 Content Hash 缓存机制,「未修改的图片跳过压缩」,拒绝拖慢构建速度。

  • 「🛡 安全可靠」:CLI 模式默认不覆盖原图,支持备份;Vite 模式仅处理引用资源。


💡 设计思路解密

这个工具的设计初衷是**“一套逻辑,多端复用”**。将核心压缩能力封装,分别通过 CLI 和 Vite 插件暴露给用户。

1. 核心逻辑:Sharp 封装

为了保证压缩质量和速度,我选择了 sharp。核心函数抹平了不同格式的参数差异。

// src/core/compressor.js
import sharp from 'sharp';

export async function compressImage(input, options) {
  let instance = sharp(input);
  
  // 统一处理格式参数
  if (format === 'png') {
    // 开启调色板量化,显著减小体积
    instance = instance.png({ palettetruecompressionLevel9 });
  } else {
    // JPEG/WebP 默认高质量压缩
    instance = instance.jpeg({ quality: options.quality || 88 });
  }

  return instance.toBuffer();
}

2. Vite 插件:构建流拦截

Vite 插件的核心在于利用 Rollup 的 generateBundle 钩子。在这个阶段,我们能获取到所有即将输出的资源文件,直接替换其内容。

// vite/index.js
export function compressVitePlugin(options) {
  return {
    name'@zhaoshijun/compress',
    apply'build'// 仅在构建时生效
    async generateBundle(_, bundle) {
      for (const fileName in bundle) {
        // 筛选图片资源
        if (isSupportedImage(fileName)) {
          const originalBuffer = bundle[fileName].source;
          
          // ⚡️ 核心:压缩并替换资源内容
          const compressed = await compressImage(originalBuffer, config);
          bundle[fileName].source = compressed;
          
          // 同时更新文件名(如 logo.png -> logo.min.png)
          // 并自动替换 JS/CSS 中的引用路径
        }
      }
    }
  };
}

3. 性能优化:基于内容的哈希缓存

为了避免每次构建都重新压缩所有图片,我们实现了一个简单的文件系统缓存。

// src/utils/cache.js
export async function getCache(content) {
  // 计算文件内容 Hash
  const hash = crypto.createHash('sha256').update(content).digest('hex');
  const cachePath = path.join(CACHE_DIR, hash);
  
  // 如果 Hash 命中,直接返回缓存文件,跳过压缩
  if (await fs.pathExists(cachePath)) {
    return await fs.readFile(cachePath);
  }
  return null;
}

🛠 快速上手指南

第一步:安装

npm install @zhaoshijun/compress -D

场景一:使用 CLI 批量压缩存量图片

如果你有一堆静态图片需要处理,直接运行 CLI 命令:

# 扫描当前目录,压缩图片并输出到 ./compressed 目录
npx @zhaoshijun/compress

# 常用参数
# --dry-run : 仅预览,不实际写入
# --backup  : 备份原文件

CLI 会在终端显示进度条和压缩前后的体积对比:

✔ assets/banner.jpg: 1.2 MB → 450 KB (Saved 62.5%)

场景二:集成到 Vite 项目

vite.config.js 中配置插件,即可在打包时自动瘦身:

import { defineConfig } from 'vite';
import { compressVitePlugin } from '@zhaoshijun/compress';

export default defineConfig({
  plugins: [
    compressVitePlugin({
      quality80// 压缩质量
      cachetrue  // 开启缓存
    })
  ]
});

以后每次运行 npm run build,你的产物图片就已经是最优体积了!


🔗 总结

工具化、自动化是提升前端工程效率的关键。@zhaoshijun/compress 通过简单的配置,解决了图片压缩这一高频痛点。

欢迎大家下载体验,如果有问题或建议,欢迎在评论区留言!

本文使用 markdown.com.cn 排版