一、打包工具基础概念
1. 什么是打包工具?
打包工具(Bundler)是将多个模块(JS、CSS、图片等)组合成一个或多个文件的工具,主要用于:
- 模块化处理:解决浏览器不支持原生模块的问题
- 资源优化:压缩代码、处理依赖关系、分割文件
- 兼容性转换:编译TypeScript、SCSS等非原生格式
- 开发体验增强:提供热更新、代码分割等功能
2. 为什么需要打包工具?
- 模块管理:处理复杂的依赖关系
- 性能优化:减少HTTP请求、代码分割、懒加载
- 兼容性:转换现代语法以支持旧浏览器
- 资源处理:统一处理图片、字体、CSS等资源
3. Webpack 与 Vite 对比
链接: vite中文网
链接: Webpack中文网
特性 | Webpack | Vite |
---|---|---|
构建原理 | 基于打包(Bundle) | 基于ES模块(ESM)和按需编译 |
冷启动速度 | 慢(需要分析整个依赖树) | 极快(利用浏览器原生ES模块支持) |
热更新(HMR) | 中等速度(需要重新打包受影响模块) | 即时更新(无需重新打包) |
生态系统 | 成熟,插件丰富 | 新兴,生态快速发展 |
适用场景 | 大型复杂项目,需要全面优化 | 中小型项目,追求极致开发体验 |
二、Webpack 基础配置
1. 安装依赖
npm install webpack webpack-cli --save-dev
npm install babel-loader @babel/core @babel/preset-env --save-dev
2. 基本配置文件 webpack.config.js
const path = require('path');
module.exports = {
mode: 'production', // 模式:development/production
entry: './src/index.js', // 入口文件
output: {
path: path.resolve(__dirname, 'dist'), // 输出目录
filename: 'bundle.[contenthash].js', // 输出文件名(带哈希)
},
module: {
rules: [
{
test: /\.js$/, // 匹配JS文件
exclude: /node_modules/,
use: {
loader: 'babel-loader', // 使用Babel转换
options: {
presets: ['@babel/preset-env'] // 预设环境
}
}
}
]
},
plugins: [
// 更多插件配置...
]
};
1.入口
// 单入口
entry: './src/index.js'
// 多入口(生成多个 bundle)
entry: {
main: './src/index.js',
vendor: './src/vendor.js'
}
2.输出
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[contenthash].js', // 基于内容生成哈希
publicPath: '/assets/', // 资源加载路径
clean: true // 构建前清理dist目录
}
3.loader
module: {
rules: [
// CSS 处理
{
test: /\.css$/,
use: [
'style-loader', // 将 CSS 注入 DOM
{
loader: 'css-loader',
options: {
modules: true // 启用 CSS 模块
}
},
'postcss-loader' // 自动添加浏览器前缀
]
},
// 图片处理
{
test: /\.(png|jpg|gif)$/i,
type: 'asset/resource', // 替代 file-loader
generator: {
filename: 'images/[hash][ext][query]'
}
},
// TypeScript 处理
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/
}
]
}
4.插件Plugin
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
plugins: [
// 自动生成 HTML 文件
new HtmlWebpackPlugin({
template: './src/index.html',
favicon: './src/favicon.ico'
}),
// 提取 CSS 到单独文件
new MiniCssExtractPlugin({
filename: '[name].[contenthash].css'
}),
// 清理 dist 目录
new CleanWebpackPlugin(),
// 定义全局变量
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV)
}),
// 压缩 JS
new TerserPlugin({
parallel: true, // 并行压缩
terserOptions: {
compress: {
drop_console: true // 移除 console
}
}
})
]
5.代码分割
optimization: {
splitChunks: {
chunks: 'all', // 对所有类型的 chunk 进行分割
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all'
}
}
},
runtimeChunk: 'single' // 生成单独的运行时代码
}
3. 开发环境配置
// webpack.dev.js
const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');
module.exports = merge(common, {
mode: 'development',
devtool: 'inline-source-map', // 生成source map便于调试
devServer: { // 开发服务器配置
static: './dist',
hot: true // 启用热更新
}
});
4. 生产环境配置
// webpack.prod.js
const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = merge(common, {
mode: 'production',
plugins: [
new MiniCssExtractPlugin({ // 提取CSS到单独文件
filename: '[name].[contenthash].css'
})
],
module: {
rules: [
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader, // 替代style-loader
'css-loader'
]
}
]
}
});
5.插件Plugin
Webpack 插件是扩展其功能的核心机制,可在打包过程的特定阶段执行自定义操作(如代码优化、资源管理、环境注入等)。以下是常用插件分类及详细介绍:
1. HTML 处理
-
html-webpack-plugin
功能:自动生成 HTML 文件并注入打包后的 JS/CSS 文件。
场景:单页应用(SPA)或多页应用(MPA)的入口 HTML 生成。
安装:npm install html-webpack-plugin -D
配置示例:
const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { plugins: [ new HtmlWebpackPlugin({ template: './src/index.html', // 模板文件 filename: 'index.html', // 输出文件名 minify: { // 生产环境压缩 HTML collapseWhitespace: true, removeComments: true } }) ] };
-
html-webpack-externals-plugin
功能:将特定模块排除在打包外,通过 CDN 引入。
场景:优化第三方库加载(如 React、Vue)。
2. CSS 处理
-
mini-css-extract-plugin
功能:将 CSS 提取为独立文件(替代style-loader
)。
场景:生产环境 CSS 分离(避免 JS 加载后再渲染样式)。
安装:npm install mini-css-extract-plugin -D
配置示例:
const MiniCssExtractPlugin = require('mini-css-extract-plugin'); module.exports = { module: { rules: [ { test: /\.css$/, use: [ MiniCssExtractPlugin.loader, // 替代 style-loader 'css-loader' ] } ] }, plugins: [ new MiniCssExtractPlugin({ filename: '[name].[contenthash].css' // 输出文件名 }) ] };
-
css-minimizer-webpack-plugin
功能:压缩和优化 CSS 文件。
场景:生产环境 CSS 体积优化。
3. 代码优化与压缩
-
terser-webpack-plugin
功能:压缩 JS 代码(Webpack 5 默认内置,生产环境自动启用)。
场景:生产环境减小 JS 体积。
配置示例:const TerserPlugin = require('terser-webpack-plugin'); module.exports = { optimization: { minimizer: [ new TerserPlugin({ terserOptions: { compress: { drop_console: true // 移除 console 语句 } } }) ] } };
-
image-minimizer-webpack-plugin
功能:压缩图片(JPEG、PNG、SVG 等)。
场景:优化静态资源体积。
4. 资源管理
-
clean-webpack-plugin
功能:每次打包前清理输出目录(如dist
)。
场景:避免旧文件残留。
安装:npm install clean-webpack-plugin -D
配置示例:
const { CleanWebpackPlugin } = require('clean-webpack-plugin'); module.exports = { plugins: [ new CleanWebpackPlugin() ] };
-
copy-webpack-plugin
功能:复制未经过 Webpack 处理的文件(如静态资源)。
场景:复制public
目录到dist
。
5. 环境变量与全局变量
webpack.DefinePlugin
功能:定义编译时可访问的全局变量(如 API 地址、环境标识)。
场景:区分开发/生产环境配置。
配置示例:const webpack = require('webpack'); module.exports = { plugins: [ new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV), 'API_BASE_URL': JSON.stringify('https://api.example.com') }) ] };
6. 代码分割与懒加载
split-chunks-plugin
功能:自动分割代码(Webpack 5 内置,无需额外安装)。
场景:分离第三方库、提取公共模块。
配置示例:module.exports = { optimization: { splitChunks: { chunks: 'all', // 分割所有类型的 chunks cacheGroups: { vendor: { test: /[\\/]node_modules[\\/]/, name: 'vendors', // 生成 vendor.js chunks: 'all' } } } } };
7. 类型检查与编译
fork-ts-checker-webpack-plugin
功能:在 Webpack 构建过程中并行检查 TypeScript 类型(提高编译速度)。
场景:TypeScript 项目。
8. HMR(热模块替换)
webpack.HotModuleReplacementPlugin
功能:开发环境下无需刷新页面即可替换模块。
场景:提升开发体验。
配置示例:module.exports = { devServer: { hot: true // 启用 HMR }, plugins: [ new webpack.HotModuleReplacementPlugin() ] };
9.插件执行顺序与生命周期
Webpack 插件通过监听**编译钩子(Hook)**在特定阶段执行,常见钩子:
- 编译前:
entry-option
、compile
- 构建模块时:
normal-module-loader
- 生成资源时:
emit
- 完成后:
done
示例:自定义插件监听 emit
钩子,在生成文件前修改资源:
class MyPlugin {
apply(compiler) {
// 监听 emit 钩子(生成资源到输出目录前)
compiler.hooks.emit.tap('MyPlugin', (compilation) => {
// 修改或删除资源
console.log('生成文件前执行...');
});
}
}
// 在配置中使用
module.exports = {
plugins: [new MyPlugin()]
};
10、自定义插件开发
插件本质是包含 apply
方法的类,通过监听钩子实现功能。
示例:创建一个简单的插件,在打包完成后输出统计信息:
class StatsPlugin {
constructor(options) {
this.options = options;
}
apply(compiler) {
// 监听编译完成钩子
compiler.hooks.done.tap('StatsPlugin', (stats) => {
console.log(`打包完成!共生成 ${stats.compilation.assetsCount} 个文件`);
console.log(`耗时:${stats.endTime - stats.startTime}ms`);
if (this.options.verbose) {
// 输出详细信息
console.log('生成的文件:');
Object.keys(stats.compilation.assets).forEach((filename) => {
console.log(`- ${filename} (${stats.compilation.assets[filename].size()} bytes)`);
});
}
});
}
}
// 使用插件
module.exports = {
plugins: [new StatsPlugin({ verbose: true })]
};
6. Webpack 生产构建
# 执行生产构建
npm run build
# 构建结果
dist/
├── index.html
├── main.[hash].js
├── vendor.[hash].js
├── styles.[hash].css
└── assets/
├── image1.png
└── font.woff
7.过程
1.创建进入一个目录并初始化 npm,然后 安装 webpack,接着安装 webpack-cli(此工具用于在命令行中运行 webpack):
mkdir project
cd project(创建项目,可以手动完成)
npm init -y (初始化项目/npm init)
# -y 表示默认配置,跳过手动输入
npm install webpack webpack-cli --save-dev(安装,dev开发)
# 开发环境依赖(--save-dev 缩写 -D)
# npm install webpack webpack-cli -D
2.创建以下目录结构、文件和内容
project/
+├── src/ # 源代码目录(必须)
+│ ├── index.js # 入口文件(Webpack 打包起点)
+│ └── other.js # 其他模块(如工具函数、组件等)
├── dist/ # 打包输出目录(自动生成)
+├── webpack.config.js # Webpack 配置文件(核心)
├── package.json # 项目依赖和脚本配置
└── package-lock.json
3.还需要调整 package.json 文件以确保安装包是私有的,并移除 main 入口,这可以防止意外发布代码。
{
"name": "project",
"version": "1.0.0",
"description": "",
- "main": "index.js",(删除)
+ "private": true,(添加)
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "MIT",
"devDependencies": {
"webpack": "^5.38.1",
"webpack-cli": "^4.7.2"
}
}
---------------------------------------------------
在 package.json 中添加 scripts 脚本,简化打包命令:
{
"scripts": {
"build": "webpack" // 执行 npm run build 时,会自动调用 webpack 命令
}
}
4.创建的webpack.config.js文件配置
// 引入 Node.js 路径模块(处理文件路径)
const path = require('path');
module.exports = {
// 1. 入口文件(必填):指定 Webpack 从哪个文件开始打包
entry: './src/index.js',
// 2. 输出配置(必填):指定打包后文件的输出路径和名称
output: {
path: path.resolve(__dirname, 'dist'), // 输出目录(必须是绝对路径,用 path 模块处理)
filename: 'bundle.js' // 输出文件名(默认 main.js,可自定义)
},
// 3. 模式(必填):指定环境,影响打包优化策略
mode: 'development' // 可选值:'development'(开发,未压缩)、'production'(生产,自动压缩)
};
(开发环境的包时应该使用 npm install --save-dev。)
npm install --save lodash(根据需要安装)
5.打包
终端运行以下命令,Webpack 会根据 webpack.config.js 配置进行打包:
npm run build
1.Webpack 只能直接处理 JS 和 JSON 文件,需通过 loader 处理其他类型文件
# 处理 CSS:css-loader(解析 CSS 模块)、style-loader(注入 CSS 到页面)
npm install css-loader style-loader -D
# 处理图片:file-loader(复制文件)或 url-loader(小图转 base64)
npm install file-loader url-loader -D
2.生成 HTML 自动引入打包文件
使用 html-webpack-plugin 自动生成 index.html 并引入打包后的 JS/CSS。
安装插件
npm install html-webpack-plugin -D
打包结果:
成功后会生成 dist 目录,其中 bundle.js 即为打包后的合并文件。
可新建 dist/index.html 引入该 JS 文件测试:
<script src="./bundle.js"></script>
三、Vite 基础配置
1. 安装依赖
npm install vite --save-dev
2. 基本配置文件 vite.config.js
import { defineConfig } from 'vite';
export default defineConfig({
root: './src', // 项目根目录
base: '/', // 公共基础路径
build: {
outDir: '../dist', // 输出目录
assetsDir: 'assets', // 静态资源目录
sourcemap: true // 生成source map
},
server: {
port: 3000, // 开发服务器端口
open: true, // 自动打开浏览器
proxy: { // 代理配置
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
}
}
});
1.
// vite.config.js
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';
export default defineConfig({
plugins: [
react(), // 支持 React JSX
],
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'), // 路径别名
},
},
css: {
preprocessorOptions: {
scss: {
additionalData: `@import "@/styles/variables.scss";` // 全局引入 SCSS 变量
}
}
},
server: {
host: '0.0.0.0', // 允许外部访问
port: 5173,
strictPort: true, // 端口被占用时直接退出
},
build: {
minify: 'terser', // 使用 terser 替代 esbuild 压缩 JS
terserOptions: {
compress: {
drop_console: true
}
},
rollupOptions: {
output: {
manualChunks(id) {
if (id.includes('node_modules')) {
return id.toString().split('node_modules/')[1].split('/')[0].toString();
}
}
}
}
}
});
2.插件
# React 支持
npm install @vitejs/plugin-react --save-dev
# Vue 支持
npm install @vitejs/plugin-vue --save-dev
# TypeScript 支持
npm install @rollup/plugin-typescript --save-dev
# ESLint 集成
npm install vite-plugin-eslint --save-dev
# PWA 支持
npm install vite-plugin-pwa --save-dev
3.自定义插件
// 简单的文件替换插件
const fileReplacementPlugin = (options) => {
return {
name: 'file-replacement',
enforce: 'pre',
resolveId(id) {
if (options[id]) {
return options[id];
}
return null;
}
};
};
// 在 vite.config.js 中使用
plugins: [
fileReplacementPlugin({
'./src/env.js': './src/env.prod.js'
})
]
3. 环境变量配置
在 Vite 中,环境变量的命名规则默认为 VITE_
开头,例如 VITE_API_URL
。
VITE_SOME_KEY=123
DB_PASSWORD=foobar
只有 VITE_SOME_KEY 会被暴露为 import.meta.env.VITE_SOME_KEY 提供给客户端源码,而 DB_PASSWORD 则不会。
console.log(import.meta.env.VITE_SOME_KEY) // 123
console.log(import.meta.env.DB_PASSWORD) // undefined
创建 .env
文件:
# .env.development(开发环境)
VITE_APP_TITLE=My App (Dev)
VITE_API_BASE_URL=http://dev-api.example.com
# .env.production(生产环境)
VITE_APP_TITLE=My App (Prod)
VITE_API_BASE_URL=http://api.example.com
在代码中使用:
(在代码中通过 import.meta.env 访问)
console.log(import.meta.env.VITE_APP_TITLE);
console.log(import.meta.env.VITE_API_URL);
4.Vite 生产构建
# 执行生产构建
npm run build
# 构建结果
dist/
├── index.html # 入口 HTML(自动注入资源)
├── assets/
│ ├── index.[hash].js # 打包后的 JS 文件
│ ├── index.[hash].css # 打包后的 CSS 文件
│ ├── react.[hash].js
│ └── image1.[hash].png # 图片、字体等资源
└── manifest.json
5.过程
1.创建 Vite 项目
# 方式一:使用 npm 7+(带 -- 前缀)
npm init vite@latest my-vite-app -- --template react
# 方式二:使用 yarn
(如:要构建一个 Vite + Vue 项目)
yarn create vite my-vite-app --template vue
或者直接
npm create vite@latest
yarn create vite
2.进入项目并安装依赖
cd my-vite-app
npm install
3.Vite 项目的典型结构:
my-vite-app/
├── public/ # 静态资源(直接复制,不经过打包)
│ └── favicon.ico
├── src/ # 源代码
│ ├── main.js # 入口文件
│ ├── App.vue/.js # 根组件
│ ├── index.html # 主 HTML(必须)
│ └── style.css # 样式文件
├── package.json # 依赖和脚本配置
├── vite.config.js # Vite 配置文件(核心)
└── tsconfig.json # TypeScript 配置(可选)
4.Vite 核心配置(vite.config.js)
// vite.config.js
import { defineConfig } from 'vite';
export default defineConfig({
// 项目根目录(默认:当前工作目录)
root: process.cwd(),
// 开发服务器配置
server: {
port: 3000, // 端口号
open: true, // 自动打开浏览器
proxy: { // 代理配置(解决跨域)
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
}
},
// 构建配置
build: {
outDir: 'dist', // 输出目录
assetsDir: 'assets', // 静态资源目录
minify: 'terser', // 压缩工具(默认 esbuild,terser 压缩率更高)
terserOptions: {
compress: {
drop_console: true, // 生产环境移除 console
drop_debugger: true // 生产环境移除 debugger
}
}
},
// 别名配置(路径映射)
resolve: {
alias: {
'@': '/src', // 示例:@ 指向 src 目录
}
},
// CSS 配置
css: {
preprocessorOptions: {
scss: {
additionalData: `@import "@/styles/variables.scss";` // 全局引入 SCSS 变量
}
}
}
});
5.打包
npm run build # 或 yarn build 或 pnpm build
6.总结
Vite 打包流程简洁高效,核心步骤:
1.初始化项目:使用 npm init vite 创建项目;
2.配置:通过 vite.config.js 定制开发和构建选项;
3.开发:使用 npm run dev
启动开发服务器;
4. 打包:执行 npm run build 生成生产文件;
5.部署:将 dist 目录部署到静态服务器。
四、多环境配置实践
1. Webpack 环境变量配置
// webpack.common.js
const path = require('path');
const Dotenv = require('dotenv-webpack');
module.exports = {
// ...其他配置
plugins: [
new Dotenv({
path: path.resolve(__dirname, `.env.${process.env.NODE_ENV}`),
safe: true // 加载.env.example作为安全验证
})
]
};
2. Vite 环境变量配置
// vite.config.js
import { defineConfig, loadEnv } from 'vite';
export default ({ mode }) => {
const env = loadEnv(mode, process.cwd(), 'VITE_');
return defineConfig({
// ...其他配置
define: {
'process.env': env
}
});
};
3. 运行命令配置
// package.json
{
"scripts": {
// Webpack
"dev:webpack": "webpack serve --config webpack.dev.js",
"build:webpack": "NODE_ENV=production webpack --config webpack.prod.js",
// Vite
"dev:vite": "vite",
"build:vite": "vite build --mode production",
"preview:vite": "vite preview"
}
}
五、资源处理配置
1. CSS 处理(Webpack)
// webpack.common.js
module.exports = {
// ...其他配置
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader', // 开发环境使用
// MiniCssExtractPlugin.loader, // 生产环境使用
'css-loader',
'postcss-loader' // 处理CSS前缀等
]
},
{
test: /\.scss$/,
use: [
'style-loader',
'css-loader',
'sass-loader' // 编译SCSS
]
}
]
}
};
2. 静态资源处理(Vite)
// vite.config.js
export default defineConfig({
// ...其他配置
assetsInclude: ['**/*.png', '**/*.jpg', '**/*.svg'],
build: {
assetsInlineLimit: 4096, // 小于4KB的资源内联为base64
rollupOptions: {
output: {
manualChunks: {
vendor: ['react', 'react-dom'] // 单独打包第三方库
}
}
}
}
});
六、注意
-
选择合适的工具:
- 新项目优先考虑 Vite(开发体验更好)
- 复杂项目或已有 Webpack 项目继续使用 Webpack
-
环境变量管理:
- 使用
.env
文件管理环境特定配置 - 通过
process.env.NODE_ENV
区分开发/生产环境 - 敏感信息不要直接暴露在客户端代码中
- 使用