文章目录
-
- 一、性能优化进阶
-
- [1.1 构建速度优化](#1.1 构建速度优化)
-
- [1.1.1 并行编译与资源缓存](#1.1.1 并行编译与资源缓存)
- [1.1.2 非必要任务延迟处理](#1.1.2 非必要任务延迟处理)
- [1.2 打包体积优化](#1.2 打包体积优化)
-
- [1.2.1 精细化资源压缩](#1.2.1 精细化资源压缩)
- [1.2.2 第三方依赖优化](#1.2.2 第三方依赖优化)
- 二、代码分割高级策略
-
- [2.1 splitChunks 自定义配置](#2.1 splitChunks 自定义配置)
- [2.2 动态导入与预加载](#2.2 动态导入与预加载)
- [三、Module Federation 跨应用共享](#三、Module Federation 跨应用共享)
-
- [3.1 核心概念与价值](#3.1 核心概念与价值)
- [3.2 实战配置(Webpack 5+)](#3.2 实战配置(Webpack 5+))
-
- [3.2.1 远程应用(暴露共享模块)](#3.2.1 远程应用(暴露共享模块))
- [3.2.2 宿主应用(引入远程模块)](#3.2.2 宿主应用(引入远程模块))
- [3.2.3 宿主应用使用远程模块](#3.2.3 宿主应用使用远程模块)
- [四、Tree Shaking 深度实践](#四、Tree Shaking 深度实践)
-
- [4.1 基础配置与生效条件](#4.1 基础配置与生效条件)
- [4.2 常见失效问题与解决](#4.2 常见失效问题与解决)
- 五、缓存策略优化
-
- [5.1 长期缓存配置](#5.1 长期缓存配置)
- [5.2 缓存失效控制](#5.2 缓存失效控制)
- [六、自定义 Loader 与 Plugin](#六、自定义 Loader 与 Plugin)
-
- [6.1 自定义 Loader 开发(代码替换示例)](#6.1 自定义 Loader 开发(代码替换示例))
- [6.2 自定义 Plugin 开发(版权注释示例)](#6.2 自定义 Plugin 开发(版权注释示例))
- 七、多环境配置方案
-
- [7.1 配置文件拆分](#7.1 配置文件拆分)
- [7.2 环境融合与变量注入](#7.2 环境融合与变量注入)
- [7.3 启动脚本配置](#7.3 启动脚本配置)
- 八、避坑与最佳实践
-
- [8.1 常见进阶问题解决](#8.1 常见进阶问题解决)
一、性能优化进阶
1.1 构建速度优化
1.1.1 并行编译与资源缓存
javascript
// webpack.config.js
const path = require('path');
const TerserPlugin = require('terser-webpack-plugin');
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin');
module.exports = {
// 1. 多进程压缩
optimization: {
minimizer: [
new TerserPlugin({
parallel: true, // 启用多进程压缩
threads: 4 // 指定4个线程
})
]
},
// 2. 持久化缓存(Webpack 5+)
cache: {
type: 'filesystem', // 磁盘缓存(默认内存缓存)
cacheDirectory: path.resolve(__dirname, '.webpack_cache'),
buildDependencies: {
config: [__filename] // 配置文件变更时重建缓存
}
},
// 3. 模块缓存加速
plugins: [new HardSourceWebpackPlugin()], // 缓存模块构建结果
module: {
rules: [
{
test: /.js$/,
exclude: /node_modules/,
use: [
'cache-loader', // 缓存loader结果
{
loader: 'babel-loader',
options: {
cacheDirectory: true // babel-loader内置缓存
}
}
]
}
]
}
};
1.1.2 非必要任务延迟处理
javascript
// 仅生产环境启用耗时插件
module.exports = (env) => ({
plugins: [
env.production && new BundleAnalyzerPlugin({
analyzerMode: 'static', // 生成静态报告文件(不启动服务)
openAnalyzer: false // 不自动打开报告
})
].filter(Boolean) // 过滤false值
});
1.2 打包体积优化
1.2.1 精细化资源压缩
javascript
# 安装压缩工具
npm install cssnano postcss-loader imagemin-webpack-plugin --save-dev
javascript
// webpack.config.js
const CssNanoPlugin = require('cssnano-webpack-plugin');
const ImageminPlugin = require('imagemin-webpack-plugin').default;
module.exports = {
module: {
rules: [
{
test: /.css$/,
use: ['style-loader', 'css-loader', 'postcss-loader']
}
]
},
plugins: [
// CSS压缩
new CssNanoPlugin({
preset: ['default', { discardComments: { removeAll: true } }]
}),
// 图片压缩
new ImageminPlugin({
test: /.(png|jpe?g|gif)$/i,
optipng: { optimizationLevel: 5 }, // PNG压缩级别
jpegtran: { progressive: true } // JPG渐进式压缩
})
]
};
1.2.2 第三方依赖优化
JAVASCRIPT
// 1. 排除无需打包的依赖(通过CDN引入)
module.exports = {
externals: {
lodash: '_',
react: 'React',
'react-dom': 'ReactDOM'
}
};
// 2. HTML中引入CDN资源
<!-- public/index.html -->
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/react@18.2.0/umd/react.production.min.js"></script>
二、代码分割高级策略
2.1 splitChunks 自定义配置
javascript
// webpack.config.js
module.exports = {
optimization: {
splitChunks: {
chunks: 'all', // 分割所有类型chunk(初始+异步)
minSize: 20000, // 最小分割体积20KB
minRemainingSize: 0, // 确保分割后剩余体积不为0
minChunks: 1, // 至少被引用1次才分割
maxAsyncRequests: 30, // 异步加载最大请求数
maxInitialRequests: 30, // 初始加载最大请求数
enforceSizeThreshold: 50000, // 强制分割阈值50KB
cacheGroups: {
// 1. 分割第三方依赖
vendors: {
test: /[/]node_modules[/]/,
priority: -10, // 优先级(数值越大越优先)
reuseExistingChunk: true, // 复用已存在的chunk
name: 'vendors' // 输出文件名
},
// 2. 分割公共业务代码
common: {
name: 'common',
minChunks: 2, // 至少被2个模块引用
priority: -20,
reuseExistingChunk: true
},
// 3. 分割CSS资源(需配合mini-css-extract-plugin)
styles: {
name: 'styles',
test: /.css$/,
chunks: 'all',
enforce: true // 强制分割
}
}
}
}
};
2.2 动态导入与预加载
javascript
// 1. 基础动态导入(路由懒加载场景)
const Home = () => import('./Home');
const About = () => import('./About');
// 2. 带预加载的动态导入
const Profile = () => import(/* webpackPrefetch: true */ './Profile');
// 生成<link rel="prefetch" href="profile.js"> 浏览器空闲时加载
// 3. 预加载当前页面所需资源
const Chart = () => import(/* webpackPreload: true */ './Chart');
// 生成<link rel="preload" href="chart.js"> 随当前页面优先加载
三、Module Federation 跨应用共享
3.1 核心概念与价值
Module Federation 实现了跨应用的代码共享,无需 npm 包安装与构建,直接通过 CDN 在运行时共享模块,解决了微前端架构中公共依赖冗余、组件复用困难的问题。
3.2 实战配置(Webpack 5+)
3.2.1 远程应用(暴露共享模块)
javascript
# 安装依赖
npm install webpack@5 webpack-cli html-webpack-plugin --save-dev
javascript
// remote-webpack.config.js
const { ModuleFederationPlugin } = require('webpack').container;
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
publicPath: 'http://localhost:3001/' // 远程应用CDN地址
},
plugins: [
new ModuleFederationPlugin({
name: 'remoteApp', // 远程应用名称
filename: 'remoteEntry.js', // 暴露入口文件
exposes: {
'./Button': './src/Button', // 暴露Button组件
'./utils': './src/utils' // 暴露工具函数
},
shared: {
react: { singleton: true }, // 单例模式共享react
'react-dom': { singleton: true }
}
}),
new HtmlWebpackPlugin({ template: './public/index.html' })
],
devServer: { port: 3001 }
};
3.2.2 宿主应用(引入远程模块)
javascript
// host-webpack.config.js
const { ModuleFederationPlugin } = require('webpack').container;
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
plugins: [
new ModuleFederationPlugin({
name: 'hostApp',
remotes: {
remoteApp: 'remoteApp@http://localhost:3001/remoteEntry.js'
},
shared: {
react: { singleton: true },
'react-dom': { singleton: true }
}
}),
new HtmlWebpackPlugin({ template: './public/index.html' })
],
devServer: { port: 3000 }
};
3.2.3 宿主应用使用远程模块
javascript
// src/App.js
import React from 'react';
// 引入远程组件
const RemoteButton = React.lazy(() => import('remoteApp/Button'));
// 引入远程工具函数
import { formatDate } from 'remoteApp/utils';
function App() {
return (
<div>
<h1>宿主应用</h1>
<React.Suspense fallback="加载中...">
<RemoteButton label="共享按钮" />
</React.Suspense>
<p>{formatDate(new Date())}</p>
</div>
);
}
export default App;
四、Tree Shaking 深度实践
4.1 基础配置与生效条件
javascript
// 1. package.json 标记无副作用文件
{
"sideEffects": [
"*.css", // CSS文件有副作用,不被Tree Shaking删除
"*.less"
]
}
// 2. webpack.config.js 启用优化
module.exports = {
mode: 'production', // 生产模式自动启用Tree Shaking
optimization: {
usedExports: true, // 标记未使用的导出
minimize: true // 配合Terser删除死代码
}
};
4.2 常见失效问题与解决
-
CommonJS 模块无法 Tree Shaking
解决方案:使用 ES Module 语法(import/export),避免 require
javascript
// 错误:CommonJS语法无法Tree Shaking
const { unusedFunc } = require('./utils');
// 正确:ES Module支持Tree Shaking
import { usedFunc } from './utils';
-
副作用代码误删
解决方案:在 sideEffects 中声明有副作用的文件
javascript
"sideEffects": [
"./src/polyfill.js", // 全局polyfill有副作用
"*.css"
]
-
动态导入导致分析失败
解决方案:明确导出意图,避免模糊导入
javascript
// 错误:模糊导入难以分析
import * as utils from './utils';
// 正确:精确导入需要的模块
import { formatDate } from './utils';
五、缓存策略优化
5.1 长期缓存配置
javascript
// webpack.config.js
module.exports = {
output: {
filename: '[name].[contenthash:8].js', // 基于内容哈希命名
chunkFilename: '[name].[contenthash:8].chunk.js',
assetModuleFilename: 'assets/[name].[contenthash:8].[ext]'
},
optimization: {
moduleIds: 'deterministic', // 稳定的模块ID(3-4位数字)
chunkIds: 'deterministic', // 稳定的chunkID
runtimeChunk: 'single' // 提取runtime到单独文件
}
};
5.2 缓存失效控制
javascript
// 1. 排除第三方依赖的哈希变化影响
module.exports = {
optimization: {
splitChunks: {
cacheGroups: {
vendors: {
name: 'vendors',
test: /node_modules/,
chunks: 'all'
}
}
}
}
};
// 2. 手动更新版本触发缓存失效
const packageJson = require('./package.json');
module.exports = {
plugins: [
new webpack.DefinePlugin({
'APP_VERSION': JSON.stringify(packageJson.version)
})
]
};
六、自定义 Loader 与 Plugin
6.1 自定义 Loader 开发(代码替换示例)
javascript
// loaders/replace-loader.js
module.exports = function(source) {
// 替换代码中的特定字符串
const result = source.replace(/{{VERSION}}/g, '1.0.0');
return result;
};
// 配置使用
module.exports = {
module: {
rules: [
{
test: /.js$/,
use: './loaders/replace-loader'
}
]
}
};
6.2 自定义 Plugin 开发(版权注释示例)
javascript
// plugins/copyright-plugin.js
class CopyrightPlugin {
constructor(options = {}) {
this.author = options.author || 'Unknown';
}
apply(compiler) {
// 在生成assets前触发
compiler.hooks.emit.tap('CopyrightPlugin', (compilation) => {
// 遍历所有输出文件
for (const filename in compilation.assets) {
if (filename.endsWith('.js')) {
const content = compilation.assets[filename].source();
// 添加版权注释
const newContent = `/* 版权所有 © ${this.author} */n${content}`;
compilation.assets[filename] = {
source: () => newContent,
size: () => newContent.length
};
}
}
});
}
}
// 配置使用
module.exports = {
plugins: [
new CopyrightPlugin({ author: 'Webpack进阶开发者' })
]
};
七、多环境配置方案
7.1 配置文件拆分
javascript
webpack-config/
├── webpack.common.js # 公共配置
├── webpack.dev.js # 开发环境配置
├── webpack.test.js # 测试环境配置
└── webpack.prod.js # 生产环境配置
7.2 环境融合与变量注入
javascript
# 安装合并工具
npm install webpack-merge --save-dev
javascript
// webpack.common.js 公共配置
module.exports = {
entry: './src/index.js',
plugins: [new HtmlWebpackPlugin({ template: './public/index.html' })]
};
// webpack.prod.js 生产环境配置
const { merge } = require('webpack-merge');
const common = require('./webpack.common');
const TerserPlugin = require('terser-webpack-plugin');
module.exports = merge(common, {
mode: 'production',
optimization: { minimizer: [new TerserPlugin()] },
plugins: [
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify('production'),
'process.env.API_BASE': JSON.stringify('https://api.prod.com')
})
]
});
7.3 启动脚本配置
javascript
// package.json
{
"scripts": {
"start": "webpack serve --config webpack-config/webpack.dev.js",
"build:test": "webpack --config webpack-config/webpack.test.js",
"build:prod": "webpack --config webpack-config/webpack.prod.js"
}
}
八、避坑与最佳实践
8.1 常见进阶问题解决
-
Module Federation 版本冲突
解决方案:共享依赖设置版本范围
javascript
shared: {
react: {
singleton: true,
requiredVersion: '^18.0.0' // 兼容18.x版本
}
}
-
Tree Shaking 删除必要代码
解决方案:使用
/*#__PURE__*/标记纯函数
javascript
// 标记此函数无副作用,仅返回值有用
const utils = /*#__PURE__*/ require('./utils');