Webpack vs Vite 构建工程化实战(Vue 项目深度解析)

导读

如果你正在做 Vue 项目开发,一定遇到过这些问题:

  • Webpack 启动慢、热更新慢,改一行代码要等半天
  • Loader 和 Plugin 配置复杂,改个配置要查半天文档
  • 项目越来越大,构建时间越来越长

这篇文章会从 Vue2 + Webpack 典型配置 讲起,深入解析 Webpack Loader/Plugin 的核心概念,对比 Webpack vs Vite 的架构差异,最后结合我当前真实项目 uni-platform-admin 的 Vite 配置,给出可直接落地的工程化方案。


1. Vue2 + Webpack 典型配置

1.1 Vue2 项目完整配置示例

目标文件webpack.config.js(Vue2 典型配置)

javascript 复制代码
const path = require('path')
const webpack = require('webpack')
const { VueLoaderPlugin } = require('vue-loader')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
const TerserPlugin = require('terser-webpack-plugin')

module.exports = {
  // 入口文件
  entry: './src/main.js',

  // 输出配置
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'js/[name].[contenthash:8].js',
    chunkFilename: 'js/[name].[contenthash:8].js',
    clean: true,
  },

  // 模式
  mode: process.env.NODE_ENV === 'production' ? 'production' : 'development',

  // 模块规则(Loader)
  module: {
    rules: [
      // Vue 文件处理
      {
        test: /\.vue$/,
        loader: 'vue-loader',
      },
      // JavaScript 文件处理
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env'],
            plugins: ['@babel/plugin-transform-runtime'],
          },
        },
      },
      // CSS 文件处理
      {
        test: /\.css$/,
        use: [
          process.env.NODE_ENV === 'production'
            ? MiniCssExtractPlugin.loader
            : 'style-loader',
          'css-loader',
          'postcss-loader',
        ],
      },
      // SCSS 文件处理
      {
        test: /\.scss$/,
        use: [
          process.env.NODE_ENV === 'production'
            ? MiniCssExtractPlugin.loader
            : 'style-loader',
          'css-loader',
          'postcss-loader',
          {
            loader: 'sass-loader',
            options: {
              additionalData: `@import "@/assets/styles/variables.scss";`,
            },
          },
        ],
      },
      // 图片文件处理
      {
        test: /\.(png|jpg|jpeg|gif|svg)$/,
        type: 'asset',
        generator: {
          filename: 'images/[name].[hash:8][ext]',
        },
      },
      // 字体文件处理
      {
        test: /\.(woff|woff2|eot|ttf|otf)$/,
        type: 'asset/resource',
        generator: {
          filename: 'fonts/[name].[hash:8][ext]',
        },
      },
    ],
  },

  // 插件配置
  plugins: [
    // Vue Loader 插件(必须)
    new VueLoaderPlugin(),

    // HTML 模板插件
    new HtmlWebpackPlugin({
      template: './public/index.html',
      filename: 'index.html',
      minify: process.env.NODE_ENV === 'production' ? {
        collapseWhitespace: true,
        removeComments: true,
        removeAttributeQuotes: true,
      } : false,
    }),

    // CSS 抽离插件(生产环境)
    ...(process.env.NODE_ENV === 'production'
      ? [
          new MiniCssExtractPlugin({
            filename: 'css/[name].[contenthash:8].css',
            chunkFilename: 'css/[name].[contenthash:8].css',
          }),
        ]
      : []),

    // 环境变量定义插件
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV),
      'process.env.VUE_APP_BASE_API': JSON.stringify(process.env.VUE_APP_BASE_API || '/api'),
    }),
  ],

  // 解析配置
  resolve: {
    extensions: ['.js', '.vue', '.json'],
    alias: {
      '@': path.resolve(__dirname, 'src'),
      'vue$': 'vue/dist/vue.esm.js',
    },
  },

  // 开发服务器配置
  devServer: {
    host: '0.0.0.0',
    port: 8080,
    hot: true,
    open: true,
    proxy: {
      '/api': {
        target: 'http://localhost:3000',
        changeOrigin: true,
        pathRewrite: {
          '^/api': '',
        },
      },
    },
  },

  // 优化配置(生产环境)
  optimization: {
    minimize: true,
    minimizer: [
      new TerserPlugin({
        parallel: true,
        terserOptions: {
          compress: {
            drop_console: true,
            drop_debugger: true,
          },
        },
      }),
      new CssMinimizerPlugin(),
    ],
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        // 第三方库
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendor',
          priority: 10,
        },
        // 公共代码
        common: {
          name: 'common',
          minChunks: 2,
          priority: 5,
          reuseExistingChunk: true,
        },
      },
    },
    runtimeChunk: {
      name: 'runtime',
    },
  },
}

