webpack 整个原理过程

698 阅读9分钟

什么是webpack

简单来说,webpack是一个简单的打包工具。可以将互相依赖的html,css,js,以及图片文字字体文件等资源文件,经过处理打包一个可执行的项目文件

开始第一步

我们开始第一步的话 要去配置初始环境 在需要使用webpack文件夹下, 首先去执行npm initial-y去初始化, 其中-y表示所有选项使用的默认

在执行 npm add webpack webpack-cli -- dev 将webpack安装在开发者模式 这个我们就能获得初始的开发环境

image.png 新建一个“src”文件夹,然后在里面index.js 编写一个简单的hello 同时添加到index 文件夹中

image.png

之后我们就能看到我们的打包文件 dist文件夹 里面的main.js就是打包完成的文件

image.png

接下来我们体验一下webpack整合代码的功能

image.png 这个就代码整合的过程

配置webpack.config.js

webpack.config.js 这个文件就像webpack 的大心脏 接下来我们就开始吧

初等配置

const path = require("path");
module.exports = {
    mode: "development",  // 这个是开发者模式 ,便于我们以后的维护
    entry: "./src/index.js",   // 这个是入口文件  
    output: {
        filename: "dist.js",
        path: path.resolve(__dirname,"dist"),
    }
}
  • mode,选择了开发者模式
  • entry,选择了相对于config文件的src目录下的index.js作为入口文件
  • output, 对于输出配置了输出的名字,并且使用了自带的path配置了输出目录

执行npx webpack,可以看到不仅重新输出了dist.js,其中的内容也和之前的有了不一样

output: {
    filename: "[name].[contenthash].js",
    path: path.resolve(__dirname, "dist"),
  },

这样可以避免打包文件更新之后,浏览缓存发生改变 ,可以给文件添加一段随机的字符,每次更新后都会生成新的随机字符,所有webpack.config.js output中设置

打包css文件和图片

要先安装 npm add --dev style-loader css-loader 配置如下:

 {
                test: /\.css$/i,
                use: ["style-loader", "css-loader"],
            },
            {
                test: /\.(png|svg|jpg|jpeg|gif)$/i,
                type: "asset/resource",
            },

test 这里面的通过正则来表示解析什么样的文件

所以在入口文件index.js中引入style.css和图片assets/images/avater.jpg即可

使用webpack插件自动生成html文件

还是安装

npm add html-webpack-plugin --dev

然后导入webpack.config.js文件

const HtmlWebpackPlugin = require("html-webpack-plugin")

再进行配置即可

  plugins:[
    new HtmlWebpackPlugin()
    title:'文件名'
  ],

所以这时候再执行打包命令,可以看到输出文件夹下还多了一个index.html

这个HTML的标题默认为Webpack App

我们也可以对这个进行配置

兼容低版本浏览器

这个可以借助我们的依赖 babel 而webpack也支持相应的loader

所以首先还是安装

npm add --dev babel-loader @babel/core @babel/preset-env

这三个包提供了我们需要的功能

然后再进行下面的配置 这个也是写在 model 中的

{
  test: /.js$/,
  exclude: /node_modules/,
  use: {
    loader: "babel-loader",
    options: {
      presets: ["@babel/preset-env"],
    },
  },
},

用于将 ES6+ 的代码转换为向后兼容的 JavaScript 代码,以便在现代浏览器中运行。

压缩打包后的js代码

同样是两步,先安装

npm add --dev terser-webpack-plugin

然后引入和配置

const TerserPlugin = require("terser-webpack-plugin")

  optimization: {
    minimize: true,
    minimizer: [new TerserPlugin()],
  },
  • TerserWebpackPlugin
  1. 代码压缩:TerserWebpackPlugin 使用 Terser 压缩器对 JavaScript 代码进行压缩,包括删除空格、注释和不必要的代码,以及压缩变量名等操作,从而减小文件体积。
  2. 混淆:该插件可以将 JavaScript 代码进行混淆,使得代码更难以理解,增加了代码的安全性。
  3. 删除未使用的代码:TerserWebpackPlugin 可以检测并删除未使用的代码,减小最终生成的文件大小,提高性能。
  4. 配置灵活:插件提供了丰富的配置选项,可以根据项目需求进行定制,例如启用/禁用某些压缩选项、设置压缩的级别等。

无需执行命令自动打包

现在每次修改了代码后都得重新执行打包命令

webpack也提供了一个插件能在保存后自动打包

还是先安装

npm add --dev webpack-dev-server

然后先在webpack.config.js中进行配置

  devServer: {
    static: "./dist",
  },

再在package.json中进行配置

  "scripts": {
    "start": "webpack serve --open"
  },

这样在我们执行npm start的时候就会自动执行webpack serve

并且通过 npm start 打开的网页能在我们修改保存后自动刷新页面

配置可视化打包工具

这是一个帮助分析的工具,它会可视化地展现打包过程中哪个文件占的体积比较大

所以同样的,先是安装这个插件

npm add --dev webpack-bundle-analyzer

接着在配置中引入这个插件

const BundleAnalyzerPlugin = require("webpack-bundle-analyzer");

以及在plugins中添加它

  plugins: [
    new HtmlWebpackPlugin({
      title: "test",
    }),
    new BundleAnalyzerPlugin.BundleAnalyzerPlugin()
  ],

所以此时再运行npx webpack

自动跳出了这样的文件分析图

难点 :在webpack打包过程中 文件过大 可以怎么样处理

  1. 压缩代码:使用 TerserWebpackPlugin 或者其他压缩插件来压缩 JavaScript 代码,减小文件体积。
  2. 图片优化:对图片文件进行优化,可以使用像 Image-webpack-loader 这样的 loader 来压缩图片,减小图片文件的大小。
  3. 移除不必要的插件和库:检查项目中是否有不必要的插件和库,尽量只保留需要的部分,减小打包文件的体积。

SplitChunksPlugin

SplitChunksPlugin 是 Webpack 提供的一个用于代码拆分的插件,它可以将公共模块拆分出来,从而减小每个入口文件的体积,提高加载速度。具体来说,SplitChunksPlugin 可以根据配置选项将重复的代码拆分成单独的 chunk,并且在必要时自动创建新的 chunk。

const webpack = require('webpack');

