
bj.png

二.png
1. speed-measure-webpack-plugin
使用步骤:
# 安装分析工具
npm install speed-measure-webpack-plugin
// vue.config.js
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin')
module.exports = {
// 添加分析工具
chainWebpack: config => {
config.plugin('speed-measure-webpack-plugin').use(SpeedMeasurePlugin).end()
}
}
2. webpack-bundle-analyzer
使用步骤:
# 安装分析工具
npm install webpack-bundle-analyzer --save-dev
// vue.config.js
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
configureWebpack: {
plugins: [new BundleAnalyzerPlugin()]
}
}

image.png
// vue.config.js
module.exports = {
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 }
})
}
}
WebP 格式
尽量使用 webp 格式(体积比 jpg/png 小很多,且支持透明)。
可以做 渐进式降级,不支持 webp 的浏览器使用原始 png/jpg。
SVG 替代
对于小图标,优先使用 SVG 或者 iconfont,而不是 png,还可以使用base64内联小于2K的小图标。
Vue 项目里可用 vue-svg-loader 直接把 svg 当组件用。
2-2. 打包策略优化
小图片转 Base64 内联
webpack 的 url-loader 可以把小于某个阈值(如 8kb)的图片转为 base64,减少请求数。
过大的图片不要内联,否则反而增大 bundle 体积。
// vue.config.js
module.exports = {
chainWebpack: config => {
config.module
.rule('images')
.use('url-loader')
.tap(options => {
return {
...options,
limit: 8192, // 8kb 以下转为 base64
name: 'img/[name].[hash:7].[ext]'
}
})
}
}
静态资源走 CDN
对于大图片,打包时不放入 bundle,而是上传到 CDN,代码中引用远程 URL。
可以结合 publicPath 配置,自动替换为 CDN 地址。
// vue.config.js
module.exports = {
publicPath: process.env.NODE_ENV === 'production' ? 'https://cdn.xxx.com/' : '/'
}
分包处理(独立存放到 static/ 目录)
在 Vue2(Webpack 构建)项目中,所有放在 src/assets/ 的图片都会被 webpack 当作模块处理,它们会被打包进 JS 或 CSS chunk 中。 如果图片体积大、数量多,会导致:
打包体积增大
构建速度变慢
浏览器首屏下载 JS 时间增加
而放在 static/ 目录下的资源不会被 webpack 打包,而是原封不动地复制到 dist/static/ 里,构建时直接引用即可。
这样做可以:
1.减少 webpack 打包压力
2.提高构建速度
3.避免大图片进入 JS chunk,提升页面加载性能
#懒加载 / 按需加载
使用 v-lazy 或 vue-lazyload 实现图片懒加载,减少首屏资源加载压力。
雪碧图 (Sprite)
对于很多小图标,可以用 spritesmith 合并成雪碧图,减少 http 请求数。
组件化封装图标
经常使用的图片资源(比如状态图标),可以抽成 Vue 组件,改用字体图标或 svg。
2. 组件库按需加载
// babel.config.js
module.exports = {
plugins: [
[
'component',
{
libraryName: 'element-ui',
styleLibraryName: 'theme-chalk'
}
]
]
}

