webpack从0到1详解

文章目录

  • 从0开始搭建一个简单webpack用例
  • [Webpack 核心模块](#Webpack 核心模块)
    • Loader
    • Plugin
      • [打包更便捷: HtmlWebpackPlugin & CleanWebpackPlugin](#打包更便捷: HtmlWebpackPlugin & CleanWebpackPlugin)
    • [Entry 与 Output](#Entry 与 Output)
    • Devtools
      • [配置 source-map](#配置 source-map)
    • [WebpackDevServer 配置本地开发](#WebpackDevServer 配置本地开发)
    • [optimization 配置优化](#optimization 配置优化)
      • [minimizer : css代码压缩](#minimizer : css代码压缩)
      • [Tree Shaking:usedExports](#Tree Shaking:usedExports)
      • [代码分割(Code Splitting):splitChunks](#代码分割(Code Splitting):splitChunks)
    • [resolve : 路径解析](#resolve : 路径解析)
      • [extensions 省略扩展名](#extensions 省略扩展名)
      • [alias 简化长路径引用](#alias 简化长路径引用)
  • [Webpack 常用性能优化](#Webpack 常用性能优化)
    • 优化构建速度
      • [babel开启缓存,exclude 限制 Loader 处理范围](#babel开启缓存,exclude 限制 Loader 处理范围)
      • IngorePlugin、DllPlugin、NoParse
      • [Thread-loader (5.x) / HappyPack (4.x) 多进程加速构建](#Thread-loader (5.x) / HappyPack (4.x) 多进程加速构建)
      • [TerserPlugin 多进程压缩](#TerserPlugin 多进程压缩)
      • [DllPulgin 生成动态链接库文件](#DllPulgin 生成动态链接库文件)
    • 优化代码体积
      • [Tree Shaking](#Tree Shaking)
      • [TerserPlugin 代码压缩](#TerserPlugin 代码压缩)
      • css、图片资源优化
      • [SplitChunksPlugin 代码分割](#SplitChunksPlugin 代码分割)

从0开始搭建一个简单webpack用例

构建环境

初始化项目,产生package.json

ts 复制代码
npm init -y  

安装webpack

ts 复制代码
npm install webpack webpack-cli -D

package.json

在跟目录下新建src文件夹,写一个index.js

并建立webpack的配置文件 webpack.config.js,进行基本配置:

ts 复制代码
const path = require('path'); // 引入path模块,用于处理文件和目录的路径

module.exports = {
  mode: 'development', // 设置模式为开发模式
  entry: path.join(__dirname, 'src', 'index.js'), //'./src/index.js', // 入口文件,Webpack将从这里开始构建依赖图
  output: {
    filename: 'bundle.js', // 输出文件的名称
    path: path.join(__dirname, 'dist') // 输出文件的目录
    },
}

本地打包

package.json中增加webpack运行命令build,尝试构建

ts 复制代码
"webpack-build": "webpack --config webpack.config.js"

运行:

ts 复制代码
npm run webpack-build

会发现在跟文件下自动生成dist文件夹,并生成打包后的文件bundle.js

本地启动

1 在src目录下新建一个默认的html模板

2 安装html-webpack-plugin, 用来解析html的插件

3 安装webpack-dev -server, 用来启动本地服务的插件

ts 复制代码
npm install html-webpack-plugin -D
npm install webpack-dev -serve -D

在webpack.config.js新增配置

package.json中增加webpack运行命令dev,尝试本地启动

  • webpack serve 即为本地启动devServer的命令,它编译生成的文件并不会放在dist目录下,而是暂存在电脑缓存中
ts 复制代码
"webpack-dev": "webpack serve --config webpack.config.js"

尝试运行: npm run webpack-dev

浏览器打开http://localhost:9000 会发现正常启动

配置babel

babel使用

安装babel babel的作用是将js文件在打包后编译为es5语法,适配各种环境的浏览器

  • core 是babel的核心模块
  • preset-env 可以被看作是一组 Babel 常用插件的集合
ts 复制代码
npm install @babel/core @babel/preset-env babel-loader

跟目录下建立.babelrc配置文件

ts 复制代码
{
 "presets": ["@babel/preset-env"]
 }

webpack.config.js新增配置

  • babel-loader是webpack和babel之间做了打通
  • @babel/preset-env包含了es6转换es5的规则

babel-polyfill

  • babel-polyfill 是一个用于模拟现代 JavaScript API 的工具库,旨在解决 Babel 仅转换语法(如箭头函数、类)而不转换新 API(如 Promise、Array.from)的问题 。它通过注入全局作用域或内置对象原型上的方法,为旧浏览器提供完整的 ES2015+ 环境支持。

安装完整依赖包

ts 复制代码
npm install --save-dev @babel/plugin-transform-runtime @babel/runtime-corejs3
npm install --save core-js@3  # 需单独安装 core-js@3

引入polyfill,会自动提供完整的ES6 API

ts 复制代码
	import '@babel/polyfill'

	const ES6LIST = ['let', 'const', 'class', 'extends', 'super', 'promise', 'async', 'await']
	console.log(ES6LIST.includes('let')); // 测试babel-polyfill是否生效

🌟 通过配置采用按需引入,会减小包的体积

ts 复制代码
// .babelrc 配置文件
{
  "presets": [
    [
      "@babel/preset-env",
      {
        "useBuiltIns": "usage",
        "corejs": 3
      }
    ]
  ],
}

babel-runtime

适用场景:

  • 开发工具库或组件库,避免全局 polyfill 污染宿主环境。
ts 复制代码
// .babelrc 配置文件
{
	// ...
  "plugins": [
    [
      "@babel/plugin-transform-runtime",
      {
        "corejs": 3,
        "helpers": true,
        "regenerator": true
      }
    ]
  ]
}
名称 特点 适用场景
babel-polyfill 体积较大,会引起全局污染 完整的独立应用
babel-runtime 体积小 独立组件库、包等

development 和 production 模式

拆分配置和merge

在webpack.config的基础上,将mode修改为production, 并为output中的fileName增加 [contenthash] 后缀,建立新的webpack.config.prod.js:

  • 在production模式下,生成的打包文件会启动压缩,体积减小很多
  • [contenthash]的作用是当代码发生改变,打包后会生成全新的hash随机码

在package.json中新增命令并打包生产

ts 复制代码
"webpack-prod-build": "webpack --config webpack.config.prod.js",

成功运行:

安装 webpack-merge

ts 复制代码
npm install webpack-merge -D

将公共部分的配置在webpack.common.js中导出

dev和prod中使用 webpack-merge 合并公共配置并导出

通过环境变量拆分配置

修改common.js 导出一个函数,根据环境判断最终导出

ts 复制代码
    "prod-build":  "webpack --env=production --config webpack.common.js", // 生产环境打包
    "dev-build": "webpack --config webpack.common.js", // 开发环境打包

多入口打包

  • 在entry中配置多个入口,output中需要注意不能写死变量
ts 复制代码
  entry: {
    home: path.join(__dirname, "src", "index.js"), //'./src/index.js', 
    other:  path.join(__dirname, "src", "other.js"), //'./src/other.js', 
  },
  output: {
    filename: "[name].[contenthash].js", // 输出文件的名称
    path: path.join(__dirname, "dist"), // 输出文件的目录
  },
  • plugins 中为每个入口单独配置 HtmlWebpackPlugin, 注意 splitChunks 中配置的chunk 加入对应参数
ts 复制代码
plugins: [
    new HtmlWebpackPlugin({
      template: path.join(__dirname, "src", "index.html"), // 指定模板文件
      filename: "index.html", // 输出的HTML文件名称
      chunks: ["home", "vendors", "common"], // 关联的chunk
    }),
    new HtmlWebpackPlugin({
      template: path.join(__dirname, "src", "other.html"), // 指定模板文件
      filename: "other.html", // 输出的HTML文件名称
      chunks: ["other"], // 关联的chunk
    }),
    new CleanWebpackPlugin() // 默认清理 output.path 目录
  ],

Webpack 核心模块

Loader

Loader 是用于对模块的源代码进行转换的插件,允许你预处理文件(如将非 JavaScript 文件转为 Webpack 能处理的模块)。

loader 更详细的知识访问 webpack官网-loader

作用:

  • 转换文件类型(如 .scss → .css,.ts → .js)
  • 加载资源(如图片、字体)
  • 代码优化(如压缩、混淆)

图片资源:file-loader / url-loader

在module中使用 file-loader 或者 url-loader 可以使图片资源正常加载

  • url-loader 使用与 file-loader 类似,不过在options中增加了一个limit配置,小于该值(kb)的图片资源在经过处理后会转为base64编码置于js文件中而不是变为图片资源生成在对应目录下

样式文件:style-loader / css-loader / postcss-loader ...

  • style-loader:使css样式挂在在对应的元素上
  • css-loader:使webpack识别.css的文件
  • postcss-loader: 使css兼容不同的浏览器
  • 其它loader,按需引入,如less-loader、saas-loader等 🌟 loader的引入顺序是从后至前

配置postcss.config.js

autoprefixer会为样式自动增加兼容前缀

在webpack中新增配置,按模块化打包css

需要注意的是 namedExport 为默认true时可能无法正常导出整个样式对象,是采用ES6的按导入

namedExport 使用
false import styles from ./index.module.less
true import { class } from ./index.module.less

原代码

打包构建结果:

production模式下打包后抽离css文件

安装mini-css-extract-plugin

ts 复制代码
npm install --save-dev mini-css-extract-plugin

构建结果

字体文件

Webpack 5 内置了资源模块(Asset Modules),无需额外安装 file-loader 或 url-loader,直接通过配置即可处理字体文件

ts 复制代码
module.exports = {
  module: {
    rules: [
      {
        test: /\.(woff|woff2|eot|ttf|otf)$/i,
        type: 'asset/resource', // 直接输出文件到输出目录
        generator: {
          filename: 'fonts/[name][ext][query]', // 自定义输出路径和文件名
        },
      },
    ],
  },
};

Webpack 4 则安装 file-loader 或 url-loader配置即可处理字体文件

ts 复制代码
module.exports = {
  module: {
    rules: [
      {
        test: /\.(woff|woff2|eot|ttf|otf)$/i,
        use: [
          {
            loader: 'file-loader',
            options: {
              name: '[name].[ext]', // 输出文件名
              outputPath: 'fonts/', // 输出目录
              publicPath: '/fonts/', // 公共路径(用于 HTML 引用)
            },
          },
        ],
      },
    ],
  },
};

Plugin

打包更便捷: HtmlWebpackPlugin & CleanWebpackPlugin

  • htmlWebpackPlugin 会在打包结束后,自动生成一个html文件,并把打包生成的js自动引入到html文件中;htmlWebpackPlugin 也可以配置一个模板
  • cleanWebpackPlugin 在打包前,自动清除打包文件
ts 复制代码
 plugins: [
    new HtmlWebpackPlugin({
      template: path.join(__dirname, "src", "index.html"), // 指定模板文件
      filename: "index.html", // 输出的HTML文件名称
    }),
    new CleanWebpackPlugin() // 默认清理 output.path 目录
  ],

Entry 与 Output

配置多入口

多入口(Multi-Entry)打包,可以将多个独立的页面或模块打包成不同的输出文件

通过 entry 配置多个入口文件,每个入口对应一个输出文件

ts 复制代码
const path = require('path');

module.exports = {
  entry: {
    // 键:输出文件名(不含扩展名),值:入口文件路径
    home: path.join(__dirname, "src", "index.js"),
    other: path.join(__dirname, "src", "otherother.js.js"),
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].bundle.[contenthash].js', // [name] 会被替换为入口键名(home/about/contact)
};

打包后如图

Devtools

配置 source-map

参考 source-map配置

sourceMap 映射原始代码和打包代码之间的映射关系,也就是本地打包或者运行时,可以知道源代码中具体哪一行错误
开发模式下source-map是默认打开的,一些常用的devtool配置如下

  • source-map:打包后单独生成对映文件,较耗费性能

  • inline-source-map: 映射表直接放在bundle文件里面 具体到哪一行的哪一列出错

  • cheap-inline-source-map :具体到哪一行出错

  • cheap-module-source-map: 不仅管业务代码里面的错误 还管第三方代码和loaders里面的报错

  • 带eval:效率最快,性能最好,但是复杂的内容eval提示并不全面。

development模式下推荐:cheap-module-eval-source-map

production模式下使用: cheap-module-source-map

WebpackDevServer 配置本地开发

devServer基本配置

ts 复制代码
module.exports = {
  // ...其他配置(entry, output等)
  devServer: {
    static: {
      directory: path.join(__dirname, 'dist'), // 静态文件目录(默认根目录)
    },
    compress: true, // 启用gzip压缩
    port: 9000,    // 端口号(默认8080)
    hot: true,      // 启用热更新(HMR)
    open: true,     // 自动打开浏览器
    historyApiFallback: true, // 支持SPA的路由回退
 	proxy: { //配置代理
    	'/api': {
   		 target: 'http://localhost:3000',
   		 pathRewrite: { '^/api': '' },
    },
  },
  },
  headers: {
    'Access-Control-Allow-Origin': '*',
  },
};

🌟 Proxy 解决前端→后端的跨域,CORS 解决客户端→DevServer的跨域

Hot Module Replacement (HMR)

HMR的作用

开启后,当代码更新时,无需刷新页面,HMR 仅更新受影响的模块,不会重置整个应用。
适用场景:

  • 表单输入、滚动位置、定时器等状态不会丢失
  • 开发复杂交互应用(如游戏、画布编辑器)
  • 修改CSS文件时,css-loader已经内置css热更新

  • 在修改JS文件时,通过 module.hot.accept 自定义热更新逻辑

ts 复制代码
if (module.hot) {
  module.hot.accept('./updatedModule.js', () => {
    console.log('模块已更新');
  });
}
  • React中使用 @pmmmwh/react-refresh-webpack-plugin;Vue中使用 vue-loader 支持 HMR。

optimization 配置优化

minimizer : css代码压缩

复制代码
npm install terser-webpack-plugin css-minimizer-webpack-plugin --save-dev
ts 复制代码
 optimization: {
  	minimizer: [new TerserPlugin(), new CssMinimizerPlugin()],
  }

Tree Shaking:usedExports

只有ES Module 才会采取 Tree Shaking配置,因为ES Module 是静态引入(编译时引入),这样可以进行分析,而common.js是执行时引入,无法分析

production模式下 Tree Shaking 自动开启 ;development 模式下 Tree Shaking 默认关闭

ts 复制代码
optimization: { usedExports: true  }

开启 Tree Shaking 后,只会引入文件中需要的模块,降低文件体积

代码分割(Code Splitting):splitChunks

打包后的代码如何拆分代码块(如第三方库、公共模块),避免重复引用

配置:

ts 复制代码
optimization: {
  splitChunks: {
    chunks: 'all', // 对所有入口和动态导入生效
    minSize: 30000, // 模块大于 30KB 才拆分
    maxSize: 0, // 不限制最大体积(0 表示无限制)
    minChunks: 1, // 模块被引用至少 1 次才拆分
    maxAsyncRequests: 5, // 异步加载的最大并行请求数
    maxInitialRequests: 3, // 入口点的最大并行请求数
    automaticNameDelimiter: '~', // 生成文件名分隔符(如 `vendors~main.js`)
    cacheGroups: {
      vendors: {
        test: /[\\/]node_modules[\\/]/, // 拆分 node_modules 中的模块
        priority: -10, // 优先级(数值越高优先级越高)
        name: 'vendors', // 生成的文件名
      },
      common: {
        minChunks: 2, // 模块被引用至少 2 次才拆分
        priority: -20,
        reuseExistingChunk: true, // 复用已存在的 chunk
        name: 'common',
      },
    },
  },
}

resolve : 路径解析

extensions 省略扩展名

extensions 允许自动解析模块后缀名,允许省略扩展名

示例:import File from './file' 会依次尝试加载 ./file.js、./file.jsx

tsjavascript 复制代码
resolve: {
  extensions: ['.js', '.jsx', '.json'] // 默认包含 .js 和 .json
}

alias 简化长路径引用

import { func } from '@/utils' 会解析为 src/utils/index.js:

ts 复制代码
const path = require('path');
resolve: {
  alias: {
    '@': path.resolve(__dirname, 'src'), // 将 @ 指向 src 目录
    'utils': path.resolve(__dirname, 'src/utils') // 精确匹配 utils
  }
}

Webpack 常用性能优化

优化构建速度

babel开启缓存,exclude 限制 Loader 处理范围

ts 复制代码
   // webpack.config.js
module.exports = {
 module: {
   rules: [
     {
       test: /\.js$/,
       exclude: /node_modules/, // 排除node_modules目录
       use: {
         loader: 'babel-loader',
         options: {
           cacheDirectory: true, // 启用缓存(默认路径:node_modules/.cache/babel-loader)
           cacheCompression: false, // 禁用缓存压缩(加速构建,减少CPU开销)
         }
       }
     }
	 ]  }};

IngorePlugin、DllPlugin、NoParse

  • IngorePlugin

用于显式忽略特定模块或文件,从而减少打包体积、避免引入不必要的依赖或解决潜在冲突

ts 复制代码
module.exports = {
  plugins: [
    new IgnorePlugin(resourceRegExp, contextRegExp)
  ]
};
	// resourceRegExp:正则表达式,匹配需要忽略的模块路径(如 './locale/en')。
    // contextRegExp(可选):正则表达式,限制忽略的上下文目录(如 'moment' 表示仅在 moment 包内忽略)。


	// 忽略 moment.js 的所有本地化文件
	new IgnorePlugin(/^\.\/locale$/, /moment$/);
  • NoParse

跳过已压缩的库(如 lodash.min.js)的依赖解析,避免重复打包

ts 复制代码
// 跳过 jQuery 和 Lodash 的解析
module.exports = {
  module: {
    noParse: /jquery|lodash/,
    rules: [
      {
        test: /\.js$/,
        use: 'babel-loader',
        exclude: /node_modules/,
      },
    ],
  },
  externals: {
    // 结合 externals 从 CDN 引入
    jquery: 'jQuery',
    lodash: '_',
  },
};

Thread-loader (5.x) / HappyPack (4.x) 多进程加速构建

多进程并行处理:将 Loader 的任务分解到多个子进程(Worker)中并行执行,子进程处理完成后将结果返回主进程

  • 适合项目较大,打包较慢的工程,小项目开启多进程反倒会拖慢构建
  • happyPack
ts 复制代码
const HappyPack = require('happypack');

module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        use: 'happypack/loader?id=babel', // 使用 happypack/loader
        exclude: /node_modules/,
      },
    ],
  },
  plugins: [
    new HappyPack({
      id: 'babel', // 唯一标识符
      loaders: ['babel-loader?cacheDirectory'], // 实际使用的 Loader
      threadPool: HappyPack.ThreadPool({ size: os.cpus().length }), // 可选:共享线程池
    }),
  ],
};
  • ThreadLoader
ts 复制代码
module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        use: [
          {
            loader: 'thread-loader', // 放置在其他 Loader 之前
            options: {
              workers: os.cpus().length, // Worker 数量
            },
          },
          'babel-loader', // 后续 Loader 在 Worker 池中运行
        ],
        exclude: /node_modules/,
      },
    ],
  },
};

TerserPlugin 多进程压缩

在比较旧的版本中,可采用 ParallelUglifyPlugin

ts 复制代码
const TerserPlugin = require('terser-webpack-plugin');

module.exports = {
  optimization: {
    minimize: true,
    minimizer: [
      new TerserPlugin({
        parallel: true, // 启用多进程
        terserOptions: {
          compress: {
            drop_console: true,
            warnings: false,
          },
          output: {
            comments: false,
          },
        },
      }),
    ],
  },
};

DllPulgin 生成动态链接库文件

开发模式下,将依赖较多且更新频率低的第三方库,(如 react、react-dom、lodash)打包成独立的 DLL 文件,生成 manifest.json 文件,记录 DLL 中模块的映射关系,供 DllReferencePlugin 使用。

  • 创建 DLL 打包配置
ts 复制代码
const path = require('path');
const webpack = require('webpack');

module.exports = {
  mode: 'production', // 或 'development'
  entry: {
    vendor: ['react', 'react-dom', 'lodash'], // 需拆分的第三方库
  },
  output: {
    filename: '[name].dll.js', // 输出 DLL 文件名
    path: path.resolve(__dirname, 'dll'), // 输出目录
    library: '[name]_[fullhash]', // 暴露的全局变量名(避免冲突)
  },
  plugins: [
    new webpack.DllPlugin({
      name: '[name]_[fullhash]', // 与 output.library 一致
      path: path.join(__dirname, 'dll', '[name]-manifest.json'), // manifest 文件路径
    }),
  ],
};
  • 在主配置中引用 DLL(webpack.config.js)
ts 复制代码
const path = require('path');
const webpack = require('webpack');

module.exports = {
  // ...其他配置(如 entry、output、module 等)
  plugins: [
    new webpack.DllReferencePlugin({
      manifest: require('./dll/vendor-manifest.json'), // 引入 manifest 文件
      context: __dirname, // 上下文路径(需与 DLL 配置一致)
    }),
  ],
};
  • 在 HTML 中引入 DLL 文件
ts 复制代码
<!-- index.html -->
<script src="./dll/vendor.dll.js"></script> <!-- 手动引入或通过插件自动插入 -->

优化代码体积

Tree Shaking

TerserPlugin 代码压缩

css、图片资源优化

  • 小图片 Base64 编码:使用 url-loader 设置 limit
  • MiniCssExtractPlugin 抽离配置

SplitChunksPlugin 代码分割

相关推荐
xkxnq2 小时前
第二阶段:Vue 组件化开发(第 26天)
前端·javascript·vue.js
华玥作者2 小时前
uni-app + Vite 项目中使用 @uni-helper/vite-plugin-uni-pages 实现自动路由配置(超详细)
前端·uni-app·vue·vue3·vite
m0_748254662 小时前
HTML 文本格式化基础
前端·html
十六年开源服务商2 小时前
WordPress集成GoogleAnalytics最佳实践指南
前端·人工智能·机器学习
小救星小杜、2 小时前
el-form 表格校验 开始和结束时间,时间选择范围
javascript·vue.js·elementui
狼性书生2 小时前
uniapp+vue3实现的简单吐司通知弹窗组件
前端·uni-app·vue·组件·插件
克里斯蒂亚诺更新2 小时前
使用elementUI的表格报错ResizeObserver loop completed with undelivered notifications.
前端·javascript·elementui
陈随易2 小时前
Vue-Router v5内置文件式路由,告别手写维护的恐惧
前端·后端·程序员
C_心欲无痕2 小时前
移动端 B 站弹幕功能实现
前端·javascript