1.2 Vue2 项目 package.json 脚本

json 复制代码
{
  "scripts": {
    "dev": "webpack serve --mode development",
    "build": "webpack --mode production",
    "build:prod": "NODE_ENV=production webpack --mode production",
    "lint": "eslint --ext .js,.vue src",
    "lint:fix": "eslint --ext .js,.vue src --fix"
  }
}

1.3 Vue2 项目依赖

json 复制代码
{
  "dependencies": {
    "vue": "^2.6.14",
    "vue-router": "^3.5.3",
    "vuex": "^3.6.2",
    "axios": "^0.27.2",
    "element-ui": "^2.15.9"
  },
  "devDependencies": {
    "webpack": "^5.72.0",
    "webpack-cli": "^4.9.2",
    "webpack-dev-server": "^4.9.0",
    "vue-loader": "^15.9.8",
    "vue-template-compiler": "^2.6.14",
    "babel-loader": "^8.2.5",
    "@babel/core": "^7.17.9",
    "@babel/preset-env": "^7.17.10",
    "css-loader": "^6.7.1",
    "style-loader": "^3.3.1",
    "sass-loader": "^12.6.0",
    "sass": "^1.51.0",
    "html-webpack-plugin": "^5.5.0",
    "mini-css-extract-plugin": "^2.6.0",
    "css-minimizer-webpack-plugin": "^3.4.1",
    "terser-webpack-plugin": "^5.3.1",
    "postcss": "^8.4.12",
    "postcss-loader": "^6.2.1",
    "autoprefixer": "^10.4.7"
  }
}

2. Webpack Loader 与 Plugin 核心概念

2.1 Loader 是什么?怎么用?

作用:将非 JS 文件(.vue、.css、.png 等)转换成 JS 模块

Vue 项目典型 Loader

Loader 用途
vue-loader 解析 .vue 单文件组件,拆成 template/script/style
css-loader 处理 css 中的 @import 和 url()
style-loader 将 css 注入到

使用方式(webpack.config.js)

javascript 复制代码
module.exports = {
  module: {
    rules: [
      {
        test: /\.vue$/,           // 匹配.vue文件
        loader: 'vue-loader'      // 使用vue-loader
      },
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']  // 链式调用,从右往左
      },
      {
        test: /\.(png|jpg)$/,
        type: 'asset/resource'    // Webpack5内置,不再需要file-loader
      }
    ]
  }
}

执行顺序 :Loader 是链式调用,从右往左执行。例如 use: ['style-loader', 'css-loader'] 先执行 css-loader,再执行 style-loader。


2.2 Plugin 是什么?怎么用?

作用:执行更广泛的任务,打包优化、资源注入、环境变量定义等

Vue 项目典型 Plugin

Plugin 用途
DefinePlugin 定义全局环境变量(如 process.env.NODE_ENV)
HtmlWebpackPlugin 自动生成 index.html 并注入 bundle
MiniCssExtractPlugin 将 CSS 抽离成独立文件
VueLoaderPlugin 配合 vue-loader 必需的插件

使用方式(webpack.config.js)

javascript 复制代码
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { VueLoaderPlugin } = require('vue-loader')

