Skip to main content

Webpack

Webpack 是一个功能丰富的前端模块打包工具,广泛用于现代 Web 开发。 它专注于将 HTML, JSCSS图片 等资源转换和打包成优化的静态文件,以便在浏览器中高效加载。

主要特性

  1. 模块打包:将应用程序的所有依赖(JSCSS图片)打包成一个或多个 bundle
  2. 加载器(Loaders):通过加载器,Webpack 可以处理非 JavaScript 文件,如 CSSSass图片 等。
  3. 插件系统:通过插件,Webpack 可以扩展其功能,执行各种构建任务。
  4. 代码拆分(Code Splitting):将代码拆分成多个 chunk ,以实现按需加载,提高应用性能。
  5. Tree Shaking:移除未引用的代码,减少 bundle 大小。
  6. 热模块替换(HMR):在开发过程中,无需完全刷新页面就能更新模块。
  7. 性能优化:包括最小化(Minification)、压缩(Compression)和缓存(Caching)。

Webpack5 的新特性和改进

持久化缓存:提高构建速度,特别是在重复构建时。 模块联邦(Module Federation):这是一个新特性,允许不同的 Webpack 构建共享代码,非常适用于微前端架构。 改进的 Tree Shaking :对于 ES 模块,Tree Shaking 更加有效。 改进的资产模块(Asset Modules):简化了资产(如图片和字体)的导入和处理。 移除 Node.js 核心模块的自动填充:在以前的版本中,Webpack 会自动填充 Node.js 核心模块,现在需要手动添加这些填充。 更好的 TypeScript 支持:改进了对 TypeScript 的支持。

基础配置结构

webpack.config.js
const path = require('path');

module.exports = {
mode: 'development', // 可设置为 'production'
entry: './src/index.js', // 入口文件
output: { // 输出配置
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: { // 模块配置
rules: [
// 加载器配置...
]
},
plugins: [ // 插件配置
// 插件实例...
],
optimization: { // 优化配置
// 优化选项...
},
// ...其他配置
};

配置选项详解

mode: 指定构建模式,可以是 development(开发模式)或 production(生产模式)。不同模式会触发不同的内置优化。 entry: 指定 Webpack 开始构建的入口文件。 output: 定义打包后文件的输出方式,包括输出文件名和路径。 module: 配置模块相关的选项,主要是加载器(loaders)。 rules: 定义一系列的加载器,用于转换不同类型的文件。 plugins: 添加并配置插件,以扩展 Webpack 功能。 optimization: 配置优化功能,如代码拆分和压缩。

加载器(Loaders)

加载器用于处理非JavaScript文件(如CSS、Sass、图片和字体文件)。

module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
type: 'asset/resource',
}
// ...更多规则
]
}
}

插件(Plugins)

插件可以用于执行范围更广泛的任务,比如打包优化、资源管理和环境变量注入。

const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
})
// ...更多插件
]
}

优化(Optimization)

Webpack 提供了多种优化选项,包括代码拆分和压缩。

module.exports = {
optimization: {
// 分割代码到不同的bundle
splitChunks: {
chunks: 'all', // 对所有模块进行优化
maxSize: 200000, // 最大尺寸
minSize: 100000, // 最小尺寸
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/, // 将node_modules目录下的模块分配到vendor chunk
name: 'vendors',
chunks: 'all',
priority: -10 // 优先级
},
default: {
minChunks: 2, // 最小共用次数
priority: -20,
reuseExistingChunk: true
}
}
},

// 通过TerserPlugin压缩JavaScript代码
minimize: true,
minimizer: [
new TerserPlugin({
terserOptions: {
compress: {
drop_console: true, // 移除console
},
},
}),
],

// 设置模块的运行时如何解析
runtimeChunk: 'single',

// 更多优化选项...
},
}

解释

  • splitChunks: 用于代码拆分,将代码分割成不同的 chunks
  • chunks: 'all':对所有模块应用优化。
  • maxSize和minSize:控制 chunks 的大小。
  • cacheGroups:定义缓存组,用于控制如何合并模块到共同的 chunks
  • minimize和minimizer:开启代码压缩,并通过 TerserPlugin 进行定制化配置。
  • runtimeChunk: 将 webpack 的运行时代码拆分到一个单独的 chunk 中。

