背景
平时从蓝湖下载下来的图片,个头都非常大,身边同事基本上都会手动压缩一下再用,或者压缩完成后上传到CDN中。前段时间做了一个自动上传CDN的插件,把手动上传这一步给省了,现在想着能不能把手动压缩这一步也省了,图片拿过来直接用,自动压缩自动上传岂不美哉?
何时压缩?
和同事激烈讨论一番后,觉得有三个时间点适合做这项工作:
- 通过VSCODE插件在下载图片后压缩
- 在webpack/vite打包时压缩
- pre commit阶段压缩
第1点有悖我们做这项工作的初衷,自动就是配置好后一点事情都不用做,就算点个按钮那都不能算作自动。
在打包阶段压缩,确实实现了自动化,但是每次打包都会全部压缩一遍,会有效率问题和重复压缩的问题。
最终决定,还是在commit之前进行压缩操作,这样图片提交到代码库时就是压缩过的,之后也不会出现重复压缩的情况。
如何实现?
这里就要用到两款插件husky和lint-staged。做过提交时对代码进行lint的同学对这两款插件一定不会陌生,它们可以在代码提交时对代码进行一些校验,通过才能提交,以此来避免错误代码提交到仓库中。
我们可以借助这两个插件的能力,在pre commit阶段,通过lint-staged获取到暂存区的图片资源路径,然后对图片进行压缩并替换掉源文件,再将替换后的文件添加到暂存区。lint-staged执行完成后会将我们压缩后的图片一并提交到代码库。
至于用什么压缩,那还得是tinypng,它也提供了一款npm包,可以在node中调用它的压缩图片接口。
前期准备
在完成这项工作之前,我们需要一个示例项目,配置好husky和lint-staged。
安装、配置husky
大家准备好一个项目,然后先安装husky:
npm install husky -D
初始化husky:
npm pkg set scripts.prepare="husky install"
npm run prepare
添加pre-commit:
npx husky add .husky/pre-commit "npx lint-staged"
git add .husky/pre-commit
安装lint-staged
npm install --save-dev lint-staged
在package.json中添加一条:
{
"lint-staged": {
"*.{png,jpg,jpeg}": "tiny-files --key pVP3cH1vNnLscYbkC8wNQC7KcBk8MT2b",
}
}
这项配置的意思是,在lint-staged被执行时,会筛选暂存区中的文件,当暂存区存在png、jpg、jpeg文件时,运行tiny-files --key pVP3cH1vNnLscYbkC8wNQC7KcBk8MT2b指令,并将相关文件当做参数传入。
安装tiny-files
npm i tiny-files -D
这个tiny-files是什么?我们接下来说。
关于tiny-files
tiny-files是一个压缩图片工具,它封装了tinypng的api,能够实现批量图片压缩并替换源文件。
GitHub地址:leglegend/tiny-files: 在commit之前压缩图片 (github.com)
key
tinypng的api需要key才能调通,申请key非常简单,只需要在TinyPNG官网填入邮箱即可。该key需作为参数传入tiny-files中,以使其正常工作。
原理
tiny-files的功能实现原理非常简单,先从参数中拆分出key,将key值传入tinify(tinypng的npm包)如果没有key则抛出异常:
const tinify = require('tinify')
// 获取参数
const args = process.argv.slice(2)
const argsKeyIndex = args.findIndex((arg) => ['-k', '--key'].includes(arg))
const argsKeyValue = argsKeyIndex !== -1 ? args[argsKeyIndex + 1] : undefined
if (!argsKeyValue && (!package.tinyFiles || !package.tinyFiles.key))
throw Error('tiny-files缺少key')
tinify.key = argsKeyValue || package.tinyFiles.key
通过参数读取文件路径列表,并筛选出图片文件,没有图片则退出:
// 过滤图片
const files = args.filter((file) => /\.(png|jpg|jpeg)$/.test(file))
if (files.length === 0) {
process.exit(0)
}
将图片压缩,并将压缩完成后的图片加入暂存区:
const fs = require('fs')
const tinify = require('tinify')
const { spawnSync } = require('child_process')
const tasks = []
files.forEach((path) => {
tasks.push(tinify.fromFile(path).toFile(path))
})
Promise.all(tasks).then(() => {
const { status } = spawnSync('git', ['add', ...files])
process.exit(status)
})
至此,我们需要的功能就全部完成了。
体验一下
将上方的配置完全配置后,在项目中添加一个图片,在提交时,图片会自动压缩。
写在最后
上面只是个人对压缩图片如何融入工作流程中的一点见解,如果大家有更好的方案,欢迎指正指出!
如果觉得还实用,麻烦给这个仓库点个星:leglegend/tiny-files: 在commit之前压缩图片 (github.com),谢谢支持!