vue3-image-compressor
v1.0.10
Published
Vue3 + TypeScript 图片压缩组件,基于 Squoosh 核心原理
Maintainers
Readme
Vue Image Compressor
基于 Squoosh 核心原理封装的 Vue3 + TypeScript 通用图片压缩组件库。
🚀 在线 Demo
直接上传图片即可试用,无需安装。也可以使用下面的徽章快速访问:
架构分析
图片压缩原理
本项目完整还原了 Squoosh 的压缩流程:
输入文件 → 解码 → 预处理(旋转) → 处理(Resize/Quantize) → 编码 → 输出文件- 解码:浏览器原生支持 JPEG/PNG/GIF,特殊格式(AVIF/WebP/JXL 等)通过 WASM Worker 解码
- 预处理:旋转操作(0°/90°/180°/270°)
- 处理:
- Resize:使用多种重采样算法(Lanczos3、Catrom、Mitchell、Triangle)
- Quantize:颜色量化(调色板缩减),用于减少 PNG 体积
- 编码:
- 浏览器内置:Canvas.toBlob() 生成 JPEG/PNG
- WASM 编码器:MozJPEG、WebP、AVIF、JXL、OxiPNG、QOI、WebP2
关键文件映射
| 原始文件 | 封装文件 | 说明 |
|---------|---------|------|
| src/features/decoders/*/dec.ts | src/workers/imageWorker.ts | 解码器聚合,Worker 中加载 WASM |
| src/features/encoders/*/enc.ts | src/workers/imageWorker.ts | 编码器聚合,Worker 中加载 WASM |
| src/features/processors/resize/*.ts | src/workers/imageWorker.ts | 缩放处理器(resize/quantize) |
| src/features/preprocessors/rotate/*.ts | src/workers/imageWorker.ts | 旋转预处理 |
| src/client/lazy-app/Compress/index.tsx | src/composables/useCompression.ts | 核心编排逻辑 |
| src/client/lazy-app/worker-bridge/index.ts | src/composables/useWorker.ts | Worker 封装与生命周期管理 |
| src/client/lazy-app/feature-meta/index.ts | src/constants/encoders.ts | 编码器注册表与默认参数 |
| src/features/encoders/*/shared/meta.ts | src/types/encoder.ts | 编码器类型定义 |
| src/features/encoders/mozjpeg/encoder-meta.ts | src/constants/encoders.ts | MozJPEG 参数映射 |
项目结构
vue-compressor/
├── src/
│ ├── components/
│ │ └── ImageCompressor.vue # 主组件(上传+预览+设置+操作)
│ ├── composables/
│ │ ├── useCompression.ts # 压缩流程编排(解码→处理→编码)
│ │ ├── useEncoderRegistry.ts # 编码器注册表管理
│ │ └── useWorker.ts # Worker 封装(懒加载+超时回收+取消)
│ ├── workers/
│ │ ├── imageWorker.ts # Web Worker 入口(聚合编解码器)
│ │ └── utils/
│ │ └── emscripten.ts # WASM 初始化工具
│ ├── types/
│ │ ├── encoder.ts # 编码器类型(10种编码器)
│ │ ├── processor.ts # 处理器类型(resize/quantize)
│ │ ├── compression.ts # 压缩结果类型
│ │ └── worker.ts # Worker API 接口
│ ├── constants/
│ │ ├── encoders.ts # 编码器注册表与默认参数
│ │ └── resizeMethods.ts # 重采样算法常量
│ ├── utils/
│ │ ├── image.ts # 图像工具(blob→ImageData、编码检测)
│ │ └── file.ts # 文件工具(格式化、节省计算、BlobURL)
│ └── index.ts # 库入口(导出所有组件/Composables/类型/工具)
├── package.json
├── vite.config.ts
├── tsconfig.json
└── tsconfig.node.json安装
npm install vue-image-compressor
# 或
pnpm add vue-image-compressor使用方式
1. 组件方式(最简)
<template>
<ImageCompressor
default-encoder="mozJPEG"
:default-options="{ quality: 80 }"
@success="onSuccess"
@error="onError"
/>
</template>
<script setup>
import { ImageCompressor } from 'vue-image-compressor'
function onSuccess(result) {
console.log('压缩后:', result.compressed.size)
console.log('节省:', result.savingsPercent + '%')
}
function onError(err) {
console.error('失败:', err.message)
}
</script>2. Composable 方式(灵活定制)
<script setup>
import { useCompression, useEncoderRegistry } from 'vue-image-compressor'
const { compress, progress, isCompressing, result, cancel } = useCompression()
const { selectedEncoder, encoderOptions, updateOption } = useEncoderRegistry()
// 自定义压缩流程
async function handleCompress(file) {
selectedEncoder.value = 'webP'
updateOption('quality', 85)
const result = await compress(file, {
encoder: 'webP',
encoderOptions: { ...encoderOptions.value },
resize: { enabled: true, width: 800, height: 600, method: 'lanczos3' },
})
// 上传结果
uploadToServer(result.compressed.file)
}
</script>3. 纯工具方式(无 Worker 依赖)
import { blobToImageData, imageDataToBlob, formatBytes } from 'vue-image-compressor'
// 使用浏览器原生能力进行简单压缩
async function simpleCompress(file: File, quality: number): Promise<Blob> {
const imageData = await blobToImageData(file)
return imageDataToBlob(imageData, 'image/jpeg', quality)
}4. 手动压缩函数(compressFile)
如果需要更灵活地控制压缩流程,可以直接使用 compressFile 函数。它支持所有 WASM 编码器,返回完整的压缩结果信息,并且支持通过 AbortSignal 取消任务。
import { compressFile } from 'vue-image-compressor'
async function handleCompress(file: File) {
const result = await compressFile(file, {
encoder: 'mozJPEG', // 可选:编码器类型,默认 'mozJPEG'
encoderOptions: { quality: 75 }, // 可选:编码器参数,不传则使用默认值
})
console.log('压缩前:', result.originalSize)
console.log('压缩后:', result.compressedSize)
console.log('节省:', result.savingsPercent + '%')
console.log('尺寸:', result.width, 'x', result.height)
// 直接上传压缩后的 File 对象
upload(result.file)
}支持取消(AbortController)
const controller = new AbortController()
// 开始压缩
const promise = compressFile(file, {
encoder: 'avif',
encoderOptions: { cqLevel: 20 },
signal: controller.signal, // 传入 AbortSignal
})
// 用户点击取消时
controller.abort()
try {
await promise
} catch (err) {
if (err.name === 'AbortError') {
console.log('压缩已取消')
}
}返回值说明
| 字段 | 类型 | 说明 |
|------|------|------|
| file | File | 压缩后的文件对象,可直接上传或下载 |
| originalSize | number | 原始文件大小(字节) |
| compressedSize | number | 压缩后文件大小(字节) |
| savingsBytes | number | 节省的字节数 |
| savingsPercent | number | 节省百分比 |
| width | number | 图片宽度 |
| height | number | 图片高度 |
| encoderType | string | 实际使用的编码器类型 |
| encoderOptions | object | 实际使用的编码器参数(含默认值) |
编码器支持
| 编码器 | 类型 | 质量参数 | 说明 | |--------|------|---------|------| | mozJPEG | WASM | quality | 优化的 JPEG 编码器,体积更小 | | webP | WASM | quality | Google 的 WebP 格式 | | avif | WASM | cqLevel | 下一代图像格式,体积最小 | | jxl | WASM | quality | JPEG XL 未来格式 | | oxiPNG | WASM | level | 优化的 PNG 编码器 | | browserJPEG | 内置 | quality | 浏览器 Canvas.toBlob() | | browserPNG | 内置 | - | 浏览器原生 PNG | | browserGIF | 内置 | - | 浏览器原生 GIF | | qoi | WASM | - | 快速有损格式 | | wp2 | WASM | quality | WebP2 实验格式 |
核心特性
- Web Worker 异步:压缩任务在 Worker 线程执行,不阻塞 UI
- WASM 高性能:引用 Squoosh 的 C/Rust 编码器,性能远超 JS
- AbortController 取消:支持取消正在进行的压缩任务
- Worker 懒加载+超时回收:首次使用时加载,空闲 10 秒自动回收
- 10 种编码器:覆盖所有主流图片格式
- 处理链式编排:解码 → 旋转 → 缩放 → 量化 → 编码
- 类型安全:完整的 TypeScript 类型定义
依赖说明
- peer:
vue ^3.3.0 - dev:
vite,@vitejs/plugin-vue,vite-plugin-dts,typescript
注意:WASM 编解码器文件需要额外从 Squoosh 项目中复制到 public/codecs/ 目录下,并在 Worker 中加载。