注意

  • 确保安装了 terser-webpack-plugin 如果你在使用默认的压缩插件。
  • 适当地配置 splitChunks 可以显著减少 bundle 的大小,并提高缓存利用率。
  • 通过移除 console 和其他调试代码,可以进一步减小打包后的文件大小。 这个示例仅展示了 optimization 配置中的一部分功能。Webpack 提供了更多的优化选项, 可以根据项目的具体需求进行调整。适当的优化配置可以显著提高应用的加载速度和性能。

开发服务器(DevServer)

Webpack 提供了一个内置的对于开发环境的开发服务器。

module.exports = {
devServer: {
contentBase: './dist',
open: true,
hot: true
// ...更多配置
}
}

其他配置

Webpack 还提供了许多其他配置选项,包括解析策略、性能优化提示等。

个人理解 Webpack

核心组件

  • Compiler:Webpack 的主要引擎,负责管理整个构建流程。
  • Compilation:代表了一次单独的构建过程。Compiler 会为每次构建创建一个新的 Compilation 对象。
  • Module:表示一个模块,可以是任何类型的文件。
  • Chunk:多个模块的集合,是优化和输出过程中的中间表示形式。
  • Asset:构建产生的最终输出文件。
  • Loader:用于将非 JavaScript 文件(如 CSS图片 等)转换成 Webpack 可以处理的模块。
  • Plugin:提供了一个丰富的 API ,允许在 Webpack 构建流程的不同阶段执行自定义任务。

构建流程

启动:通过 CLINode API 启动 Webpack ,加载配置文件。 编译:Compiler 开始编译过程,创建一个新的 Compilation 实例。 模块解析:解析入口文件,构建依赖图。 加载器处理:应用加载器转换模块。 依赖图构建:分析模块间依赖,构建依赖图。 代码生成:根据依赖图生成 Chunk ,然后将 Chunk 转换成最终的 Assets

插件系统

事件钩子:Webpack 提供了丰富的事件钩子供插件使用。 插件实现:插件可以订阅这些事件钩子,并在特定时刻执行操作。

HMR(热模块替换)

HMR逻辑:Webpack 支持热模块替换,允许应用在运行时更新模块而无需完全刷新。

优化机制

Tree Shaking:移除未使用的代码。 代码拆分:根据需要拆分代码,优化加载性能。

配置处理

配置解析:处理和解析webpack.config.js文件中的配置选项。

案例

webpack.config.js
// Generated using webpack-cli https://github.com/webpack/webpack-cli
const { resolve } = require("path")
const merge = require("webpack-merge")
const argv = require("yargs-parser")(process.argv.slice(2))
console.log(argv, '获取当前环境变量的类型 🥃🥃🥃')
const _mode = argv.mode || "development"
const Dotenv = require('dotenv-webpack')

const _mergeConfig = require(`./build/webpack.${_mode === "development" ? "dev" : "prod"}.js`)

const WebpackBar = require("webpackbar")
const NodePolyfillPlugin = require("node-polyfill-webpack-plugin")

const MiniCssExtractPlugin = require("mini-css-extract-plugin")
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin")