module.exports = {
 // 其他配置项...
 optimization: {
   splitChunks: {
     chunks: 'all',
     minSize: 20000, // 拆分前的最小文件大小,默认为 30kb
     maxSize: 0, // 拆分后的最大文件大小,0 表示不限制
     minChunks: 1, // 被拆分模块至少被引用的次数,默认为 1
     maxAsyncRequests: 30, // 按需加载时的最大并行请求数,默认为 5
     maxInitialRequests: 30, // 入口点的最大并行请求数,默认为 3
     automaticNameDelimiter: '~',
     name: true,
     cacheGroups: {
       vendors: {
         test: /[\\/]node_modules[\\/]/,
         priority: -10
       },
       default: {
         minChunks: 2,
         priority: -20,
         reuseExistingChunk: true
       }
     }
   }
 }
};

在这个配置中,我们通过 optimization.splitChunks 来配置 SplitChunksPlugin。其中,chunks: 'all' 表示拆分所有类型的 chunk,包括入口 chunk 和异步加载的 chunk。其他配置项用于定义拆分的规则和条件,例如最小/最大文件大小、最小引用次数等。

通过合理地配置 SplitChunksPlugin,可以将公共模块拆分出来,减小打包文件的体积,提高加载速度。

/**
 * Webpack 企业级项目配置示例
 * 详细说明每个配置项的作用和用法
 */

const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const TerserPlugin = require('terser-webpack-plugin')
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
const webpack = require('webpack')

// 环境变量
const isProduction = process.env.NODE_ENV === 'production'
const isDevelopment = !isProduction