module.exports = {
  plugins: [
    new VueLoaderPlugin(),                    // Vue必须
    new HtmlWebpackPlugin({                  // 生成HTML
      template: './public/index.html'
    }),
    new webpack.DefinePlugin({               // 定义环境变量
      __VUE_OPTIONS_API__: true,
      __VUE_PROD_DEVTOOLS__: false
    })
  ]
}

2.3 Loader vs Plugin 对比表

维度 Loader Plugin
定位 文件转换器 功能增强器
作用 单个文件(匹配 test 正则) 整个打包流程
执行时机 模块加载时 整个打包生命周期(hooks)
典型例子 vue-loader、babel-loader DefinePlugin、HtmlWebpackPlugin
配置位置 module.rules plugins

简单记忆

  • Loader:处理单个文件(什么文件用什么 loader)
  • Plugin:干 Loader 干不了的事(打包优化、变量注入等)

3. Vue2 vs Vue3 工程化差异对比

3.1 技术栈对比

维度 Vue2 + Webpack Vue3 + Vite
构建工具 Webpack 5 Vite 7
打包策略 全量打包 按需编译 + Rollup
配置复杂度 高(Loader + Plugin) 低(插件体系)
启动速度 慢(随项目增大) 快(基本恒定)
热更新 慢(重新打包) 快(只编译单个文件)
开发体验 一般 优秀
生态成熟度 最成熟 快速完善
类型支持 需额外配置 原生 TypeScript 支持

3.2 构建工具对比

Webpack

  • 机制:修改文件 → 重新打包该模块及其依赖链 → 浏览器替换
  • 速度:随项目增大变慢
  • 原理:需要把依赖关系算清楚

Vite

  • 机制:修改文件 → 编译该文件 → 浏览器通过 ESM 直接替换
  • 速度:恒快,只改一个文件
  • 原理:利用浏览器 ESM 天然隔离

3.3 为什么 Vite 快?

核心原因

  1. 省去打包环节:开发环境不需要打包,直接利用浏览器 ESM
  2. 按需编译:只编译浏览器请求的文件
  3. 原生 ESM:浏览器原生支持 ES 模块,无需打包
  4. 缓存机制:Vite 对编译结果做了缓存,二次访问更快

生产环境

  • Vite 使用 Rollup 打包
  • Rollup 充分利用 ES 模块的静态结构
  • 通过"作用域提升"消除无用代码
  • 输出更小、更高效的代码包

3.4 选型建议

场景 推荐方案
新项目 无脑 Vite
老项目 Webpack 够用就别折腾
需要极致兼容性(IE) 选 Webpack
追求开发体验 选 Vite

4. 当前项目 Vite 配置深度解析

4.1 项目基线

项目名称uni-platform-admin

技术栈 :Vue 3 + TypeScript + Vite + Element Plus + Tailwind CSS

Node 版本:^20.19.0 || >=22.12.0

构建工具:Vite 7.3.0(最新版本)


4.2 Vite 配置文件解析

目标文件vite.config.ts

typescript 复制代码
import {fileURLToPath, URL} from 'node:url'
import path from 'node:path'

import {defineConfig} from 'vite'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
import vueDevTools from 'vite-plugin-vue-devtools'
import tailwindcss from '@tailwindcss/vite'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import {ElementPlusResolver} from 'unplugin-vue-components/resolvers'
import GenerateComponentName from 'unplugin-generate-component-name/vite'
import routerNamedMapPlugin from './plugins/vite-plugin-router-named-map'
import {createSvgIconsPlugin} from 'vite-plugin-svg-icons'
import {lazyImport, VxeResolver} from 'vite-plugin-lazy-import'