const webpackBaseConfig = {
entry: "./src/root.tsx",
output: {
path: resolve(__dirname, "dist"),
},
stats: {
children: true
},
plugins: [
new NodePolyfillPlugin(),
new Dotenv({
path: `.env.${argv.env}`
}),
new WebpackBar(),
new MiniCssExtractPlugin(),
],
module: {
rules: [
// {
// test: /\.(ts|tsx)$/i,
// loader: "ts-loader",
// exclude: ["/node_modules/"],
// },
{
test: /\.(ts|tsx)$/i,
exclude: /(node_modules)/,
use: {
// `.swcrc` can be used to configure swc
loader: "swc-loader"
}
},
{
test: /\.s[ac]ss$/i,
use: [MiniCssExtractPlugin.loader, "css-loader", "postcss-loader", "sass-loader"],
sideEffects: true
},
{
test: /\.css$/i,
use: [MiniCssExtractPlugin.loader, "css-loader", "postcss-loader"],
sideEffects: true
},
// {
// test: /\.(eot|svg|ttf|woff|woff2|png|jpg|gif)$/i,
// type: "asset"
// },
{// 对图片资源文件进行处理,webpack5已经废弃了url-loader,改为type
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
type: 'asset',
exclude: [path.resolve(__dirname, 'src/assets/imgs')],
generator: {
filename: 'imgs/[name].[contenthash][ext]'
}
},
{// 对字体资源文件进行处理,webpack5已经废弃了url-loader,改为type
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
type: 'asset',
generator: {
filename: 'fonts/[name].[contenthash][ext]'
}
},
{// 对音频资源文件进行处理,webpack5已经废弃了url-loader,改为type
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
type: 'asset',
exclude: [path.resolve(__dirname, 'src/assets/medias')],
generator: {
filename: 'medias/[name].[contenthash][ext]'
}
},
]
},
optimization: {
// 多页拆 单页不拆
minimizer: [new CssMinimizerPlugin()],
usedExports: true,
runtimeChunk: {
name: "runtime"
},
splitChunks: {
chunks: "all",
// minChunks: 100,
maxAsyncRequests: 30,
maxInitialRequests: 30,
enforceSizeThreshold: 50000,
cacheGroups: {
commons: {
chunks: 'all',
name: 'chunk-common',
minChunks: 100,
maxInitialRequests: 5,
priority: -20,
enforce: true,
reuseExistingChunk: true
},
commonUtils: {
// chunks: 'async',
chunks: 'all',
name: "chunk-common-utils",
test: /src[\\/]utils/,
minChunks: 2,
priority: 1,
enforce: true,
reuseExistingChunk: true
},
vendors: {
// name: "vendors",
name(module, chunks, cacheGroupKey) {
// console.log("参与");
const moduleFileName = module.identifier().split('/').reduceRight(item => item)
const allChunksName = chunks.map(item => item.name).join('~')
return `ace-${cacheGroupKey}-${allChunksName}-${moduleFileName}`
},
test: /[\\/]node_modules[\\/]/,
chunks: 'initial',
priority: 2,
priority: -10,
enforce: true,
reuseExistingChunk: true
},
// jqueryLibs: {
// name: 'chunk-jquery-libs',
// test: /[\\/]node_modules[\\/](jquery.+\w)/,
// // chunks: 'all',
// priority: 3,
// enforce: true,
// reuseExistingChunk: true
// },
// protocol: {
// chunks: 'all',
// name: 'chunk-ace-protocol',
// test: /[\\/]node_modules[\\/]@ace-protocol*\w/,
// priority: 3,
// enforce: true,
// reuseExistingChunk: true
// },
// muiComponent: {
// name: 'chunk-ace-components',
// test: /([\\/]node_modules[\\/]@mui[\\/].+\w)|(src[\\/]web[\\/]components[\\/]common)|([\\/]node_modules[\\/]@ace-protocol[\\/]components)/,
// chunks: 'all',
// priority: 4,
// enforce: true,
// reuseExistingChunk: true
// },
// ethersSDK: {
// name: 'chunk-ethers-sdk',
// test: /[\\/]node_modules[\\/](ethers*\w|@ethersproject*\w|@ace*\w)/,
// chunks: 'all',
// priority: 5,
// enforce: true,
// reuseExistingChunk: true
// },
// reactLibs: {
// name: 'chunk-react-libs',
// test: /[\\/]node_modules[\\/](react|react.+\w)/,
// chunks: 'all',
// priority: 6,
// enforce: true,
// reuseExistingChunk: true
// },
react: {
name: "react",
test: /[\\/]node_modules[\\/](react.+\w)/,
chunks: "all",
priority: 3,
enforce: true,
reuseExistingChunk: true
},
reactDom: {
name: "react-dom",
test: /[\\/]node_modules[\\/](react-dom.+\w)/,
chunks: "all",
priority: 4,
enforce: true,
reuseExistingChunk: true
},
reactRouterDom: {
name: "react-router-dom",
test: /[\\/]node_modules[\\/](react-router-dom.+\w)/,
chunks: "all",
priority: 5,
enforce: true,
reuseExistingChunk: true
},
dayjs: {
name: "dayjs",
test: /[\\/]node_modules[\\/](dayjs.+\w)/,
chunks: "all",
priority: 6,
enforce: true,
reuseExistingChunk: true
},
antdMobile: {
name: "antd-mobile",
test: /[\\/]node_modules[\\/](antd-mobile.+\w)/,
chunks: "all",
priority: 7,
enforce: true,
reuseExistingChunk: true
},
axios: {
name: "axios",
test: /[\\/]node_modules[\\/](axios.+\w)/,
chunks: "all",
priority: 8,
enforce: true,
reuseExistingChunk: true
},
reactUse: {
name: "react-use",
test: /[\\/]node_modules[\\/](react-use.+\w)/,
chunks: "all",
priority: 9,
enforce: true,
reuseExistingChunk: true
},
classnames: {
name: "classnames",
test: /[\\/]node_modules[\\/](classnames.+\w)/,
chunks: "all",
priority: 10,
enforce: true,
reuseExistingChunk: true
},
antdIcons: {
name: "antd-mobile-icons",
test: /[\\/]node_modules[\\/](antd-mobile-icons.+\w)/,
chunks: "all",
priority: 11,
enforce: true,
reuseExistingChunk: true
},
million: {
name: "million",
test: /[\\/]node_modules[\\/](million.+\w)/,
chunks: "all",
priority: 12,
enforce: true,
reuseExistingChunk: true
},
reactQuery: {
name: "react-query",
test: /[\\/]node_modules[\\/](@tanstack[\\/]react-query.+\w)/,
chunks: "all",
priority: 13,
enforce: true,
reuseExistingChunk: true
},
jotai: {
name: "jotai",
test: /[\\/]node_modules[\\/](jotai.+\w)/,
chunks: "all",
priority: 14,
enforce: true,
reuseExistingChunk: true
},
jotaiXstate: {
name: "jotai-xstate",
test: /[\\/]node_modules[\\/](jotai-xstate.+\w)/,
chunks: "all",
priority: 15,
enforce: true,
reuseExistingChunk: true
},
xstate: {
name: "xstate",
test: /[\\/]node_modules[\\/](xstate.+\w)/,
chunks: "all",
priority: 16,
enforce: true,
reuseExistingChunk: true
},
jotaiZustand: {
name: "jotai-zustand",
test: /[\\/]node_modules[\\/](jotai-zustand.+\w)/,
chunks: "all",
priority: 17,
enforce: true,
reuseExistingChunk: true
},
zustand: {
name: "zustand",
test: /[\\/]node_modules[\\/](zustand.+\w)/,
chunks: "all",
priority: 18,
enforce: true,
reuseExistingChunk: true
},
jotaiImmer: {
name: "jotai-immer",
test: /[\\/]node_modules[\\/](jotai-immer.+\w)/,
chunks: "all",
priority: 19,
enforce: true,
reuseExistingChunk: true
},
immer: {
name: "immer",
test: /[\\/]node_modules[\\/](immer.+\w)/,
chunks: "all",
priority: 20,
enforce: true,
reuseExistingChunk: true
},
libFlexible: {
name: "lib-flexible",
test: /[\\/]node_modules[\\/](lib-flexible.+\w)/,
chunks: "all",
priority: 21,
enforce: true,
reuseExistingChunk: true
},
nprogress: {
name: "nprogress",
test: /[\\/]node_modules[\\/](nprogress.+\w)/,
chunks: "all",
priority: 22,
enforce: true,
reuseExistingChunk: true
},
propTypes: {
name: "prop-types",
test: /[\\/]node_modules[\\/](prop-types.+\w)/,
chunks: "all",
priority: 23,
enforce: true,
reuseExistingChunk: true
},
jotaiTanstackQuery: {
name: "jotai-tanstack-query",
test: /[\\/]node_modules[\\/](jotai-tanstack-query.+\w)/,
chunks: "all",
priority: 24,
enforce: true,
reuseExistingChunk: true
},
reactSpring: {
name: "react-spring",
test: /[\\/]node_modules[\\/](@react-spring.+\w)/,
chunks: "all",
priority: 25,
enforce: true,
reuseExistingChunk: true
},
rcFieldForm: {
name: "rc-field-form",
test: /[\\/]node_modules[\\/](rc-field-form.+\w)/,
chunks: "all",
priority: 26,
enforce: true,
reuseExistingChunk: true
},
useGestureReact: {
name: "use-gesture-react",
test: /[\\/]node_modules[\\/](@use-gesture.+\w)/,
chunks: "all",
priority: 27,
enforce: true,
reuseExistingChunk: true
},
floatingUI: {
name: "floating-ui",
test: /[\\/]node_modules[\\/](@floating-ui.+\w)/,
chunks: "all",
priority: 28,
enforce: true,
reuseExistingChunk: true
},
lodash: {
name: "lodash",
test: /[\\/]node_modules[\\/](lodash.+\w)/,
chunks: "all",
priority: 29,
enforce: true,
reuseExistingChunk: true
},
queryCore: {
name: "react-query",
test: /[\\/]node_modules[\\/](@tanstack[\\/]query-core.+\w)/,
chunks: "all",
priority: 30,
enforce: true,
reuseExistingChunk: true
},
intersectionObserver: {
name: "react-query",
test: /[\\/]node_modules[\\/](intersection-observer.+\w)/,
chunks: "all",
priority: 31,
enforce: true,
reuseExistingChunk: true
},
ahooks: {
name: "ahooks",
test: /[\\/]node_modules[\\/](ahooks.+\w)/,
chunks: "all",
priority: 32,
enforce: true,
reuseExistingChunk: true
},
},
minSize: 20000,
minRemainingSize: 0,
minChunks: 1,
// minSize: {
// javascript: 100000, // 大于10KB才会参与拆包
// style: 0 //webpack4没有
// },
// maxSize: {
// javascript: 120000,
// style: 120000 // webpack4没有
// }
}
},
resolve: {
alias: {
"@": resolve(__dirname, "./src")
},
plugins: [],
extensions: [".tsx", ".ts", ".jsx", ".js", ".json", ".css"],
fallback: {}
}
};

