概述
Hello,我是Xc,一位因antfu结缘开源的前端菜鸟,今天和大家分享最近开源的一个高效的图片压缩工具fast-imagemin-cli。
什么是图片压缩
顾名思义就是压缩图片大小,有效的图片压缩会优化网页的加载,我们常见的图片压缩处理主要有以下2种:
- 借助tinypng等图片压缩网站或者工具提前压缩好再放入项目中
- 借助脚手架插件在编译时进行压缩输出
为什么做这个工具
源于实际的需求吧,部分类型项目存在较多的图片资源,基于上面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<FmConfig>({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
imagemin 安装问题
感谢大佬指出这个问题,这个问题我在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
Config
fm.config.ts
or fm.config.json
- inclde(required): 需要压缩的图片目录会自动递归遍历.
- options: imagemin 配置项
// 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,},],},},
})
Options
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 |
添加 Script
// package.json
{"scripts": {"fm": "fm" // or "fm --force" 强制压缩,跳过记录// 如果担心会忘记压缩的情况,可以直接把命令配置到编译命令中"build": "fm && vue-tsc --noEmit && vite build" }
}
Demo
xlegex
first
你会发现其中一个 .png
文件压缩耗时 76930
ms, 如果每次都花这么长时间,会大大影响工作效率。
second
第二次就只需要5
ms,节省了77000
ms。
最后
感谢easy-tinypng-cli
和vite-plugin-imagemin
提供的灵感
项目地址
fast-imagemin-cli
使用中有任何问题可以提issue哦~~
如果这个插件能够帮助到你或者让你觉得有用可以给个star和关注~~
最后
最近找到一个VUE的文档,它将VUE的各个知识点进行了总结,整理成了《Vue 开发必须知道的36个技巧》。内容比较详实,对各个知识点的讲解也十分到位。
有需要的小伙伴,可以点击下方卡片领取,无偿分享
最后
以上就是自然红牛为你收集整理的【前端工程化】我开源了个高效的图片压缩工具 - fast-imagemin-cli什么是图片压缩为什么做这个工具如何做这个工具开发应用最后的全部内容,希望文章能够帮你解决【前端工程化】我开源了个高效的图片压缩工具 - fast-imagemin-cli什么是图片压缩为什么做这个工具如何做这个工具开发应用最后所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复