Search K
Appearance
Appearance
📊 SEO元描述:2024年最新Vue打包优化教程,详解Webpack构建优化、代码分割、资源压缩。包含完整优化案例,适合Vue.js开发者提升项目构建性能和用户体验。
核心关键词:Vue打包优化 2024、Vue构建优化、Webpack优化、Vue代码分割、Vue性能优化、Vue生产构建
长尾关键词:Vue打包怎么优化、Vue构建速度优化、Vue包体积优化、Vue Webpack配置优化、Vue生产环境优化
通过本节Vue打包优化,你将系统性掌握:
打包优化是什么?这是现代前端开发中提升用户体验的关键技术。打包优化是通过配置构建工具来提升构建性能和输出质量的过程,也是Web性能优化的重要组成部分。
💡 学习建议:打包优化是前端性能优化的基础,建议从基础配置开始,逐步深入高级优化技巧
// 🎉 vue.config.js - 构建时间分析
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin')
const smp = new SpeedMeasurePlugin()
module.exports = {
configureWebpack: smp.wrap({
// webpack配置
}),
chainWebpack: config => {
// 添加构建进度显示
config.plugin('progress').use(require('webpack').ProgressPlugin)
}
}// 🎉 包体积分析配置
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
module.exports = {
configureWebpack: config => {
if (process.env.ANALYZE) {
config.plugins.push(new BundleAnalyzerPlugin())
}
}
}
// package.json中添加分析脚本
// "analyze": "cross-env ANALYZE=true npm run build"// 🎉 路由懒加载配置
import { createRouter, createWebHistory } from 'vue-router'
const routes = [
{
path: '/',
name: 'Home',
component: () => import(/* webpackChunkName: "home" */ '@/views/Home.vue')
},
{
path: '/about',
name: 'About',
component: () => import(/* webpackChunkName: "about" */ '@/views/About.vue')
},
{
path: '/user',
name: 'User',
component: () => import(/* webpackChunkName: "user" */ '@/views/User.vue')
}
]
export default createRouter({
history: createWebHistory(),
routes
})<!-- 🎉 组件懒加载 -->
<template>
<div>
<h1>主页面</h1>
<!-- 懒加载重型组件 -->
<Suspense>
<template #default>
<HeavyComponent />
</template>
<template #fallback>
<div>加载中...</div>
</template>
</Suspense>
</div>
</template>
<script>
import { defineAsyncComponent } from 'vue'
export default {
components: {
HeavyComponent: defineAsyncComponent({
loader: () => import('./HeavyComponent.vue'),
loadingComponent: () => import('./LoadingComponent.vue'),
errorComponent: () => import('./ErrorComponent.vue'),
delay: 200,
timeout: 3000
})
}
}
</script>// 🎉 第三方库代码分割
module.exports = {
configureWebpack: {
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
// Vue相关库
vue: {
name: 'chunk-vue',
test: /[\\/]node_modules[\\/](vue|vue-router|vuex)[\\/]/,
priority: 20,
chunks: 'initial'
},
// UI库
elementUI: {
name: 'chunk-element-ui',
test: /[\\/]node_modules[\\/]element-plus[\\/]/,
priority: 15,
chunks: 'initial'
},
// 工具库
libs: {
name: 'chunk-libs',
test: /[\\/]node_modules[\\/](axios|lodash|moment)[\\/]/,
priority: 10,
chunks: 'initial'
},
// 其他第三方库
vendor: {
name: 'chunk-vendors',
test: /[\\/]node_modules[\\/]/,
priority: 5,
chunks: 'initial',
minChunks: 2
}
}
}
}
}
}// 🎉 图片优化配置
module.exports = {
chainWebpack: config => {
// 图片压缩
config.module
.rule('images')
.test(/\.(png|jpe?g|gif|svg)(\?.*)?$/)
.use('image-webpack-loader')
.loader('image-webpack-loader')
.options({
mozjpeg: {
progressive: true,
quality: 80
},
optipng: {
enabled: true
},
pngquant: {
quality: [0.65, 0.8],
speed: 4
},
gifsicle: {
interlaced: false
},
webp: {
quality: 75
}
})
// 小图片转base64
config.module
.rule('images')
.use('url-loader')
.loader('url-loader')
.options({
limit: 8192,
name: 'img/[name].[hash:8].[ext]'
})
}
}// 🎉 CSS优化配置
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
module.exports = {
css: {
extract: process.env.NODE_ENV === 'production' ? {
filename: 'css/[name].[contenthash:8].css',
chunkFilename: 'css/[name].[contenthash:8].css'
} : false
},
configureWebpack: config => {
if (process.env.NODE_ENV === 'production') {
config.optimization.minimizer.push(
new CssMinimizerPlugin({
minimizerOptions: {
preset: [
'default',
{
discardComments: { removeAll: true },
normalizeUnicode: false
}
]
}
})
)
}
}
}// 🎉 JavaScript压缩配置
const TerserPlugin = require('terser-webpack-plugin')
module.exports = {
configureWebpack: config => {
if (process.env.NODE_ENV === 'production') {
config.optimization.minimizer = [
new TerserPlugin({
terserOptions: {
compress: {
drop_console: true,
drop_debugger: true,
pure_funcs: ['console.log']
},
format: {
comments: false
}
},
extractComments: false
})
]
}
}
}// 🎉 文件名哈希配置
module.exports = {
configureWebpack: {
output: {
filename: 'js/[name].[contenthash:8].js',
chunkFilename: 'js/[name].[contenthash:8].js'
}
},
css: {
extract: {
filename: 'css/[name].[contenthash:8].css',
chunkFilename: 'css/[name].[contenthash:8].css'
}
}
}// 🎉 长期缓存优化
module.exports = {
configureWebpack: {
optimization: {
// 提取runtime代码
runtimeChunk: {
name: 'runtime'
},
// 模块ID稳定化
moduleIds: 'deterministic',
chunkIds: 'deterministic',
splitChunks: {
chunks: 'all',
cacheGroups: {
vendor: {
name: 'chunk-vendors',
test: /[\\/]node_modules[\\/]/,
priority: 10,
chunks: 'initial'
},
common: {
name: 'chunk-common',
minChunks: 2,
priority: 5,
chunks: 'initial'
}
}
}
}
}
}// 🎉 构建时Gzip压缩
const CompressionPlugin = require('compression-webpack-plugin')
module.exports = {
configureWebpack: config => {
if (process.env.NODE_ENV === 'production') {
config.plugins.push(
new CompressionPlugin({
algorithm: 'gzip',
test: /\.(js|css|html|svg)$/,
threshold: 8192,
minRatio: 0.8,
deleteOriginalAssets: false
})
)
}
}
}// 🎉 Brotli压缩配置
const BrotliPlugin = require('brotli-webpack-plugin')
module.exports = {
configureWebpack: config => {
if (process.env.NODE_ENV === 'production') {
config.plugins.push(
new BrotliPlugin({
asset: '[path].br[query]',
test: /\.(js|css|html|svg)$/,
threshold: 10240,
minRatio: 0.8
})
)
}
}
}// 🎉 多线程构建配置
const os = require('os')
module.exports = {
chainWebpack: config => {
// 使用thread-loader
config.module
.rule('js')
.use('thread-loader')
.loader('thread-loader')
.options({
workers: os.cpus().length - 1
})
.before('babel-loader')
}
}// 🎉 构建缓存配置
module.exports = {
configureWebpack: {
cache: {
type: 'filesystem',
buildDependencies: {
config: [__filename]
}
}
},
chainWebpack: config => {
// Babel缓存
config.module
.rule('js')
.use('babel-loader')
.loader('babel-loader')
.options({
cacheDirectory: true
})
}
}// 🎉 生产环境完整优化配置
const path = require('path')
const CompressionPlugin = require('compression-webpack-plugin')
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
module.exports = {
// 生产环境关闭source map
productionSourceMap: false,
// 公共路径
publicPath: process.env.NODE_ENV === 'production' ? '/my-app/' : '/',
// 输出目录
outputDir: 'dist',
// 静态资源目录
assetsDir: 'static',
// CSS配置
css: {
extract: {
filename: 'css/[name].[contenthash:8].css',
chunkFilename: 'css/[name].[contenthash:8].css'
},
sourceMap: false
},
configureWebpack: config => {
// 生产环境配置
if (process.env.NODE_ENV === 'production') {
// 文件名配置
config.output.filename = 'js/[name].[contenthash:8].js'
config.output.chunkFilename = 'js/[name].[contenthash:8].js'
// 代码分割
config.optimization.splitChunks = {
chunks: 'all',
cacheGroups: {
vue: {
name: 'chunk-vue',
test: /[\\/]node_modules[\\/](vue|vue-router|vuex)[\\/]/,
priority: 20
},
vendor: {
name: 'chunk-vendors',
test: /[\\/]node_modules[\\/]/,
priority: 10,
chunks: 'initial'
},
common: {
name: 'chunk-common',
minChunks: 2,
priority: 5
}
}
}
// 压缩插件
config.plugins.push(
new CompressionPlugin({
algorithm: 'gzip',
test: /\.(js|css|html|svg)$/,
threshold: 8192,
minRatio: 0.8
})
)
// 分析插件
if (process.env.ANALYZE) {
config.plugins.push(new BundleAnalyzerPlugin())
}
}
},
chainWebpack: config => {
// 图片优化
config.module
.rule('images')
.use('url-loader')
.loader('url-loader')
.options({
limit: 4096,
name: 'img/[name].[hash:8].[ext]'
})
// 预加载优化
config.plugin('preload').tap(options => {
options[0] = {
rel: 'preload',
include: 'initial',
fileBlacklist: [/\.map$/, /hot-update\.js$/]
}
return options
})
// 预取优化
config.plugin('prefetch').tap(options => {
options[0].fileBlacklist = options[0].fileBlacklist || []
options[0].fileBlacklist.push(/runtime\..*\.js$/)
return options
})
}
}// 🎉 构建性能监控脚本
const fs = require('fs')
const path = require('path')
class BuildPerformancePlugin {
constructor(options = {}) {
this.options = options
this.startTime = Date.now()
}
apply(compiler) {
compiler.hooks.compile.tap('BuildPerformancePlugin', () => {
this.startTime = Date.now()
console.log('🚀 开始构建...')
})
compiler.hooks.done.tap('BuildPerformancePlugin', (stats) => {
const duration = Date.now() - this.startTime
const { errors, warnings } = stats.compilation
console.log(`✅ 构建完成,耗时: ${duration}ms`)
console.log(`📊 错误: ${errors.length}, 警告: ${warnings.length}`)
// 保存构建报告
const report = {
timestamp: new Date().toISOString(),
duration,
errors: errors.length,
warnings: warnings.length,
assets: stats.compilation.assets
}
fs.writeFileSync(
path.resolve(__dirname, 'build-report.json'),
JSON.stringify(report, null, 2)
)
})
}
}
module.exports = {
configureWebpack: {
plugins: [
new BuildPerformancePlugin()
]
}
}通过本节Vue打包优化的学习,你已经掌握:
A: 合理配置预加载(preload)和预取(prefetch),优化关键路径资源的加载顺序。
A: 使用代码分割避免单个包过大,同时避免过度分割导致请求数量过多。
A: 使用缓存、多线程构建、减少不必要的插件,优化loader配置。
A: Gzip兼容性好,Brotli压缩率更高,可以同时生成两种格式供服务器选择。
A: 建议关闭source map或使用hidden-source-map,避免暴露源码同时保留调试能力。
# 🎉 打包优化检查清单
✅ 启用代码分割和懒加载
✅ 配置合理的缓存策略
✅ 压缩JavaScript、CSS和图片
✅ 启用Gzip/Brotli压缩
✅ 优化第三方库的引入
✅ 配置CDN资源
✅ 移除未使用的代码
✅ 优化字体和图标加载
✅ 配置合理的预加载策略
✅ 监控构建性能和产物大小// 🎉 性能预算配置
module.exports = {
configureWebpack: {
performance: {
maxAssetSize: 250000,
maxEntrypointSize: 250000,
hints: 'warning'
}
}
}"打包优化是提升用户体验的关键技术,合理的优化策略能够显著改善应用的加载性能。恭喜你完成了第13章Vue CLI与构建工具的学习,你已经掌握了Vue项目工程化的核心技能!"