module.exports = merge.default(webpackBaseConfig, _mergeConfig);

build/webpack.dev.js
console.log("🥤🥤🥤🥤🥤 这是开发环境");

const { join, resolve } = require('path');
const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');
const notifier = require('node-notifier');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');

const port = 3000;

module.exports = {
cache: {
type: 'filesystem',
buildDependencies: {
config: [__filename]
}
},
mode: 'development',
devtool: 'eval-cheap-module-source-map',
devServer: {
historyApiFallback: true,
proxy: {
'/api': {
target: 'https://main-dev.pay-git.com',
pathRewrite: { '^/api': '' },
changeOrigin: true
},
},
static: {
// 挂载到服务器中间件的可访问虚拟地址
// 例如设置为 /static,在访问服务器静态文件时,就需要使用 /static 前缀
// 相当于webpack-dev-server@3.X的 contentBasePublicPath 属性
publicPath: './',
// 告诉服务器从哪里提供内容
directory: join(__dirname, '../dist'),
},
host: '0.0.0.0',
hot: true,
port,
},
output: {
publicPath: '/',
filename: 'js/[name].bundle.js',
assetModuleFilename: 'images/[name].[ext]',
},
stats: 'errors-only',
plugins: [
new HtmlWebpackPlugin({
title: '开发替换的标题',
filename: 'index.html',
template: resolve(__dirname, '../index-dev.html'),
}),

new FriendlyErrorsWebpackPlugin({
compilationSuccessInfo: {
messages: ['You application is running here http://localhost:3000'],
notes: ['🐉 构建信息随时关注右上角'],
},
onErrors: function (severity, errors) {
// You can listen to errors transformed and prioritized by the plugin
// severity can be 'error' or 'warning'
if (severity !== 'error') {
return;
}
const error = errors[0];
notifier.notify({
title: '🐢 Webpack 构建失败',
message: severity + ': ' + error.name,
subtitle: error.file || '',
icon: join(__dirname, 'icon.png'),
});
},
// should the console be cleared between each compilation?
// default is true
clearConsole: true,
}),
new webpack.SourceMapDevToolPlugin({ exclude: '/node_modules/*' }),
// 一个实验性的Webpack 插件,用于为 React 组件启用“快速刷新”(也称为热重载)。
new ReactRefreshWebpackPlugin(),
// new BundleAnalyzerPlugin(),
],
};
build/webpack.prod.js
console.log("🍺🍺🍺🍺🍺 这是生产环境");