export default defineConfig({
  plugins: [
    vue(),                                    // Vue 单文件组件支持
    vueJsx(),                                 // JSX 语法支持
    vueDevTools(),                            // Vue DevTools 集成
    tailwindcss(),                            // Tailwind CSS 支持
    AutoImport({                              // 自动导入 API
      resolvers: [ElementPlusResolver({importStyle: 'sass'})],
    }),
    Components({                              // 自动导入组件
      resolvers: [ElementPlusResolver({importStyle: 'sass'})],
    }),
    GenerateComponentName({                   // 自动生成组件名
      include: ['src/views/**/*.vue'],
      enter: [
        {
          include: ['src/views/**/*.vue'],
          exclude: ['src/views/**/components/**/*.vue'],
          genComponentName: ({attrName, dirname, originalName}) =>
            attrName ?? `${dirname}-${originalName}`,
        },
      ],
    }),
    routerNamedMapPlugin(),                   // 路由命名映射插件
    createSvgIconsPlugin({                    // SVG 图标插件
      iconDirs: [path.resolve(process.cwd(), 'src/assets/icons')],
    }),
    lazyImport({                              // 懒加载插件
      resolvers: [
        VxeResolver({libraryName: 'vxe-pc-ui'}),
        VxeResolver({libraryName: 'vxe-table'}),
      ],
    }),
  ],
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url)),  // 路径别名
    },
  },
  server: {
    host: '0.0.0.0',
    hmr: {
      overlay: true,                         // 错误覆盖层
    },
    watch: {
      ignored: ['**/src/types/router.d.ts'],  // 忽略路由类型文件监听
    },
  },
  css: {
    preprocessorOptions: {
      scss: {
        additionalData: `@use "@/assets/styles/element/variables.scss" as *;`,  // 全局注入 SCSS 变量
      },
    },
  },
})

4.3 插件体系详解

4.3.1 核心插件
插件 作用 对应 Webpack 概念
@vitejs/plugin-vue 处理 .vue 文件 vue-loader
@vitejs/plugin-vue-jsx 支持 JSX 语法 babel-loader + JSX 插件
vite-plugin-vue-devtools 集成 Vue DevTools 无对应(Vite 特有)
@tailwindcss/vite Tailwind CSS 支持 postcss-loader + tailwindcss
4.3.2 自动化插件
插件 作用 价值
unplugin-auto-import 自动导入 Vue API(ref、computed 等) 减少手动导入,提升开发效率
unplugin-vue-components 自动导入组件(Element Plus 等) 按需加载,减少包体积
unplugin-generate-component-name 自动生成组件名 统一命名规范
4.3.3 业务插件
插件 作用 自定义程度
routerNamedMapPlugin 路由命名映射 项目自定义
vite-plugin-svg-icons SVG 图标管理 配置化
vite-plugin-lazy-import VXE Table 懒加载 性能优化

4.4 路径别名配置

typescript 复制代码
resolve: {
  alias: {
    '@': fileURLToPath(new URL('./src', import.meta.url)),
  },
}

作用 :将 @ 映射到 src 目录,简化导入路径

对比 Webpack

javascript 复制代码
// Webpack
resolve: {
  alias: {
    '@': path.resolve(__dirname, 'src')
  }
}

使用示例

typescript 复制代码
import { formatDate } from '@/utils/date-helper'  // 使用别名
import { formatDate } from '../../utils/date-helper'  // 不使用别名

4.5 开发服务器配置

typescript 复制代码
server: {
  host: '0.0.0.0',          // 允许外部访问
  hmr: {
    overlay: true,          // 错误覆盖层
  },
  watch: {
    ignored: ['**/src/types/router.d.ts'],  // 忽略路由类型文件监听
  },
}

关键点

  • host: '0.0.0.0':允许局域网访问,方便移动端调试
  • hmr.overlay: true:错误时在浏览器显示覆盖层
  • watch.ignored:忽略某些文件的监听,避免不必要的 HMR

4.6 CSS 预处理器配置

typescript 复制代码
css: {
  preprocessorOptions: {
    scss: {
      additionalData: `@use "@/assets/styles/element/variables.scss" as *;`,
    },
  },
}