module.exports = {
  // ==================== 1. 入口 (Entry) ====================
  /**
   * 作用:告诉 webpack 从哪个文件开始打包
   * 说明:webpack 会从这个文件开始,找出所有依赖的模块
   * 
   * 示例:
   * - 单入口:entry: './src/index.js'
   * - 多入口:entry: { main: './src/index.js', admin: './src/admin.js' }
   */
  entry: {
    main: './src/index.jsx',  // 主入口文件
    // vendor: './src/vendor.js'  // 第三方库入口(可选)
  },

  // ==================== 2. 输出 (Output) ====================
  /**
   * 作用:告诉 webpack 打包后的文件输出到哪里,以及如何命名
   * 说明:控制打包后文件的路径、文件名、CDN 路径等
   */
  output: {
    /**
     * path: 输出目录的绝对路径
     * 作用:指定打包后文件存放的文件夹
     * 示例:打包后的文件会放在项目根目录的 build 文件夹中
     */
    path: path.resolve(__dirname, 'build'),

    /**
     * filename: 输出文件的名称
     * 作用:控制打包后的 JS 文件命名规则
     * 
     * 占位符说明:
     * - [name]: 入口名称(如 main)
     * - [hash]: 整个项目的 hash 值(所有文件变化时改变)
     * - [chunkhash]: chunk 的 hash 值(只有该 chunk 变化时改变)
     * - [contenthash]: 内容的 hash 值(文件内容变化时改变)
     * 
     * 示例:
     * - 'bundle.js' → 固定名称
     * - '[name].js' → main.js
     * - '[name].[hash].js' → main.a1b2c3d4.js(用于缓存)
     */
    filename: isProduction
      ? 'static/js/[name].[contenthash:8].js'  // 生产环境:带 hash,便于缓存
      : 'static/js/[name].js',                 // 开发环境:简单名称,便于调试

    /**
     * chunkFilename: 非入口 chunk 文件的名称
     * 作用:控制代码分割后产生的 chunk 文件命名
     * 示例:通过 import() 动态导入的模块会使用这个命名规则
     */
    chunkFilename: isProduction
      ? 'static/js/[name].[contenthash:8].chunk.js'
      : 'static/js/[name].chunk.js',

    /**
     * publicPath: 资源的基础路径
     * 作用:指定打包后资源(JS、CSS、图片等)的公共路径
     * 
     * 示例:
     * - '/' → 相对路径(默认)
     * - '/assets/' → 所有资源都在 /assets/ 目录下
     * - 'https://cdn.example.com/' → CDN 地址
     * 
     * 实际应用:
     * - 开发环境:通常用 '/'
     * - 生产环境:如果用 CDN,设置为 CDN 地址
     */
    publicPath: '/',

    /**
     * assetModuleFilename: 静态资源的输出路径和名称
     * 作用:控制图片、字体等静态资源的输出位置
     */
    assetModuleFilename: 'static/media/[name].[hash:8][ext]',

    /**
     * clean: 是否在打包前清理输出目录
     * 作用:每次打包前自动删除旧的输出文件
     */
    clean: true,
  },

  // ==================== 3. 模式 (Mode) ====================
  /**
   * 作用:设置 webpack 的运行模式
   * 说明:会自动启用相应的优化配置
   * 
   * - 'development': 开发模式,启用开发工具,不压缩代码
   * - 'production': 生产模式,启用代码压缩和优化
   * - 'none': 不启用任何默认优化
   */
  mode: isProduction ? 'production' : 'development',

  // ==================== 4. 开发工具 (Devtool) ====================
  /**
   * 作用:控制如何生成 source map(源码映射)
   * 说明:用于调试,将打包后的代码映射回原始源码
   * 
   * 常用选项:
   * - 'eval': 最快,但不适合生产环境
   * - 'source-map': 最完整,但文件最大
   * - 'cheap-module-source-map': 平衡速度和完整性
   * - 'hidden-source-map': 生成但不引用(用于错误追踪)
   * 
   * 生产环境建议:'source-map' 或 'hidden-source-map'
   * 开发环境建议:'eval-source-map' 或 'cheap-module-eval-source-map'
   */
  devtool: isProduction
    ? 'source-map'  // 生产环境:完整的 source map
    : 'eval-source-map',  // 开发环境:快速构建

  // ==================== 5. 模块解析 (Resolve) ====================
  /**
   * 作用:配置模块如何被解析
   * 说明:控制如何查找和解析模块
   */
  resolve: {
    /**
     * extensions: 自动解析的文件扩展名
     * 作用:导入模块时可以省略扩展名
     * 示例:import Component from './Component' 会自动查找 Component.jsx
     */
    extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'],

    /**
     * alias: 路径别名
     * 作用:简化导入路径,避免使用相对路径
     * 
     * 示例:
     * - import util from '@/utils/util' 代替 import util from '../../../utils/util'
     * - import Component from '@components/Button' 代替长路径
     */
    alias: {
      '@': path.resolve(__dirname, 'src'),
      '@components': path.resolve(__dirname, 'src/components'),
      '@utils': path.resolve(__dirname, 'src/utils'),
      '@services': path.resolve(__dirname, 'src/services'),
      '@assets': path.resolve(__dirname, 'src/assets'),
    },

    /**
     * modules: 告诉 webpack 解析模块时应该搜索的目录
     * 作用:优先从这些目录查找模块
     */
    modules: ['node_modules', path.resolve(__dirname, 'src')],

    /**
     * mainFields: 指定从 package.json 的哪个字段读取入口文件
     * 作用:控制模块的入口文件解析顺序
     */
    mainFields: ['browser', 'module', 'main'],
  },

  // ==================== 6. 模块规则 (Module Rules) ====================
  /**
   * 作用:配置如何处理不同类型的模块
   * 说明:告诉 webpack 遇到不同类型的文件时应该使用什么 loader
   */
  module: {
    rules: [
      // ========== JavaScript/TypeScript 处理 ==========
      {
        /**
         * test: 匹配文件的正则表达式
         * 作用:指定哪些文件需要被这个规则处理
         */
        test: /\.(js|jsx|ts|tsx)$/,

        /**
         * exclude: 排除的目录或文件
         * 作用:告诉 webpack 不要处理这些文件
         * 说明:node_modules 中的文件通常不需要 babel 转译
         */
        exclude: /node_modules/,

        /**
         * use: 使用的 loader
         * 作用:指定如何处理匹配的文件
         * 
         * 说明:
         * - loader 的执行顺序是从下到上(或从右到左)
         * - 先执行 babel-loader,再执行其他处理
         */
        use: [
          {
            loader: 'babel-loader',
            options: {
              /**
               * cacheDirectory: 缓存目录
               * 作用:缓存 babel 编译结果,加快构建速度
               */
              cacheDirectory: true,
              presets: [
                ['@babel/preset-env', {
                  useBuiltIns: 'usage',  // 按需引入 polyfill
                  corejs: 3
                }],
                '@babel/preset-react',   // React JSX 支持
                '@babel/preset-typescript'  // TypeScript 支持
              ],
              plugins: [
                '@babel/plugin-proposal-class-properties',
                '@babel/plugin-syntax-dynamic-import'  // 动态导入支持
              ]
            }
          }
        ]
      },

      // ========== CSS 处理 ==========
      {
        test: /\.css$/,
        use: [
          /**
           * 开发环境:使用 style-loader(将 CSS 注入到 <style> 标签)
           * 生产环境:使用 MiniCssExtractPlugin.loader(提取 CSS 到独立文件)
           */
          isProduction ? MiniCssExtractPlugin.loader : 'style-loader',
          {
            loader: 'css-loader',
            options: {
              /**
               * modules: 是否启用 CSS Modules
               * 作用:将 CSS 类名局部化,避免样式冲突
               */
              modules: {
                localIdentName: '[local]--[hash:base64:5]'  // 类名格式
              },
              importLoaders: 1  // 在 css-loader 之前应用的 loader 数量
            }
          },
          {
            /**
             * postcss-loader: CSS 后处理器
             * 作用:自动添加浏览器前缀、压缩 CSS 等
             */
            loader: 'postcss-loader',
            options: {
              postcssOptions: {
                plugins: [
                  require('autoprefixer'),  // 自动添加浏览器前缀
                  require('cssnano')        // CSS 压缩
                ]
              }
            }
          }
        ]
      },

      // ========== Less/Sass 处理 ==========
      {
        test: /\.less$/,
        use: [
          isProduction ? MiniCssExtractPlugin.loader : 'style-loader',
          'css-loader',
          {
            loader: 'less-loader',
            options: {
              lessOptions: {
                /**
                 * modifyVars: 修改 Less 变量
                 * 作用:覆盖 Less 文件中的变量值
                 * 示例:可以在配置中统一修改主题色
                 */
                modifyVars: {
                  '@primary-color': '#ff7e00',
                  '@text-color': '#333'
                },
                javascriptEnabled: true  // 允许在 Less 中使用 JavaScript
              }
            }
          }
        ]
      },

      // ========== 图片处理 ==========
      {
        test: /\.(png|jpg|jpeg|gif|svg|webp)$/,
        /**
         * type: 'asset'
         * 作用:webpack 5 的新特性,自动处理静态资源
         * 
         * 说明:
         * - 小于 8KB 的图片会转为 base64(内联)
         * - 大于 8KB 的图片会作为文件输出
         */
        type: 'asset',
        parser: {
          dataUrlCondition: {
            maxSize: 8 * 1024  // 8KB
          }
        },
        generator: {
          filename: 'static/images/[name].[hash:8][ext]'
        }
      },

      // ========== 字体处理 ==========
      {
        test: /\.(woff|woff2|eot|ttf|otf)$/,
        type: 'asset/resource',
        generator: {
          filename: 'static/fonts/[name].[hash:8][ext]'
        }
      },

      // ========== 其他文件处理 ==========
      {
        test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)$/,
        type: 'asset/resource',
        generator: {
          filename: 'static/media/[name].[hash:8][ext]'
        }
      }
    ]
  },

  // ==================== 7. 插件 (Plugins) ====================
  /**
   * 作用:扩展 webpack 功能
   * 说明:插件可以执行更广泛的任务,如打包优化、资源管理、环境变量注入等
   */
  plugins: [
    /**
     * HtmlWebpackPlugin: 自动生成 HTML 文件
     * 作用:自动将打包后的 JS/CSS 文件注入到 HTML 中
     */
    new HtmlWebpackPlugin({
      template: './public/index.html',  // HTML 模板文件
      filename: 'index.html',           // 输出文件名
      inject: true,                     // 自动注入 JS/CSS
      minify: isProduction ? {          // 生产环境压缩 HTML
        removeComments: true,
        collapseWhitespace: true,
        removeRedundantAttributes: true
      } : false
    }),

    /**
     * CleanWebpackPlugin: 清理输出目录
     * 作用:每次打包前自动删除旧的输出文件
     */
    new CleanWebpackPlugin(),

    /**
     * DefinePlugin: 定义全局常量
     * 作用:在代码中使用环境变量
     * 
     * 示例:
     * - 代码中:console.log(process.env.NODE_ENV)
     * - 会被替换为:console.log('production')
     */
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
      'process.env.REACT_APP_API_URL': JSON.stringify(process.env.REACT_APP_API_URL)
    }),

    /**
     * MiniCssExtractPlugin: 提取 CSS 到独立文件
     * 作用:将 CSS 从 JS 中分离出来,单独打包
     * 
     * 优势:
     * - CSS 可以并行加载
     * - CSS 可以被浏览器缓存
     * - 减少 JS 文件大小
     */
    ...(isProduction ? [
      new MiniCssExtractPlugin({
        filename: 'static/css/[name].[contenthash:8].css',
        chunkFilename: 'static/css/[name].[contenthash:8].chunk.css'
      })
    ] : []),

    /**
     * IgnorePlugin: 忽略某些模块
     * 作用:排除不需要的模块,减小打包体积
     * 
     * 示例:忽略 moment.js 的 locale 文件(只保留英文)
     */
    new webpack.IgnorePlugin({
      resourceRegExp: /^\.\/locale$/,
      contextRegExp: /moment$/
    })
  ],

  // ==================== 8. 优化 (Optimization) ====================
  /**
   * 作用:配置代码分割和压缩优化
   * 说明:控制如何优化打包后的代码
   */
  optimization: {
    /**
     * minimize: 是否压缩代码
     * 作用:生产环境压缩 JS 和 CSS,减小文件体积
     */
    minimize: isProduction,

    /**
     * minimizer: 压缩器配置
     * 作用:指定使用什么工具压缩代码
     */
    minimizer: [
      /**
       * TerserPlugin: JavaScript 压缩器
       * 作用:压缩和混淆 JavaScript 代码
       */
      new TerserPlugin({
        terserOptions: {
          compress: {
            drop_console: isProduction,  // 生产环境移除 console
            drop_debugger: isProduction  // 生产环境移除 debugger
          }
        },
        extractComments: false  // 不提取注释到单独文件
      }),

      /**
       * CssMinimizerPlugin: CSS 压缩器
       * 作用:压缩 CSS 代码
       */
      new CssMinimizerPlugin()
    ],

    /**
     * splitChunks: 代码分割配置
     * 作用:将代码拆分成多个 chunk,实现按需加载和缓存优化
     */
    splitChunks: {
      chunks: 'all',  // 对所有类型的 chunk 进行分割
      cacheGroups: {
        /**
         * vendor: 第三方库单独打包
         * 作用:将 node_modules 中的代码单独打包
         * 
         * 优势:
         * - 第三方库代码变化频率低,可以长期缓存
         * - 业务代码和第三方代码分离,便于缓存管理
         */
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          priority: 10,  // 优先级
          reuseExistingChunk: true
        },

        /**
         * common: 公共代码单独打包
         * 作用:将多个入口共享的代码提取出来
         */
        common: {
          minChunks: 2,  // 至少被 2 个 chunk 引用
          priority: 5,
          reuseExistingChunk: true
        },

        /**
         * react: React 相关库单独打包
         * 作用:React、ReactDOM 等核心库单独打包
         */
        react: {
          test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/,
          name: 'react',
          priority: 20,
          reuseExistingChunk: true
        }
      }
    },

    /**
     * runtimeChunk: 运行时代码单独打包
     * 作用:将 webpack 的运行时代码单独提取
     * 
     * 优势:
     * - 运行时代码变化频率低,可以长期缓存
     * - 业务代码变化时,运行时代码不需要重新下载
     */
    runtimeChunk: {
      name: 'runtime'
    }
  },

  // ==================== 9. 开发服务器 (DevServer) ====================
  /**
   * 作用:配置开发服务器
   * 说明:只在开发环境使用,提供热更新、代理等功能
   */
  devServer: {
    /**
     * port: 开发服务器端口
     * 作用:指定开发服务器监听的端口号
     */
    port: 3000,

    /**
     * host: 开发服务器主机
     * 作用:指定开发服务器的主机地址
     * 示例:
     * - 'localhost': 只能本机访问
     * - '0.0.0.0': 可以通过 IP 地址访问(局域网)
     */
    host: 'localhost',

    /**
     * open: 是否自动打开浏览器
     * 作用:启动开发服务器后自动打开浏览器
     */
    open: true,

    /**
     * hot: 是否启用热模块替换 (HMR)
     * 作用:修改代码后自动更新,无需刷新页面
     */
    hot: true,

    /**
     * historyApiFallback: SPA 路由支持
     * 作用:所有路由都返回 index.html,支持前端路由
     */
    historyApiFallback: {
      rewrites: [
        { from: /./, to: '/index.html' }
      ]
    },

    /**
     * proxy: API 代理
     * 作用:将 API 请求代理到后端服务器,解决跨域问题
     * 
     * 示例:
     * - 前端请求:/api/user/info
     * - 实际请求:http://localhost:8080/api/user/info
     */
    proxy: {
      '/api': {
        target: 'http://localhost:8080',
        changeOrigin: true,  // 改变请求头中的 origin
        pathRewrite: {
          '^/api': ''  // 重写路径,去掉 /api 前缀
        }
      },
      '/mservice': {
        target: 'http://mobileservice.demo.ehi.com.cn',
        changeOrigin: true,
        pathRewrite: {
          '^/mservice': ''
        }
      }
    },

    /**
     * compress: 是否启用 gzip 压缩
     * 作用:压缩响应内容,加快传输速度
     */
    compress: true,

    /**
     * client: 客户端配置
     * 作用:控制浏览器中的日志和错误提示
     */
    client: {
      overlay: {
        errors: true,   // 显示错误
        warnings: false // 不显示警告
      },
      logging: 'info'  // 日志级别
    }
  },

  // ==================== 10. 性能优化 ====================
  /**
   * 作用:控制 webpack 的性能提示
   * 说明:当打包文件过大时给出警告或错误
   */
  performance: {
    hints: isProduction ? 'warning' : false,  // 生产环境显示警告
    maxEntrypointSize: 512000,  // 入口文件最大 512KB
    maxAssetSize: 512000        // 单个资源最大 512KB
  },

  // ==================== 11. 外部依赖 (Externals) ====================
  /**
   * 作用:指定哪些模块不需要打包
   * 说明:这些模块会在运行时从外部获取(如 CDN)
   * 
   * 使用场景:
   * - 使用 CDN 加载大型库(如 React、jQuery)
   * - 减小打包体积
   * - 利用浏览器缓存
   */
  externals: isProduction ? {
    // 'react': 'React',      // 从 CDN 加载 React
    // 'react-dom': 'ReactDOM' // 从 CDN 加载 ReactDOM
  } : {},

  // ==================== 12. 缓存配置 ====================
  /**
   * 作用:配置 webpack 的缓存策略
   * 说明:缓存编译结果,加快后续构建速度
   */
  cache: {
    type: 'filesystem',  // 使用文件系统缓存
    buildDependencies: {
      config: [__filename]  // 配置文件变化时清除缓存
    }
  }
}