image.png
// vue.config.js
module.exports = {
configureWebpack: {
optimization: {
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/, // 匹配规则:所有node_modules目录下的模块(第三方依赖)
name: 'chunk-vendors', // 输出的chunk文件名(如:chunk-vendors.xxx.js)
chunks: 'all', // 处理所有类型的chunk(入口+异步)
priority: 100, // 优先级:100(最高,会优先于其他缓存组匹配)
minSize: 100 * 1024, // 至少100KB才拆分
reuseExistingChunk: true // 复用已有chunk:若模块已被拆分到其他chunk,不再重复拆分
},
elementUI: {
name: 'chunk-elementUI', // 输出的chunk文件名(如:chunk-elementUI.xxx.js)
priority: 60, // 优先级
test: /[\\/]node_modules[\\/]_?element-ui(.*)/, // 匹配规则:element-ui模块(包括别名如_element-ui)
reuseExistingChunk: true
},
echarts: {
name: 'chunk-echarts', // 输出的chunk文件名
priority: 55, // 优先级
test: /[\\/]node_modules[\\/]_?echarts(.*)/, // 匹配规则:echarts模块(包括别名)
reuseExistingChunk: true
},
common: {
name: 'chunk-common', // 输出的chunk文件名(如:chunk-common.xxx.js)
minChunks: 2, // 最小引用次数:模块需被≥3个chunk引用,才会拆分到这里(比如3个页面共享的组件)
priority: 50, // 优先级
reuseExistingChunk: true // 复用已有chunk:避免重复拆分
}
}
}
}
}
}
3-3. 动态 import 结合组件懒加载: 除了按路由进行懒加载外,还可以在组件内部使用 import() 动态加载其他组件。这样可以把一个大组件拆分成多个小组件,按需加载。
export default {
components: {
// 动态导入另一个组件
MyComponent: () => import(/* webpackChunkName: "my-component" */ './MyComponent.vue')
}
}
4. 依赖清理与替代
4-1. 检查未使用的依赖
使用工具:depcheck
npm install -g depcheck
depcheck
它会输出:
Unused dependencies(项目没使用的)
Missing dependencies(代码中引用但没安装的)
Invalid devDependencies(开发依赖不需要的)
处理方式:
对于确定没用的依赖,删除:
npm uninstall 包名
对于误报(比如某些动态 import 或 require),可以手动忽略或添加注释解释。
4-2. 检查重复依赖版本
有时多个包依赖同一库(例如 lodash 不同版本),会重复打包。
**使用工具
npm dedupe
可以手动在 package.json 中锁定版本,比如:
"resolutions": {
"lodash": "4.17.21"
}
4-3. 替换更轻量依赖
# 移除冗余依赖
npm uninstall video.js jspdf
# 安装轻量替代
npm install pdf-lib day.js --save
5. 构建速度优化
5-1. Webpack 构建层面优化
1. 使用缓存与并行构建
启用持久化缓存
// vue.config.js
const path = require('path');
module.exports = {
configureWebpack: {
cache: {
type: 'filesystem', // 文件系统缓存(Webpack 5 原生支持)
buildDependencies: {
config: [__filename],
},
},
},
};
chainWebpack: config => {
// 确保缓存生效
config.cache(true);
}
};
作用:Webpack 5 内置的文件系统级缓存, 避免每次构建都重新编译未变更的模块。构建速度提升:10% ~ 60%。
开启多进程打包(thread-loader)
// vue.config.js
const threadLoader = require('thread-loader');
threadLoader.warmup({}, ['babel-loader', 'vue-loader']);
module.exports = {
chainWebpack: config => {
config.module
.rule('js')
.use('thread-loader')
.loader('thread-loader')
.before('babel-loader');
},
};
特别适合多核 CPU 环境。对大型项目(上千个文件)效果明显。
5-2、Loader / Plugin 优化
1. 减少 Babel 转译范围
// JavaScript/TypeScript 文件
config.module
.rule('js')
.test(/\.js$/)
.include
.add(path.resolve('src'))
.add(path.resolve('node_modules/element-ui')) // 只处理需要的 node_modules
.end()
.exclude
.add(/node_modules/) // 默认排除所有 node_modules
.end()
// Vue 文件
config.module
.rule('vue')
.test(/\.vue$/)
.use('cache-loader')
.loader('cache-loader')
.options({
cacheDirectory: path.resolve('.cache/vue-loader')
})
避免对 node_modules 进行 Babel 转译(极大耗时)。若有特例库需转译,可单独指定。
2. 使用更快的压缩插件
替换默认的 terser-webpack-plugin 为更快的压缩器,如 esbuild 或 swc。
npm install esbuild-loader -D
// vue.config.js
const { EsbuildPlugin } = require('esbuild-loader');
module.exports = {
configureWebpack: {
optimization: {
minimizer: [
new EsbuildPlugin({
target: 'es2015',
css: true,
}),
],
},
},
};
构建时间可缩短 30%+,压缩性能几乎相同。
3. 缩小解析范围(resolve 优化)
module.exports = {
configureWebpack: {
resolve: {
symlinks: false,
extensions: ['.js', '.vue', '.json'],
alias: {
'@': path.resolve(__dirname, 'src'),
},
},
},
};
减少模块解析深度,加速依赖定位。
6. 生产环境强化
开启GZIP压缩
在打包阶段生成 .gz 文件,通过服务器返回压缩后的静态资源,减少网络传输体积。
// vue.config.js
const CompressionPlugin = require('compression-webpack-plugin')
module.exports = {
configureWebpack: {
plugins: [
new CompressionPlugin({
algorithm: 'gzip',
threshold: 10240 // 超过10KB的文件才压缩
})
]
}
}
资源体积可减少约 60%~80%
提升首屏加载速度、降低带宽占用
建议同时在 Nginx 中启用 gzip_static 支持
去除 console 与 debugger
防止生产环境控制台输出敏感信息,减少无用日志、减小包体积。
// vue.config.js
module.exports = {
configureWebpack: {
terserOptions: {
compress: {
drop_console: true, // 删除 console 语句
drop_debugger: true // 删除 debugger
}
}
}
}
清理开发日志,缩小构建体积
避免泄露接口、环境变量等信息
提升执行性能,控制台更干净
移除源码与 map 文件
防止打包后的 .map 文件暴露源码,提升安全性。
// vue.config.js
module.exports = {
productionSourceMap: false
}
禁止生成 .map 文件,防止源码反编译
减少构建体积与加载请求

总结.png

避坑.png
附录:核心配置代码
vue.config.js
const { defineConfig } = require('@vue/cli-service')
const CompressionPlugin = require('compression-webpack-plugin')
module.exports = defineConfig({
productionSourceMap: false,
transpileDependencies: true,
parallel: require('os').cpus().length > 1,
configureWebpack: {
cache: {
type: 'filesystem',
buildDependencies: {
config: [__filename]
}
},
plugins: [new CompressionPlugin()],
optimization: {
splitChunks: {
chunks: 'all',
minSize: 20000,
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10
}
}
}
}
},
chainWebpack: config => {
// 图片压缩
config.module
.rule('images')
.use('image-webpack-loader')
.loader('image-webpack-loader')
.options({ /* 参数配置 */ })
// 多线程处理
config.module
.rule('js')
.use('thread-loader')
.loader('thread-loader')
.options({ workers: 3 })
}
})
路由懒加载示例
const routes = [
{
path: '/dashboard',
component: () => import(/* webpackChunkName: "dashboard" */ '@/views/Dashboard.vue'),
meta: { preload: true } // 重要路由添加预加载标识
},
{
path: '/report/:id',
component: () => import(/* webpackChunkName: "report" */ '@/views/Report.vue')
}
]