作用:全局注入 SCSS 变量,无需在每个文件中手动导入

对比 Webpack

javascript 复制代码
// Webpack
module.exports = {
  module: {
    rules: [
      {
        test: /\.scss$/,
        use: [
          'style-loader',
          'css-loader',
          {
            loader: 'sass-loader',
            options: {
              additionalData: `@import "@/assets/styles/element/variables.scss";`
            }
          }
        ]
      }
    ]
  }
}

5. 当前项目构建脚本分析

5.1 脚本清单

目标文件package.json

json 复制代码
{
  "scripts": {
    "dev": "vite",
    "build": "run-p type-check \"build-only {@}\" --",
    "build:prod": "VITE_APP_ENV=prod pnpm run build",
    "generator-api": "node scripts/generator-api.js",
    "deploy:uat": "./scripts/build.sh",
    "deploy:prod": "VERSION=prod ./scripts/build.sh",
    "preview": "vite preview",
    "test:unit": "vitest",
    "test:e2e": "playwright test",
    "build-only": "vite build",
    "type-check": "vue-tsc --build",
    "lint": "run-s lint:*",
    "lint:oxlint": "oxlint . --fix",
    "lint:eslint": "eslint . --fix --cache",
    "format": "prettier --write --experimental-cli src/",
    "prepare": "husky"
  }
}

5.2 脚本解析

脚本 作用 工具
dev 启动开发服务器 Vite
build 类型检查 + 构建 vue-tsc + Vite
build:prod 生产环境构建 Vite + 环境变量
preview 预览构建结果 Vite
test:unit 单元测试 Vitest
test:e2e E2E 测试 Playwright
type-check TypeScript 类型检查 vue-tsc
lint 代码规范检查 oxlint + eslint
format 代码格式化 Prettier

5.3 构建流程

开发环境

bash 复制代码
pnpm run dev
  1. 启动 Vite 开发服务器
  2. 按需编译请求的文件
  3. HMR 热更新

生产环境

bash 复制代码
pnpm run build:prod
  1. 设置环境变量 VITE_APP_ENV=prod
  2. 执行类型检查 vue-tsc --build
  3. 执行构建 vite build
  4. 使用 Rollup 打包优化

6. Webpack vs Vite 配置对比

6.1 Vue 文件处理

Webpack

javascript 复制代码
module.exports = {
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader'
      }
    ]
  },
  plugins: [
    new VueLoaderPlugin()
  ]
}

Vite

typescript 复制代码
import vue from '@vitejs/plugin-vue'

export default defineConfig({
  plugins: [vue()]
})

对比:Vite 配置更简洁,一行插件搞定。


6.2 自动导入

Webpack :需要配置 babel-plugin-import 或手动导入

Vite

typescript 复制代码
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import {ElementPlusResolver} from 'unplugin-vue-components/resolvers'

export default defineConfig({
  plugins: [
    AutoImport({
      resolvers: [ElementPlusResolver({importStyle: 'sass'})],
    }),
    Components({
      resolvers: [ElementPlusResolver({importStyle: 'sass'})],
    }),
  ]
})

优势:Vite 的 unplugin 生态更完善,配置更简单。


6.3 路径别名

Webpack

javascript 复制代码
const path = require('path')

module.exports = {
  resolve: {
    alias: {
      '@': path.resolve(__dirname, 'src')
    }
  }
}

Vite

typescript 复制代码
import {fileURLToPath, URL} from 'node:url'

export default defineConfig({
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url))
    }
  }
})

对比:Vite 使用 ESM 语法,更符合现代标准。


7. 当前项目工程化亮点

7.1 自动化程度高

  • 自动导入 API :无需手动导入 refcomputed
  • 自动导入组件:Element Plus 组件按需加载
  • 自动生成组件名:统一命名规范
  • 自动类型检查 :构建前自动执行 vue-tsc