Webpack 完整指南

📚 目录

  1. Webpack 是什么
  2. 为什么需要 Webpack
  3. Webpack 核心概念
  4. 企业级配置详解
  5. 实际应用示例

Webpack 是什么

定义

Webpack 是一个模块打包器(Module Bundler),它的主要功能是:

  1. 打包:将多个文件(模块)打包成一个或多个文件
  2. 转换:将现代 JavaScript、TypeScript、Less/Sass 等转换为浏览器可识别的代码
  3. 优化:压缩代码、代码分割、按需加载等
  4. 开发工具:提供开发服务器、热更新等功能

简单理解

想象一下:

  • 没有 Webpack:你需要手动管理每个 JS 文件的加载顺序,处理浏览器兼容性,手动压缩代码
  • 有 Webpack:你只需要写现代代码,Webpack 自动帮你打包、转换、优化

工作流程

源代码 (src/)
  ↓
Webpack 处理
  ├─ 解析依赖关系
  ├─ 转换代码(Babel、TypeScript、Less)
  ├─ 优化代码(压缩、分割)
  └─ 生成输出文件
  ↓
打包后的文件 (build/)

为什么需要 Webpack

1. 模块化开发

问题:浏览器原生不支持 ES6 模块、CommonJS 等模块系统

解决:Webpack 可以将模块化代码转换为浏览器可执行的代码

