vue-cli4已经默认帮我们做了很多优化处理,包括静态资源输出、样式处理、代码分割等等。我们需要自己手动配置的事情更少了,而图片压缩处理就是其中的一件。
推荐这个插件,GitHub地址:image-webpack-loader
安装:npm i image-webpack-loader -D
很多人直接这样在vue.config.js
里面加 image-webpack-loader
配置:
chainWebpack: config => {
config.module
.rule("images")
.use("image-webpack-loader")
.loader("image-webpack-loader")
.options({
mozjpeg: { progressive: true, quality: 65 },
optipng: { enabled: true },
pngquant: { quality: [0.65, 0.9], speed: 4 },
gifsicle: { interlaced: false }
// webp: { quality: 75 }
})
}
但image-webpack-loader
的GitHub文档已经明确告诉我们:
In your webpack.config.js
, add the image-loader, chained after the file-loader.
即:添加 image-loader 的时候,必须要链式地跟在 file-loader 后面。其实是先执行image-webpack-loader
,然后再交由file-loader
去处理。这很好理解,当然是先压缩后再复制输出到打包的静态资源目录去啦。
关于 loader 处理顺序可参考 webpack loader 执行顺序
// 官方示例
rules: [{
test: /\.(gif|png|jpe?g|svg)$/i,
use: [
'file-loader',
{
loader: 'image-webpack-loader',
options: {
bypassOnDebug: true, // [email protected]
disable: true, // [email protected] and newer
},
},
]
}]
然鹅,在 vue-cli4 中 file-loader 的部分已经帮我们像这样配置过了:
webpackConfig => {
webpackConfig.module
.rule('images')
.test(/\.(png|jpe?g|gif|webp)(\?.*)?$/)
.use('url-loader')
.loader(require.resolve('url-loader'))
.options({
limit: 4096,
fallback: {
loader: require.resolve('file-loader'),
options: {
name: `static/img[name].[hash:8].[ext]`
}
}
})
那我们可以像如下配置:
image-webpack-loader 要先执行所以要配置在下面,实测按照 webpack-chain 文档配置 enforce 没有什么卵用(我还故意把顺序弄错),结果都会正常压缩图片,感觉还是老老实实根据需要的先后顺序从下到上配置好了
chainWebpack 配置参考 webpack-chain 配置之 ChainedMap
chainWebpack: config => {
if(IS_PROD){
config.module
.rule('images')
.exclude.add(resolve('src/assets/icons')) // 排除icons目录,这些图标已用 svg-sprite-loader 处理,打包成 svg-sprite 了
.end()
.use('url-loader')
.tap(options => ({
limit: 10240, // 稍微改大了点
fallback: {
loader: require.resolve('file-loader'),
options: {
// 在这里修改file-loader的配置
// 直接把outputPath的目录加上,虽然语义没分开清晰但比较简洁
name: 'static/img/[name].[hash:8].[ext]'
// 从生成的资源覆写 filename 或 chunkFilename 时,`assetsDir` 会被忽略。
// 因此别忘了在前面加上静态资源目录,即assetsDir指定的目录,不然会直接在dist文件夹下
// outputPath: 'static/img'
}
}
}))
.end()
.use('image-webpack-loader')
.loader('image-webpack-loader')
.options({
mozjpeg: { progressive: true, quality: 50 }, // 压缩JPEG图像
optipng: { enabled: true }, // 压缩PNG图像
pngquant: { quality: [0.5, 0.65], speed: 4 }, // 压缩PNG图像
gifsicle: { interlaced: false } // 压缩GIF图像
})
.end()
.enforce('post') // 表示先执行配置在下面那个loader,即image-webpack-loader
}
}
图片被 image-webpack-loader 压缩后,小于10kb 的转成base64(by url-loader
),大于这个 size 的则被file-loader
输出到dist/static/img
文件夹了
如果想要图片资源打包输出时保持原有的目录结构,或者根据自定义条件决定不同的输出目录,可以用函数配置 outputPath;如果要让资源引用地址输出为相对路径,可以把 outputPath
的内容拷贝一份放到 publicPath
.options({
name: '[name].[hash:8].[ext]',
// outputPath: 'static/img', // 别忘了加上静态资源目录这个前缀,即assetsDir指定的目录,不然会直接在dist文件夹下
outputPath: function (url, resourcePath, context) {
// 返回从项目根目录到该图片的相对路径
const relativePath = path.relative(context, resourcePath)
const pathArr = relativePath.split('/')
// 如果你的静态资源目录结构较为简单(最多二个层级),图片只放在/src/assets/ 或/src/assets/xxx
// 希望根据assets下的目录结构原样输出,可以这样做
if (pathArr[3] !== undefined) {
return `static/img/${pathArr[2]}/${url}` // url 是上面配置的 name 的值,必须加在路径最后
}
return `static/img/${url}`
// 这些都可依照个人习惯来安排,个人建议没必要太复杂
// if (/denglun-bg\.jpg/.test(resourcePath)) {
// 如果图片以 denglun-bg.jpg 结尾
// return `static/denglun/${url}`
// }
// if (/bg_images\//.test(resourcePath)) {
// 如果图片路径包含 bg_images 目录
// return `static/bg_images/${url}`
// }
// return `static/img/${url}`
},
publicPath: (url, resourcePath, context) => {
// 如果要让资源引用地址输出为相对路径,把 `outputPath` 的内容拷贝一份到这里即可
}
},
关于 file-loader 的详细配置说明以及 vue-cli4 涉及图片处理的源码分析:➡️ file-loader 配置详解以及资源相对路径处理(废话连篇预警)