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 代码分割

相关推荐
卸任4 小时前
Vibe coding工具使用心得
前端·ai编程
brien95 小时前
为什么模型标称 1M,Google Antigravity 聊到 256K 却突然失忆?我们给它做了个原生行内监控圆环
前端
修炼的dance5 小时前
录屏关键帧间隔多少合适?六档实测,2秒到10秒小了75.6%
前端
Lank_M5 小时前
关键帧从1秒拉到10秒,录屏小87.3%,摄像头小10.8%
前端
补码缩写5 小时前
用 ffprobe 拆录屏,八成字节在 29 个关键帧上
前端
百思不得小李5 小时前
一个复杂的动态表单是如何实现的
前端·设计模式
陈随易5 小时前
Cmder 停更 17 个月,2.0 支持 Windows Terminal
前端·后端·程序员
清晨Coding5 小时前
删了 Cookie 还能自动登录?Google系网站登录态自动恢复的原理分析
javascript
风骏时光牛马5 小时前
前沿视界
前端
kyriewen6 小时前
我扫了 814 个依赖,97 个还敢要 Node 10
前端·javascript·程序员