Hello,我是Xc,一位因antfu结缘开源的前端菜鸟,今天和大家分享最近开源的一个高效的图片压缩工具fast-imagemin-cli。
顾名思义就是压缩图片大小,有效的图片压缩会优化网页的加载,我们常见的图片压缩处理主要有以下2种:
源于实际的需求吧,部分类型项目存在较多的图片资源,基于上面2种方案先说下在生产中存在的一些问题吧。
就在思考要如何解决这类问题时候,正好在 antfu 的 开源探店直播项目收集 中看到了 速冻鱼 大佬开源的 easy-tinypng-cli 插件,它可以自动帮忙压缩替换图片资源。
然后又结合了目前项目在用的vite-plugin-imagemin,整理了下这两个不满足我的需求的地方:
vite-plugin-imagemin
时,每次构建都必须重新压缩,我认为这是不必要的。easy-tinypng-cli
时,需要联网,有500个限制,不能满足大量处理。好奇心让我去看了这两个工具的源码,发现easy-tinypng-cli
其中设计的recode.json来作为一个记录表,防止图片进行二次压缩,导致浪费每个月的次数。
vite-plugin-imagemin
是一个基于imagemin
系列插件开发一个组合压缩图片插件。
瞬间有个想法戛然而生,我把他们结合一下是不是就行了?
vite-plugin-imagemin
中的压缩功能解决easy-tinypng-cli
的联网和数量限制问题。easy-tinypng-cli
中的recode设计来解决vite-plugin-imagemin
每次的重复压缩工作问题。是打算将其做成cli工具还是一个编译插件,这里我选择将其作为类似easy-tinypng-cli
的工具,做成插件开发成本较大,要需要处理多种脚手架的问题,虽然说可以用unplugin来开发兼容多种脚手加插件,但是还是觉得cli工具更方便,所以决定将其作为一个cli工具进行开发。
graph TD
A[执行命令] --> B(读取配置)
B --> |未读取到配置文件| C(结束)
B --> |读取到配置文件| D(获取配置目录下的所有图片路径)
D --> E(加载imagemin插件)
E --> F(遍历文件路径)
F --> G(根据文件内容获取hash)
G --> R(比对记录表)
R --> |hash存在| K(跳过该路径)
R --> |hash不存在| L(压缩并替换源文件并写入记录)
L --> 结束
参考了easy-tinypng-cli
中使用的unconfig进行配置文件的加载,可以加载执行命令目录下文件名与files配置匹配的文件,支持多种拓展名,拓展名匹配优先级按照extensions
import { loadConfig } from 'unconfig'
const { config } = await loadConfig({sources: [{files: 'fm.config',extensions: ['ts', 'mts', 'cts', 'js', 'mjs', 'cjs', 'json', ''],},],})
首先需要一个遍历方法
export function walkSync(currentDirPath: string, callback: (filePath: string, dirent: fs.Dirent) => void) {fs.readdirSync(currentDirPath, { withFileTypes: true }).forEach((dirent: fs.Dirent) => {const filePath = path.join(currentDirPath, dirent.name)if (dirent.isFile())callback(filePath, dirent)else if (dirent.isDirectory())walkSync(filePath, callback)})
}
根据配置文件中获取到的目录配置,进行遍历
const { include, options } = configconst includeArr = Array.isArray(include) ? include : [include]const filePaths: string[] = []includeArr.forEach((targetDir) => {const currentDirPath = path.resolve(process.cwd(), targetDir)walkSync(currentDirPath, (filePath: string, dirent: fs.Dirent) => {if (isImageExp(dirent.name)) { // 判断是否是图片文件const hash = getHashOfFile(filePath)// 获取文件hashif (force || !oldTinyMap.has(hash)) // 判断是否已经压缩过filePaths.push(filePath)}})})
const plugins = getImageminPlugins(options) // 复用vite-plugin-imagemin中的方法if (plugins.length === 0) {debug(chalk.redBright('Please configure at least one compression configuration parameter.'))return}const tasks = filePaths.map(filePath => Promise.resolve(processFile(plugins!, filePath, fs.readFileSync(filePath))))const spinner = ora({ text: chalk.bold.yellow('compressing...'), color: 'yellow' }).start() // 利用promise.all去执行压缩操作任务await Promise.all(tasks).then((results) => {spinner.stop()const total = results.lengthconst success = results.filter(result => result).lengthconst fail = total - successdebug(`compress results: total: ${chalk.bold.blue(total)} success: ${chalk.bold.green(success)} fail: ${chalk.bold.red(fail)}`)// eslint-disable-next-line no-consoleconsole.table(Array.from(tinyMap.values()), ['relativePath', 'oldSize', 'size', 'ratio', 'time(ms)'])Array.from(tinyMap.entries()).forEach(([key, value]) => {oldTinyMap.set(key, value)})// 写入日志fs.writeFileSync(RecordFilePath, JSON.stringify(Object.fromEntries(oldTinyMap.entries()), null, '\t'))})
主体功能就开发完成了,有兴趣的可以去fast-imagemin-cli查看源码
# pnpm
pnpm install vite-plugin-imagemin -D
# npm
npm install vite-plugin-imagemin -D
# yarn
yarn add vite-plugin-imagemin -D
感谢大佬指出这个问题,这个问题我在vite-plugin-imagemin的README中有看到相应的解决方案,如下
由于 imagemin 在国内不好安装。现提供几个解决方案
1.使用 yarn 在 package.json 内配置(推荐)
"resolutions": {"bin-wrapper": "npm:bin-wrapper-china"},
2.使用 npm,在电脑 host 文件加上如下配置即可
199.232.4.133 raw.githubusercontent.com
fm.config.ts
or fm.config.json
// fm.config.ts
import { defineFmConfig } from 'fast-imagemin-cli/support'
export default defineFmConfig({include: ['./src/assets'], // string | string[]options: {gifsicle: {optimizationLevel: 7,interlaced: false,},optipng: {optimizationLevel: 7,},mozjpeg: {quality: 20,},pngquant: {quality: [0.8, 0.9],speed: 4,},svgo: {plugins: [{name: 'removeViewBox',},{name: 'removeEmptyAttrs',active: false,},],},},
})
params | type | default | default |
---|---|---|---|
svgo | object or false |
- | See Options |
gifsicle | object or false |
- | See Options |
mozjpeg | object or false |
- | See Options |
optipng | object or false |
- | See Options |
pngquant | object or false |
- | See Options |
webp | object or false |
- | See Options |
// package.json
{"scripts": {"fm": "fm" // or "fm --force" 强制压缩,跳过记录// 如果担心会忘记压缩的情况,可以直接把命令配置到编译命令中"build": "fm && vue-tsc --noEmit && vite build" }
}
xlegex
你会发现其中一个 .png
文件压缩耗时 76930
ms, 如果每次都花这么长时间,会大大影响工作效率。
第二次就只需要5
ms,节省了77000
ms。
easy-tinypng-cli
和vite-plugin-imagemin
提供的灵感fast-imagemin-cli
使用中有任何问题可以提issue哦~~
如果这个插件能够帮助到你或者让你觉得有用可以给个star和关注~~
最近找到一个VUE的文档,它将VUE的各个知识点进行了总结,整理成了《Vue 开发必须知道的36个技巧》。内容比较详实,对各个知识点的讲解也十分到位。
有需要的小伙伴,可以点击下方卡片领取,无偿分享