本文讲解 Webpack 的核心优化策略。
目录
1. Webpack 打包原理
1.1 打包过程
入口文件 → 依赖分析 → 模块打包 → 代码分割 → 输出资源
1.2 核心概念
| 概念 | 说明 |
|---|---|
| Entry | 入口,构建起点 |
| Output | 输出,打包后的位置 |
| Module | 模块,处理的基本单位 |
| Chunk | 代码块,多个模块组合 |
| Bundle | 打包后的最终文件 |
2. 代码分割
2.1 为什么需要代码分割?
所有代码打包成一个文件,用户需要下载所有代码,即使只用其中一小部分。
2.2 配置示例
module.exports = {
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors'
},
common: {
name: 'common',
minChunks: 2
}
}
}
}
}
2.3 动态导入
// 方式一
const loadAbout = () => import('./About.vue')
// 方式二
const loadHome = () => import(/* webpackChunkName: "home" */'./Home.vue')
// Vue Router
const routes = [
{ path: '/', component: () => import('./views/Home.vue') }
]
3. Tree Shaking
3.1 原理
通过静态分析移除未使用的代码。
3.2 前提条件
- 使用 ESM 的 import/export
- package.json 标记无副作用
- production 模式自动开启
3.3 配置副作用
// package.json
{
"sideEffects": false // 整个包无副作用
}
// 或指定某些文件
{
"sideEffects": ["*.css", "*.scss"]
}
4. 懒加载
4.1 作用
只在需要时才加载代码,提升首屏加载速度。
4.2 实现方式
// 箭头函数
const loadComponent = () => import('./Component.vue')
// 预加载
import(/* webpackPrefetch: true */'./NextPage.js')
5. 缓存优化
5.1 构建缓存
module.exports = {
cache: {
type: 'filesystem',
buildDependencies: {
config: [__filename]
}
}
}
5.2 浏览器缓存
module.exports = {
output: {
filename: '[name].[contenthash:8].js'
}
}
| 哈希类型 | 说明 |
|---|---|
[hash] |
项目级别,任何变化都变 |
[chunkhash] |
chunk 级别 |
[contenthash] |
文件内容级别 |
6. 压缩优化
6.1 JavaScript 压缩
const TerserPlugin = require('terser-webpack-plugin')
module.exports = {
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({
terserOptions: {
compress: {
drop_console: true
}
}
})
]
}
}
6.2 Gzip 压缩
const CompressionPlugin = require('compression-webpack-plugin')
module.exports = {
plugins: [
new CompressionPlugin({
algorithm: 'gzip',
test: /\.(js|css|html)$/,
threshold: 10240
})
]
}
7. 构建速度优化
7.1 缩小构建范围
module.exports = {
module: {
rules: [
{
test: /\.js$/,
include: path.resolve(__dirname, 'src'),
exclude: /node_modules/
}
]
}
}
7.2 多进程构建
const HappyPack = require('happypack')
module.exports = {
plugins: [
new HappyPack({
threads: 4
})
]
}
8. 实战配置
const isProd = process.env.NODE_ENV === 'production'
module.exports = {
mode: isProd ? 'production' : 'development',
output: {
filename: isProd ? '[name].[contenthash:8].js' : '[name].js',
publicPath: '/'
},
resolve: {
alias: {
'@': path.resolve(__dirname, 'src')
}
},
optimization: {
minimize: isProd,
splitChunks: {
chunks: 'all',
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors'
}
}
}
},
plugins: isProd ? [
new TerserPlugin({ parallel: true }),
new CompressionPlugin()
] : []
}
总结
| 优化方向 | 策略 | 效果 |
|---|---|---|
| 代码分割 | SplitChunks、动态导入 | 减少首次加载 |
| Tree Shaking | 移除未使用代码 | 减小体积 |
| 懒加载 | 路由级、组件级 | 提升首屏速度 |
| 缓存优化 | 构建缓存、contenthash | 加快二次构建 |
| 压缩优化 | Terser、Gzip | 减小文件大小 |
| 构建速度 | 多进程、缩小范围 | 加快构建 |
先用 BundleAnalyzer 分析,再针对性优化。