const os = require('os');
const { join, resolve } = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
// optimize-css-assets-webpack-plugin 过时了 曾经的优化CSS的插件
// css-minimizer-webpack-plugin webpack5
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
const CompressionPlugin = require("compression-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const WorkboxPlugin = require('workbox-webpack-plugin');
const ID = Date.now();
module.exports = {
mode: 'production',
output: {
path: join(__dirname, '../dist'),
publicPath: '/',
filename: 'js/[name].[contenthash:5].bundle.js',
assetModuleFilename: 'images/[name].[hash:5][ext]',
},
devtool: false,
optimization: {
minimize: true,
minimizer: [
// 这里不能换成esbuild 因为会破坏webpack 的 prebuild的压缩
new TerserPlugin({
parallel: os.cpus().length - 1,
}),
new CssMinimizerPlugin({
parallel: os.cpus().length - 1,
}),
],
},
plugins: [
new HtmlWebpackPlugin({
title: '生产替换的标题',
filename: 'index.html',
template: resolve(__dirname, '../index-prod.html'),
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
},
}),

new MiniCssExtractPlugin(),
// gzip 压缩
new CompressionPlugin({
algorithm: "gzip",
}),
new WorkboxPlugin.GenerateSW({
cacheId: `${ID}-gsw`,
exclude: [/\.(?:png|jpg|jpeg|svg)$/, 'service-wroker.js'],
swDest: `../dist/service-worker.js`,
skipWaiting: true,
clientsClaim: true,
runtimeCaching: [
{
// Match any request that ends with .png, .jpg, .jpeg or .svg.
urlPattern: /^https:\/\/cdn.example.com\/platform/, // /\.(?:png|jpg|jpeg|svg)$/,
// Apply a cache-first strategy.
handler: 'CacheFirst',
options: {
// Use a custom cache name.
cacheName: `${ID}-icon-images`,
// Only cache 50 images, and expire them after 30 days
expiration: {
maxEntries: 50
},
// Ensure that only requests that result in a 200 status are cached
cacheableResponse: {
statuses: [0, 200]
}
}
},
// note images & others
{
// Match any request that ends with .png, .jpg, .jpeg or .svg.
urlPattern: /^https:\/\/image.example.com/, // /\.(?:png|jpg|jpeg|svg)$/,
// Apply a cache-first strategy.
handler: 'CacheFirst',
options: {
// Use a custom cache name.
cacheName: `${ID}-note-images`,
// Only cache 50 images, and expire them after 30 days
expiration: {
maxEntries: 50,
maxAgeSeconds: 60 * 60 * 24 * 30 // 30 Days
},
// Ensure that only requests that result in a 200 status are cached
cacheableResponse: {
statuses: [0, 200]
}
}
}
]
}),
// 大小包分析
new BundleAnalyzerPlugin(),
],
};
Webpack 优缺点