// 源代码(ES6 模块)
import React from 'react'
import { Button } from './components/Button'
import './styles.css'

// Webpack 处理后:所有代码打包成一个文件,浏览器可以直接运行

2. 代码转换

问题:现代 JavaScript(ES6+)、TypeScript、Less/Sass 浏览器不支持

解决:Webpack 通过 Loader 转换代码

// 源代码:TypeScript + Less
const Component: React.FC = () => {
  return <div className="container">Hello</div>
}

// Webpack 处理后:转换为 ES5 JavaScript + CSS

3. 性能优化

问题:代码文件太大,加载慢

解决:Webpack 可以:

  • 压缩代码(减小体积)
  • 代码分割(按需加载)
  • Tree Shaking(移除未使用代码)

4. 开发体验

问题:每次修改代码需要手动刷新浏览器

解决:Webpack DevServer 提供:

  • 热更新(HMR):修改代码自动更新
  • 开发服务器:本地开发环境
  • Source Map:方便调试

Webpack 核心概念

1. Entry(入口)

作用:告诉 Webpack 从哪个文件开始打包

// 单入口
entry: './src/index.js'

// 多入口(多页面应用)
entry: {
  main: './src/index.js',
  admin: './src/admin.js'
}

示例

// 项目入口文件:src/index.jsx
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'

ReactDOM.render(<App />, document.getElementById('root'))

// Webpack 会从这个文件开始,找出所有依赖的模块

2. Output(输出)

作用:告诉 Webpack 打包后的文件输出到哪里

output: {
  path: path.resolve(__dirname, 'build'),  // 输出目录
  filename: 'bundle.js'                     // 输出文件名
}

示例

// 打包前
src/
  ├─ index.jsx
  ├─ App.jsx
  └─ components/Button.jsx

// 打包后
build/
  └─ bundle.js  // 所有代码打包成一个文件

3. Loader(加载器)

作用:告诉 Webpack 如何处理不同类型的文件

module: {
  rules: [
    {
      test: /\.jsx?$/,        // 匹配 .js 和 .jsx 文件
      use: 'babel-loader',    // 使用 babel-loader 处理
      exclude: /node_modules/ // 排除 node_modules
    },
    {
      test: /\.css$/,
      use: ['style-loader', 'css-loader']  // 从右到左执行
    }
  ]
}

常见 Loader

Loader作用示例
babel-loader转换 ES6+、JSXimport React from 'react' → ES5
css-loader处理 CSSimport './style.css'
less-loader处理 Lessimport './style.less'
file-loader处理文件import logo from './logo.png'
url-loader处理文件(可转 base64)小图片转 base64

4. Plugin(插件)

作用:扩展 Webpack 功能,执行更广泛的任务

plugins: [
  new HtmlWebpackPlugin(),      // 自动生成 HTML
  new CleanWebpackPlugin(),     // 清理输出目录
  new MiniCssExtractPlugin()    // 提取 CSS
]

常见 Plugin

Plugin作用示例
HtmlWebpackPlugin自动生成 HTML自动注入 JS/CSS 到 HTML
CleanWebpackPlugin清理输出目录每次打包前删除旧文件
MiniCssExtractPlugin提取 CSS将 CSS 从 JS 中分离
DefinePlugin定义全局变量process.env.NODE_ENV
TerserPlugin压缩 JavaScript减小文件体积

5. Mode(模式)

作用:设置 Webpack 的运行模式,自动启用相应优化

mode: 'production'  // 或 'development'

区别

特性developmentproduction
代码压缩
Source Map完整可选
构建速度
文件大小
调试方便困难

企业级配置详解

配置结构概览

module.exports = {
  entry: {},           // 入口
  output: {},          // 输出
  mode: '',            // 模式
  devtool: '',         // Source Map
  resolve: {},         // 模块解析
  module: {},          // 模块规则
  plugins: [],         // 插件
  optimization: {},    // 优化
  devServer: {}        // 开发服务器
}