7.2 性能优化

  • 懒加载:VXE Table 按需加载
  • SVG 图标优化 :使用 vite-plugin-svg-icons 统一管理
  • HMR 优化:忽略不必要的文件监听
  • 缓存机制:Vite 内置缓存,二次访问更快

7.3 开发体验

  • 错误覆盖层:HMR 错误时在浏览器显示
  • Vue DevTools 集成:开发调试更方便
  • 路径别名:简化导入路径
  • SCSS 全局变量:无需手动导入

7.4 代码质量

  • 类型检查 :构建前自动执行 vue-tsc
  • 代码规范:oxlint + eslint 双重检查
  • 代码格式化:Prettier 统一风格
  • Git Hooks:husky + lint-staged 提交前检查

8. 实战建议

8.1 新项目选型

推荐 Vite

  • 开发体验更好(启动快、热更新快)
  • 配置更简洁
  • 生态完善(unplugin 系列)
  • 性能更优(原生 ESM)

8.2 老项目迁移

迁移步骤

  1. 安装 Vite 和相关插件
  2. webpack.config.js 转换为 vite.config.ts
  3. 替换 Loader 为对应的 Vite 插件
  4. 替换 Plugin 为对应的 Vite 插件
  5. 测试构建和开发环境

注意事项

  • 某些 Webpack 插件可能没有对应的 Vite 插件
  • 需要调整构建脚本
  • 需要调整路径别名配置

8.3 性能优化建议

开发环境

  • 使用 ignored 忽略不必要的文件监听
  • 开启 HMR 错误覆盖层
  • 使用路径别名简化导入

生产环境

  • 使用 Rollup 的 Tree-shaking
  • 按需加载第三方库
  • 使用 CDN 加速静态资源
  • 开启 Gzip 压缩

9. 总结

9.1 Vue2 vs Vue3 工程化核心差异

维度 Vue2 + Webpack Vue3 + Vite
构建工具 Webpack 5 Vite 7
打包策略 全量打包 按需编译 + Rollup
配置复杂度 高(Loader + Plugin) 低(插件体系)
启动速度 慢(随项目增大) 快(基本恒定)
热更新 慢(重新打包) 快(只编译单个文件)
开发体验 一般 优秀
生态成熟度 最成熟 快速完善
类型支持 需额外配置 原生 TypeScript 支持

9.2 当前项目工程化特点

  • 构建工具:Vite 7.3.0(最新版本)
  • 插件体系:12 个插件,覆盖开发全流程
  • 自动化:自动导入、自动生成组件名、自动类型检查
  • 性能优化:懒加载、SVG 优化、HMR 优化
  • 代码质量:类型检查、代码规范、代码格式化

9.3 选型建议

  • 新项目:无脑 Vite
  • 老项目:Webpack 够用就别折腾
  • 追求开发体验:选 Vite
  • 需要极致兼容性:选 Webpack

10. 参考资源

相关推荐
swg3213211 小时前
Redis实现主从选举
java·前端·redis
英俊潇洒美少年1 小时前
前端核心性能指标全解(CWV三大指标+辅助指标、检测方式、优化、面试背诵)
前端
云水一下1 小时前
Vue.js从零到精通系列(八):项目实战——构建一个完整的电商后台管理系统
前端·javascript·vue.js
Csvn1 小时前
Vue3 响应式陷阱:解构赋值后页面不动了?Proxy 的"隐形成员"在搞鬼
前端·vue.js
LAM LAB2 小时前
【Web】网页如何模拟移动端获取定位\定位模拟测试
开发语言·前端·javascript
yunceqing2 小时前
从Excel调度到TMS平台:物流软件开发避坑清单
大数据·前端·网络·人工智能·excel·推荐算法
IT_陈寒2 小时前
Redis主从切换把我坑惨了,这份血泪史你最好看看
前端·人工智能·后端
weixin_471383032 小时前
Taro-04-网络请求
前端·javascript·taro
Doker 多克2 小时前
Spring AI Alibaba—快速构建ReactAgent
java·开发语言·前端·ai编程