优点

  1. 模块化:Webpack 支持多种模块化标准(包括 ES6CommonJSAMD ),方便代码组织和管理。
  2. 代码拆分和懒加载:Webpack 支持代码拆分和懒加载,有助于减少首屏加载时间和优化性能。
  3. 丰富的加载器和插件生态:有大量的加载器和插件可用,可以处理各种文件类型和复杂的任务。
  4. 高度可配置: Webpack 提供了广泛的配置选项,可以满足不同项目的复杂需求。
  5. 社区支持和生态:拥有活跃的社区和丰富的学习资源。
  6. 集成HMR(热模块替换):提高了开发效率,改善了开发体验。
  7. Tree Shaking:有效地去除未使用的代码,减少 bundle 的大小。

缺点

  1. 配置复杂性:Webpack 的配置比较复杂,对于新手来说可能不太友好。
  2. 构建速度:在大型项目中,构建过程可能比较慢,尤其是在没有优化的情况下。
  3. 学习曲线:由于功能强大且配置复杂,Webpack 的学习曲线比较陡峭。
  4. 初始设置成本:对于简单的项目,Webpack 的初始设置可能过于繁琐。
  5. 资源占用:在某些情况下,Webpack 的内存和 CPU 占用可能较高。
  6. 版本升级的挑战:Webpack 的大版本升级有时会引入重大变更,可能需要调整现有配置。

总结

Webpack 是一个非常强大的工具,适合于复杂的现代 Web 应用程序开发。它提供了广泛的功能和高度的可定制性, 但这也意味着更高的学习成本和配置复杂性。对于 小型项目 或 类库开发 可以考虑更简单的工具,如 ParcelRollup 。但对于需要细致优化和复杂构建过程的大型应用, Webpack 仍然是一个非常优秀的选择。