1. Entry(入口配置)

作用

指定 Webpack 从哪个文件开始打包,找出所有依赖

配置示例
// 单入口(SPA 应用)
entry: './src/index.jsx'

// 多入口(多页面应用)
entry: {
  main: './src/index.jsx',
  admin: './src/admin.jsx',
  mobile: './src/mobile.jsx'
}

// 项目中的实际用法
entry: {
  main: './src/index.jsx'  // React 应用入口
}
实际例子
// src/index.jsx
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import './index.css'

ReactDOM.render(<App />, document.getElementById('root'))

// Webpack 会:
// 1. 从 index.jsx 开始
// 2. 找到 App.jsx
// 3. 找到 App.jsx 中导入的所有模块
// 4. 递归处理所有依赖
// 5. 打包成一个或多个文件

2. Output(输出配置)

作用

控制打包后文件的输出位置、文件名、CDN 路径等

配置详解
output: {
  // 输出目录(绝对路径)
  path: path.resolve(__dirname, 'build'),
  // 输出文件名
  filename: 'static/js/[name].[contenthash:8].js',
  // 公共路径(CDN 地址)
  publicPath: '/',
  // 清理输出目录
  clean: true
}
文件名占位符
占位符说明示例
[name]入口名称main.js
[hash]整个项目的 hashmain.a1b2c3d4.js
[chunkhash]chunk 的 hashmain.b2c3d4e5.js
[contenthash]内容的 hashmain.c3d4e5f6.js
实际例子
// 开发环境
filename: 'static/js/[name].js'
// 输出:static/js/main.js

// 生产环境(带 hash,便于缓存)
filename: 'static/js/[name].[contenthash:8].js'
// 输出:static/js/main.a1b2c3d4.js
// 优势:文件内容变化时 hash 变化,浏览器会重新下载
//      文件内容不变时 hash 不变,浏览器使用缓存

3. Resolve(模块解析)

作用

配置如何查找和解析模块

配置详解
resolve: {
  // 自动解析的扩展名
  extensions: ['.js', '.jsx', '.ts', '.tsx'],
  
  // 路径别名
  alias: {
    '@': path.resolve(__dirname, 'src'),
    '@components': path.resolve(__dirname, 'src/components')
  },
  
  // 模块搜索目录
  modules: ['node_modules', path.resolve(__dirname, 'src')]
}
实际例子
// 没有 alias(使用相对路径)
import Button from '../../../components/Button'
import util from '../../utils/util'

// 有 alias(使用别名)
import Button from '@components/Button'
import util from '@/utils/util'

// Webpack 会自动解析:
// @components → src/components
// @ → src

4. Module Rules(模块规则)

作用

告诉 Webpack 如何处理不同类型的文件

JavaScript/TypeScript 处理
{
  test: /\.(js|jsx|ts|tsx)$/,
  exclude: /node_modules/,
  use: {
    loader: 'babel-loader',
    options: {
      presets: [
        '@babel/preset-env',      // ES6+ → ES5
        '@babel/preset-react',    // JSX → JavaScript
        '@babel/preset-typescript' // TypeScript → JavaScript
      ]
    }
  }
}

实际例子

// 源代码(ES6 + JSX)
const Component = () => {
  const [count, setCount] = useState(0)
  return <button onClick={() => setCount(count + 1)}>{count}</button>
}

// Babel 转换后(ES5)
var Component = function Component() {
  var _useState = useState(0),
      count = _useState[0],
      setCount = _useState[1];
  return React.createElement('button', {
    onClick: function onClick() {
      return setCount(count + 1);
    }
  }, count);
};
CSS 处理
{
  test: /\.css$/,
  use: [
    'style-loader',  // 将 CSS 注入到 <style> 标签
    'css-loader'     // 解析 CSS 文件
  ]
}

实际例子

// 源代码
import './styles.css'

// Webpack 处理后:
// 1. css-loader 解析 CSS 文件
// 2. style-loader 将 CSS 注入到 <style> 标签
// 3. 页面中自动添加 <style>...</style>
Less/Sass 处理
{
  test: /\.less$/,
  use: [
    'style-loader',
    'css-loader',
    {
      loader: 'less-loader',
      options: {
        lessOptions: {
          modifyVars: {
            '@primary-color': '#ff7e00'  // 覆盖 Less 变量
          }
        }
      }
    }
  ]
}

实际例子

// 源代码:styles.less
.container {
  color: @primary-color;  // 使用变量
}

// Less 编译后:styles.css
.container {
  color: #ff7e00;  // 变量被替换
}

// Webpack 处理后:注入到页面
图片处理
{
  test: /\.(png|jpg|jpeg|gif|svg)$/,
  type: 'asset',
  parser: {
    dataUrlCondition: {
      maxSize: 8 * 1024  // 小于 8KB 转 base64
    }
  }
}

实际例子

// 源代码
import logo from './logo.png'  // 10KB 的图片
import icon from './icon.png'  // 5KB 的图片

// Webpack 处理:
// logo.png → 输出为文件:static/images/logo.a1b2c3d4.png
// icon.png → 转为 base64:data:image/png;base64,iVBORw0KG...

5. Plugins(插件)

HtmlWebpackPlugin

作用:自动生成 HTML 文件,并注入打包后的 JS/CSS

new HtmlWebpackPlugin({
  template: './public/index.html',
  filename: 'index.html',
  inject: true  // 自动注入 JS/CSS
})

实际例子

<!-- 模板文件:public/index.html -->
<!DOCTYPE html>
<html>
<head>
  <title>一嗨租车</title>
</head>
<body>
  <div id="root"></div>
</body>
</html>

<!-- Webpack 处理后:build/index.html -->
<!DOCTYPE html>
<html>
<head>
  <title>一嗨租车</title>
  <link href="static/css/main.a1b2c3d4.css" rel="stylesheet">
</head>
<body>
  <div id="root"></div>
  <script src="static/js/main.b2c3d4e5.js"></script>
</body>
</html>
MiniCssExtractPlugin

作用:将 CSS 从 JS 中提取出来,生成独立的 CSS 文件

new MiniCssExtractPlugin({
  filename: 'static/css/[name].[contenthash:8].css'
})

实际例子

// 打包前:CSS 在 JS 中
// bundle.js 包含:
// - JavaScript 代码
// - CSS 代码(通过 style-loader 注入)

