Search K
Appearance
Appearance
Webpack 是一个现代 JavaScript 应用程序的静态模块打包工具。它将项目中的所有资源(JavaScript、CSS、图片等)视为模块,通过分析模块之间的依赖关系,最终将它们打包成一个或多个 bundle。
核心理念: 一切皆模块
Vite 是新一代前端构建工具,由 Vue.js 作者尤雨溪开发。它利用浏览器原生 ES Module 特性,在开发环境下无需打包,实现极速的冷启动和热更新。
核心理念: 利用浏览器原生能力,按需编译
初始化参数 → 开始编译 → 确定入口 → 编译模块 → 完成模块编译 → 输出资源 → 输出完成(1)初始化阶段
(2)编译阶段
(3)输出阶段
// webpack.config.js 示例
module.exports = {
// 入口:指示 webpack 应该使用哪个模块作为构建依赖图的开始
entry: './src/index.js',
// 输出:告诉 webpack 在哪里输出它所创建的 bundle
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
},
// Loader:让 webpack 能够处理非 JavaScript 文件
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.ts$/,
use: 'ts-loader'
}
]
},
// 插件:执行范围更广的任务(打包优化、资源管理、注入环境变量等)
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
})
],
// 模式:development、production 或 none
mode: 'development'
}Webpack 的热更新基于 webpack-dev-server:
更新流程:
文件修改 → Webpack 监听到变化 → 重新编译 → 生成 manifest.json(描述变化)
→ 通过 WebSocket 推送给客户端 → HMR Runtime 接收 → 对比差异 → 局部更新Vite 在开发环境下不进行打包,而是利用浏览器原生 ES Module 支持:
启动服务器 → 浏览器请求模块 → Vite 按需编译 → 返回编译结果核心特点:
<script type="module"> 特性<!-- 浏览器请求 -->
<script type="module" src="/src/main.js"></script>// main.js
import { createApp } from 'vue'
import App from './App.vue'
// Vite 会将这些导入转换为:
import { createApp } from '/@modules/vue' // 预构建的依赖
import App from '/src/App.vue' // 源码模块处理步骤:
Vite 使用 esbuild 预构建依赖,主要目的:
// vite.config.js
export default {
optimizeDeps: {
// 指定需要预构建的依赖
include: ['vue', 'axios'],
// 排除不需要预构建的依赖
exclude: ['your-local-package']
}
}预构建原因:
基于ESM的HMR热更新:
目前所有的打包工具实现热更新的思路都大同小异:主要是通过WebSocket创建浏览器和服务器的通信监听文件的改变,当文件被修改时,服务端发送消息通知客户端修改相应的代码,客户端对应不同的文件进行不同的操作的更新。
Vite 的 HMR 更加精确和快速:
// Vite 的 HMR API
if (import.meta.hot) {
import.meta.hot.accept((newModule) => {
// 更新模块
})
}更新流程:
文件修改 → Vite 监听到变化 → 找到受影响的模块链 → 通过 WebSocket 通知客户端
→ 浏览器重新请求该模块 → Vite 重新编译该模块 → 返回新内容 → 精确更新优势:
Vite 在生产环境使用 Rollup 进行打包:
// vite.config.js
export default {
build: {
// 构建目标
target: 'es2015',
// 输出目录
outDir: 'dist',
// Rollup 配置
rollupOptions: {
output: {
manualChunks: {
'vue-vendor': ['vue', 'vue-router']
}
}
}
}
}| 对比项 | Webpack | Vite |
|---|---|---|
| 冷启动速度 | 慢(需要打包所有模块) | 快(无需打包,秒级启动) |
| 热更新速度 | 随项目增大而变慢 | 始终快速(毫秒级) |
| 首次加载 | 快(已打包) | 稍慢(按需加载) |
| 配置复杂度 | 较复杂 | 简单(开箱即用) |
中型项目(500+ 模块):
Webpack 冷启动: 20-30秒
Vite 冷启动: 1-2秒
Webpack HMR: 2-5秒
Vite HMR: 100-300毫秒
大型项目(2000+ 模块):
Webpack 冷启动: 60-120秒
Vite 冷启动: 1-3秒
Webpack HMR: 5-10秒
Vite HMR: 100-500毫秒
所有模块 → 全量打包 → 生成 bundle → 启动服务器启动服务器 → 预构建依赖 → 按需编译 → 返回 ESM// webpack.config.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const { VueLoaderPlugin } = require('vue-loader')
module.exports = {
mode: 'development',
entry: './src/main.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[contenthash].js',
clean: true
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader'
},
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
},
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader'
]
},
{
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'sass-loader'
]
},
{
test: /\.(png|jpe?g|gif|svg)$/,
type: 'asset/resource'
}
]
},
plugins: [
new VueLoaderPlugin(),
new HtmlWebpackPlugin({
template: './public/index.html'
}),
new MiniCssExtractPlugin({
filename: '[name].[contenthash].css'
})
],
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'@': path.resolve(__dirname, 'src')
}
},
devServer: {
port: 8080,
hot: true,
open: true
},
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
priority: 10
}
}
}
}
}// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
'@': path.resolve(__dirname, 'src')
}
},
server: {
port: 3000,
open: true,
// 代理配置
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
}
},
build: {
outDir: 'dist',
// 代码分割
rollupOptions: {
output: {
manualChunks: {
'vue-vendor': ['vue', 'vue-router', 'pinia'],
'ui-vendor': ['element-plus']
}
}
},
// 压缩配置
minify: 'terser',
terserOptions: {
compress: {
drop_console: true,
drop_debugger: true
}
}
},
// CSS 配置
css: {
preprocessorOptions: {
scss: {
additionalData: `@import "@/styles/variables.scss";`
}
}
}
})配置复杂度对比:
优势:
常用插件:
// 常见 Webpack 插件
- html-webpack-plugin // HTML 生成
- mini-css-extract-plugin // CSS 提取
- clean-webpack-plugin // 清理输出目录
- copy-webpack-plugin // 复制静态资源
- webpack-bundle-analyzer // 打包分析
- compression-webpack-plugin // Gzip 压缩
- terser-webpack-plugin // JS 压缩优势:
常用插件:
// 常见 Vite 插件
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
import legacy from '@vitejs/plugin-legacy'
import { visualizer } from 'rollup-plugin-visualizer'
import compression from 'vite-plugin-compression'
export default {
plugins: [
vue(), // Vue 3 支持
vueJsx(), // JSX 支持
legacy({ // 旧浏览器支持
targets: ['defaults', 'not IE 11']
}),
visualizer(), // 打包分析
compression() // Gzip 压缩
]
}// babel.config.js
module.exports = {
presets: [
['@babel/preset-env', {
targets: {
browsers: ['> 1%', 'last 2 versions', 'not ie <= 8']
},
useBuiltIns: 'usage',
corejs: 3
}]
]
}// vite.config.js
import legacy from '@vitejs/plugin-legacy'
export default {
plugins: [
legacy({
targets: ['defaults', 'not IE 11'],
additionalLegacyPolyfills: ['regenerator-runtime/runtime']
})
]
}dist/
├── index.html
├── js/
│ ├── app.[hash].js # 应用代码
│ ├── chunk-vendors.[hash].js # 第三方依赖
│ └── chunk-common.[hash].js # 公共代码
├── css/
│ └── app.[hash].css
└── img/
└── logo.[hash].png特点:
dist/
├── index.html
├── assets/
│ ├── index.[hash].js # 入口文件
│ ├── vendor.[hash].js # 第三方依赖
│ ├── index.[hash].css
│ └── logo.[hash].png特点:
// webpack.config.js
module.exports = {
// 1. 代码分割
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
priority: 10
},
common: {
minChunks: 2,
priority: 5,
reuseExistingChunk: true
}
}
},
// 2. 运行时代码提取
runtimeChunk: 'single',
// 3. 压缩
minimize: true,
minimizer: [
new TerserPlugin({
parallel: true,
terserOptions: {
compress: {
drop_console: true
}
}
})
]
},
// 4. 缓存
cache: {
type: 'filesystem',
buildDependencies: {
config: [__filename]
}
},
// 5. 并行处理
module: {
rules: [
{
test: /\.js$/,
use: [
{
loader: 'thread-loader',
options: {
workers: 4
}
},
'babel-loader'
]
}
]
}
}// vite.config.js
export default {
build: {
// 1. 代码分割
rollupOptions: {
output: {
manualChunks(id) {
if (id.includes('node_modules')) {
return 'vendor'
}
}
}
},
// 2. 压缩
minify: 'terser',
terserOptions: {
compress: {
drop_console: true,
drop_debugger: true
}
},
// 3. CSS 代码分割
cssCodeSplit: true,
// 4. 构建目标
target: 'es2015',
// 5. 大文件警告阈值
chunkSizeWarningLimit: 1000
},
// 6. 依赖预构建优化
optimizeDeps: {
include: ['vue', 'vue-router', 'pinia'],
exclude: ['your-local-package']
}
}✅ 推荐使用:
典型案例:
✅ 推荐使用:
典型案例:
| 指标 | Webpack 5 | Vite 4 | 提升 |
|---|---|---|---|
| 冷启动时间 | 45s | 1.8s | 25x |
| 热更新时间 | 3.2s | 0.2s | 16x |
| 内存占用 | 800MB | 300MB | 2.7x |
| CPU 占用 | 85% | 25% | 3.4x |
| 指标 | Webpack 5 | Vite 4 | 差异 |
|---|---|---|---|
| 构建时间 | 120s | 90s | Vite 快 25% |
| 产物大小 | 2.5MB | 2.2MB | Vite 小 12% |
| Tree-shaking | 良好 | 优秀 | Vite 更好 |
| 代码分割 | 灵活 | 灵活 | 相当 |
✅ 生态成熟,插件丰富
✅ 配置灵活,功能强大
✅ 浏览器兼容性好
✅ 适合大型复杂项目
✅ 社区资源丰富
❌ 配置复杂,学习成本高
❌ 开发环境启动慢
❌ 热更新速度随项目增大而变慢
❌ 构建速度相对较慢
✅ 开发体验极佳(快速启动和热更新)
✅ 配置简单,开箱即用
✅ 现代化的开发方式
✅ 构建产物更小
✅ 对 Vue 3 支持最好
❌ 生态相对不够成熟
❌ 开发环境要求现代浏览器
❌ 某些老旧依赖可能不兼容
❌ 大型项目的稳定性有待验证
选择 Webpack:
选择 Vite:
Webpack 项目优化:
// 1. 使用持久化缓存
cache: { type: 'filesystem' }
// 2. 合理配置代码分割
optimization: { splitChunks: { chunks: 'all' } }
// 3. 使用 thread-loader 并行处理
// 4. 开启 Tree Shaking
// 5. 使用 DllPlugin 预编译依赖Vite 项目优化:
// 1. 合理配置依赖预构建
optimizeDeps: { include: ['heavy-dep'] }
// 2. 使用动态导入
const Component = () => import('./Component.vue')
// 3. 配置代码分割策略
build: { rollupOptions: { output: { manualChunks } } }
// 4. 开启 CSS 代码分割
build: { cssCodeSplit: true }