前言
之前工作中遇到过图片需要压缩这个问题,每次都是手动压缩后覆盖原图,觉得挺麻烦的。虽然我是个菜鸡,总觉得不应该这么处理,手动总会有出错的时候,全自动的才好啊。
寻找解决方案
第一时间去搜索目前市面上已有的解决方案,并没有找到直接的解决方案,但也颇有收获,了解到了imagemin这个库,于是开始了更多搜索……具体过程就不说了,直接进入主题吧
开发思路
首先既然是要做vite的插件肯定先去vite官网看文档,看看提供哪些配置或者钩子, vite官网文档。好家伙不愧是我,一进门就看到常威在打来福。文档里赫然写着buildStart钩子,一看名字就知道是在构建开始的时候执行。

搭建开发环境
已经迫不及待了,想赶紧加到代码里试试看。不过呢,还是得有个开发环境。先搞个社区模板吧。
yarn create vite vite-project --template vue
创建好项目之后再在根目录新建一个文件夹vite-plugin,文件夹里面放入index.js,把下面的代码写进去,再来vite.config.ts里面引入调用一下。
const viteImagemin = () => ({
name: 'vite-plugin-imagemin',
buildStart: (options) => {
console.log('--------options')
}
})
export default viteImagemin
控制台打印如下
PS C:\Users\Administrator\Desktop\mono\vite-project> yarn build
yarn run v1.22.19
$ vue-tsc --noEmit && vite build
vite v3.1.6 building for production...
--------options
✓ 17 modules transformed.
dist/index.html 0.44 KiB
dist/assets/index.bd68a93b.css 1.26 KiB / gzip: 0.65 KiB
dist/assets/index.34b8f462.js 52.72 KiB / gzip: 21.29 KiB
Done in 5.60s.
诶嘿嘿,先打印了我们的代码 再执行代码的压缩,这不正是我们想要的吗。很好我们的开局不错,感觉离开发成功就只差一点点了(bushi)。接下来就继续完善吧。
正式开发开发功能
既然是压缩本地图片,我的想法是按照下面几步走,先确定好思路是比较重要的一件事。
- 获取到图片绝对路径
- 根据绝对路径读取这个图片
- 调用imagemin这个库帮助压缩
- 覆盖源文件 既然思路清晰了就开始敲代码。
获取图片绝对路径
这部分就涉及到node的一些知识了,其实我也不是很懂,但只要你愿意去看文档就好了项目中引入path模块,再用resolve方法获取图片的绝对路径。注意,这边不能用__dirname参数,需要用process.cwd(),区别如下。
- process.cwd() 是当前Node.js进程执行时的文件夹地址——工作目录,保证了文件在不同的目录下执行时,路径始终不变
- __dirname 是被执行的js 文件的地址 ——文件所在目录
import path from 'path'
// 获取文件绝对路径
const getAbsPath = (relativePath) => {
const cwd = process.cwd()
const absPath = path.resolve(cwd, relativePath)
return absPath
}
上面的方法是获取单个文件的路径,但是我们肯定是要指定一个文件夹,然后对这个文件夹进行处理。所以还要再写个方法。先写死一个文件夹吧,就是你了,src/assets ! 我们用下面这个字符串表示这个路径下的4大类型图片。
const dir = 'src/assets/**/*.{jpg,png,jpeg,gif}'
再调用globby获取到unixpath
import { globby } from 'globby'
const unixPaths = await globby(dir, { onlyFiles: true })
打印出来如下:
[ 'src/assets/jpeg.jpeg', 'src/assets/jpg.jpg', 'src/assets/png.png' ]
再获取一下绝对路径
const absPaths = unixPaths.map(v => getAbsPath(v))
打印出来如下,也就是每个图片的绝对路径了(平铺出来,没有层级嵌套)。
absPaths:[ 'C:\\Users\\Administrator\\Desktop\\mono\\vite-project\\src\\assets\\jpeg.jpeg', 'C:\\Users\\Administrator\\Desktop\\mono\\vite-project\\src\\assets\\jpg.jpg', 'C:\\Users\\Administrator\\Desktop\\mono\\vite-project\\src\\assets\\png.png']
压缩单个文件,主要是调用imagemin.buffer
const compressSingleFile = async (singleAbsFilePath) => {
return new Promise(async (resolve) => {
let buffer = fs.readFileSync(singleAbsFilePath)
let content = await imagemin.buffer(buffer, {
plugins: [
imageminGifsicle(),
imageminOptpng(),
imageminSvgo(),
imageminMozjpeg(),
imageminPngquant()
]
})
resolve(content)
})
}
因为是多文件,需要批量处理
const compressFiles = async (comressFilePaths) => {
let resolvedFileMap = []
for (let i = 0; i < comressFilePaths.length; i++) {
let filePath = comressFilePaths[i]
let content = await compressSingleFile(filePath)
resolvedFileMap.push({ filePath, content })
}
return resolvedFileMap
}
压缩完成后,覆盖源文件
const writeFiles = async (resolvedFileMap) => {
if (resolvedFileMap.length) {
resolvedFileMap.map(async (item) => {
const { filePath, content } = item
if (content) {
fs.writeFileSync(filePath, content)
}
})
}
}
测试
压缩前

压缩后

nice,程序跑起来了,但是压缩率好像不是很高。没关系,这些都是可以配置的,具体的去看看官网文档就好了。
优化
目前只是实现了最核心的压缩功能,但是呢还有很多需要优化的点。
- 做缓存处理,已经压缩过的图片无需再次压缩
- 文件路径可配置,并不是所有人图片都放在assets文件夹目录下的
- 文件可忽略,有可能会出现某些文件不希望压缩的情况
- 文件类型可忽略,有可能会出现某些文件类型不希望压缩的情况
- 使用ts重构一份
- 设置是否启用此插件的开关
- 控制台打印输出压缩结果,有一些图片可能会压缩不成功,此时用户可以单独特殊处理。(这段代码是在CSDN上看到的,直接CV下来了)
- 开局说的buildStart其实还不是最早的,后续改用为config钩子(这个才是build阶段第一个执行的钩子)。
- 只希望在build时启用,所以添加apply:'build',属性。(vite文档上有写明)
做好这些以后项目基本上算是比较完善了,想看源码的小伙伴可以去gayhub上看看,传送门。
gitee,传送门。
目前项目已发布到npm,可以开箱即用。常见的问题在gayhub上也有说明,可以参照着看看。
结语
第一次写东西发出来,就当是给自己做记录吧