// 打包后:CSS 独立文件
// main.js(只包含 JavaScript)
// main.css(独立的 CSS 文件)

// 优势:
// 1. CSS 可以并行加载
// 2. CSS 可以被浏览器缓存
// 3. 减少 JS 文件大小
DefinePlugin

作用:定义全局常量,在代码中使用

new webpack.DefinePlugin({
  'process.env.NODE_ENV': JSON.stringify('production'),
  'process.env.API_URL': JSON.stringify('https://api.example.com')
})

实际例子

// 源代码
if (process.env.NODE_ENV === 'development') {
  console.log('开发环境')
}

// Webpack 替换后
if ('production' === 'development') {
  console.log('开发环境')
}

// 生产环境构建时,这段代码会被移除(Tree Shaking)
IgnorePlugin

作用:忽略某些模块,减小打包体积

new webpack.IgnorePlugin({
  resourceRegExp: /^\.\/locale$/,
  contextRegExp: /moment$/
})

实际例子

// 源代码
import moment from 'moment'
import 'moment/locale/zh-cn'  // 中文语言包

// 使用 IgnorePlugin 后
// moment 的 locale 文件不会被打包
// 打包体积减小:从 200KB → 50KB

// 项目中的实际用法(rsbuild.config.js)
new rspack.IgnorePlugin({
  resourceRegExp: /^\.\/locale$/,
  contextRegExp: /moment$/
})

6. Optimization(优化配置)

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

实际例子

// 源代码
function add(a, b) {
  console.log('计算中...')
  return a + b
}

// 压缩后
function add(a,b){return a+b}
// console.log 被移除,代码被压缩
代码分割(Code Splitting)
optimization: {
  splitChunks: {
    chunks: 'all',
    cacheGroups: {
      vendor: {
        test: /[\\/]node_modules[\\/]/,
        name: 'vendors',
        priority: 10
      }
    }
  }
}

实际例子

// 打包前:所有代码在一个文件
// bundle.js (2MB)
//   - React 代码
//   - 业务代码
//   - 第三方库代码

// 打包后:代码分割
// vendors.js (500KB) - 第三方库(变化少,可长期缓存)
// main.js (1.5MB) - 业务代码(变化频繁)

// 优势:
// 1. 第三方库可以长期缓存
// 2. 业务代码更新时,用户只需重新下载 main.js
// 3. 可以并行加载多个文件

7. DevServer(开发服务器)

作用

提供开发环境的功能:热更新、代理、开发服务器

配置详解
devServer: {
  port: 3000,                    // 端口
  host: 'localhost',             // 主机
  open: true,                    // 自动打开浏览器
  hot: true,                     // 热更新
  historyApiFallback: true,      // SPA 路由支持
  proxy: {                       // API 代理
    '/api': {
      target: 'http://localhost:8080',
      changeOrigin: true
    }
  }
}
实际例子

热更新(HMR)

// 修改代码
const Component = () => {
  return <div>Hello World</div>  // 改为 Hello React
}

// 浏览器自动更新,无需刷新页面
// 页面显示:Hello React

API 代理

// 前端请求
fetch('/api/user/info')

// DevServer 代理后
// 实际请求:http://localhost:8080/api/user/info
// 解决跨域问题

实际应用示例

示例 1:React 项目配置

// webpack.config.js
module.exports = {
  entry: './src/index.jsx',
  output: {
    path: path.resolve(__dirname, 'build'),
    filename: 'static/js/[name].[contenthash:8].js'
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        use: 'babel-loader'
      },
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './public/index.html'
    })
  ]
}

示例 2:项目中的实际配置(rsbuild)

// rsbuild.config.js(基于 webpack/rspack)
export default defineConfig({
  source: {
    index: './src/index.jsx'  // 入口
  },
  output: {
    distPath: {
      root: 'build'  // 输出目录
    }
  },
  plugins: [
    pluginReact(),   // React 支持
    pluginLess({     // Less 支持
      lessOptions: {
        modifyVars: {
          '@brand-primary': '#ff7e00'  // 主题色
        }
      }
    })
  ],
  server: {
    port: 3000,
    proxy: serverProxy  // API 代理
  }
})

总结

Webpack 的核心作用

  1. 打包:将多个文件打包成少数几个文件
  2. 转换:将现代代码转换为浏览器可执行的代码
  3. 优化:压缩、分割、缓存优化
  4. 开发工具:提供开发服务器和热更新

关键配置项

配置项作用示例
entry入口文件'./src/index.jsx'
output输出配置path: 'build'
module.rules文件处理规则Babel、CSS Loader
plugins功能扩展HtmlWebpackPlugin
optimization代码优化压缩、分割
devServer开发服务器热更新、代理

学习建议

  1. 理解核心概念:Entry、Output、Loader、Plugin
  2. 掌握常用配置:开发环境和生产环境的区别
  3. 实践项目:在实际项目中配置和优化
  4. 查看文档:遇到问题查阅官方文档

更多详细信息请查看 webpack.config.example.js 文件中的完整配置示例。

Webpack 工作流程示例

📦 打包前后对比

打包前(源代码)

项目结构:
src/
├── index.jsx          (入口文件)
├── App.jsx
├── components/
│   ├── Button.jsx
│   └── Header.jsx
├── utils/
│   └── util.js
├── styles/
│   ├── index.css
│   └── app.less
└── assets/
    └── logo.png

打包后(输出文件)

build/
├── index.html                    (自动生成)
├── static/
│   ├── js/
│   │   ├── main.a1b2c3d4.js     (业务代码)
│   │   ├── vendors.b2c3d4e5.js  (第三方库)
│   │   └── runtime.c3d4e5f6.js  (运行时代码)
│   ├── css/
│   │   └── main.d4e5f6a7.css    (样式文件)
│   └── images/
│       └── logo.e5f6a7b8.png     (图片)

🔄 Webpack 处理流程

步骤 1:解析入口文件

// 入口:src/index.jsx
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import './styles/index.css'

ReactDOM.render(<App />, document.getElementById('root'))

Webpack 分析

  • 发现依赖:reactreact-dom./App./styles/index.css
  • 继续解析这些依赖

步骤 2:解析依赖模块

// src/App.jsx
import Button from './components/Button'
import Header from './components/Header'
import './styles/app.less'

function App() {
  return (
    <div>
      <Header />
      <Button />
    </div>
  )
}

Webpack 分析

  • 发现依赖:./components/Button./components/Header./styles/app.less
  • 继续解析

步骤 3:构建依赖图

index.jsx
├── react (node_modules)
├── react-dom (node_modules)
├── App.jsx
│   ├── Button.jsx
│   ├── Header.jsx
│   └── app.less
└── index.css

步骤 4:转换代码

JavaScript 转换(Babel)
// 源代码(ES6 + JSX)
const Component = () => {
  const [count, setCount] = useState(0)
  return <button onClick={() => setCount(count + 1)}>{count}</button>
}

// 转换后(ES5)
var Component = function Component() {
  var _useState = useState(0),
      count = _useState[0],
      setCount = _useState[1];
  return React.createElement('button', {
    onClick: function onClick() {
      return setCount(count + 1);
    }
  }, count);
};
CSS 处理
/* 源代码:styles/index.css */
.container {
  padding: 20px;
  background: #f5f5f5;
}

/* Webpack 处理后:注入到页面 */
<style>
.container {
  padding: 20px;
  background: #f5f5f5;
}
</style>
Less 处理
// 源代码:styles/app.less
@primary-color: #ff7e00;

.button {
  background: @primary-color;
  color: white;
}

// Less 编译后
.button {
  background: #ff7e00;
  color: white;
}

// Webpack 处理后:注入到页面或提取为独立文件

步骤 5:代码优化

压缩
// 压缩前
function calculateTotal(items) {
  let total = 0
  for (let i = 0; i < items.length; i++) {
    total += items[i].price
  }
  return total
}

// 压缩后
function calculateTotal(a){let b=0;for(let c=0;c<a.length;c++)b+=a[c].price;return b}
代码分割
// 打包前:所有代码在一个文件(2MB)
bundle.js
  - React (500KB)
  - ReactDOM (300KB)
  - 业务代码 (1.2MB)

// 打包后:代码分割
vendors.js (800KB)  // 第三方库(变化少)
main.js (1.2MB)     // 业务代码(变化频繁)

步骤 6:生成输出文件

<!-- 自动生成的 index.html -->
<!DOCTYPE html>
<html>
<head>
  <title>一嗨租车</title>
  <link href="static/css/main.a1b2c3d4.css" rel="stylesheet">
</head>
<body>
  <div id="root"></div>
  <script src="static/js/runtime.c3d4e5f6.js"></script>
  <script src="static/js/vendors.b2c3d4e5.js"></script>
  <script src="static/js/main.a1b2c3d4.js"></script>
</body>
</html>

🎯 实际配置示例

开发环境配置

module.exports = {
  mode: 'development',
  devtool: 'eval-source-map',  // 快速构建,便于调试
  entry: './src/index.jsx',
  output: {
    filename: 'static/js/[name].js',  // 简单文件名
    path: path.resolve(__dirname, 'build')
  },
  devServer: {
    port: 3000,
    hot: true,  // 热更新
    proxy: {
      '/api': 'http://localhost:8080'
    }
  }
}

特点

  • ✅ 构建速度快
  • ✅ 便于调试(Source Map)
  • ✅ 热更新
  • ❌ 文件体积大
  • ❌ 代码未压缩

生产环境配置

module.exports = {
  mode: 'production',
  devtool: 'source-map',  // 完整 Source Map(用于错误追踪)
  entry: './src/index.jsx',
  output: {
    filename: 'static/js/[name].[contenthash:8].js',  // 带 hash
    path: path.resolve(__dirname, 'build')
  },
  optimization: {
    minimize: true,  // 压缩代码
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors'
        }
      }
    }
  }
}

特点

  • ✅ 文件体积小(压缩)
  • ✅ 代码分割(缓存优化)
  • ✅ 生产环境优化
  • ❌ 构建速度慢
  • ❌ 调试困难

📊 性能对比

打包前

文件数量:100+ 个文件
总大小:未压缩
加载方式:需要手动管理加载顺序
浏览器兼容:需要手动处理

打包后

文件数量:3-5 个文件
总大小:压缩后 500KB - 2MB
加载方式:自动按顺序加载
浏览器兼容:自动处理

实际效果

指标打包前打包后提升
文件数量100+3-5减少 95%
文件大小5MB1MB减少 80%
加载时间2-3秒0.5-1秒提升 70%
浏览器兼容手动处理自动处理100%

🔍 调试技巧

1. 查看打包分析

# 安装分析工具
npm install --save-dev webpack-bundle-analyzer

# 分析打包结果
npx webpack-bundle-analyzer build/static/js/*.js

结果:可视化展示每个模块的大小

2. 查看 Source Map

// 配置 Source Map
devtool: 'source-map'

// 浏览器中:
// 1. 打开开发者工具
// 2. Sources 标签
// 3. 可以看到原始源代码

3. 查看构建信息

# 显示构建详情
npm run build -- --stats verbose

# 输出:
# - 每个模块的大小
# - 构建时间
# - 优化信息

💡 最佳实践

1. 代码分割

// 路由懒加载
const Home = React.lazy(() => import('./pages/Home'))
const About = React.lazy(() => import('./pages/About'))

// Webpack 会自动分割代码
// 访问 /home 时只加载 Home 相关代码

2. 缓存优化

// 使用 contenthash
filename: '[name].[contenthash:8].js'

// 文件内容不变 → hash 不变 → 浏览器使用缓存
// 文件内容变化 → hash 变化 → 浏览器重新下载

3. Tree Shaking

// 只导入需要的部分
import { debounce } from 'lodash'  // ✅ 只打包 debounce
// import _ from 'lodash'           // ❌ 打包整个 lodash

// Webpack 会自动移除未使用的代码

🎓 总结

Webpack 的核心价值:

  1. 模块化:支持现代模块系统
  2. 转换:将现代代码转换为浏览器可执行代码
  3. 优化:压缩、分割、缓存优化
  4. 开发体验:热更新、开发服务器

通过合理配置 Webpack,可以:

  • ✅ 提升开发效率
  • ✅ 优化生产性能
  • ✅ 改善用户体验
  • ✅ 简化项目维护