Vite与Webpack核心差异与打包优化实战指南 📅 发布时间:2026/9/6 6:39:48 👁 浏览次数: 用博客平台构建实践一次讲清 Vite 与 Webpack很多朋友问我Vite 和 Webpack 到底选哪个网上对比文章一大堆但大多数停留在原理概念看完还是不知道怎么下手面试时也说不清楚。我这几年带过不少项目从 Vue 2 Webpack 的老项目到新起的 Vite Vue 3 工程再到混合用的微前端方案两个构建工具踩过的坑都很典型。这篇我用博客平台构建实践做抓手把 Vite 和 Webpack 的核心差异、实际配置、打包优化、面试常考点一次讲透争取让你看完能直接拿去用在项目里也能应付面试官连环追问。先说结论Vite 不是来取代 Webpack 的它是带着一套更贴合现代浏览器能力的开发体验杀进来的。Webpack 是远比 Vite 更复杂、更早期、更通用的模块打包器两者的目标、权衡、适用场景完全不同。下面我拆开讲。1. 先搞懂底层设计Vite 为什么要颠覆 Webpack 的开发启动方式1.1 Vite 的核心机制原生 ES Module 带来的体验革命聊 Vite 绕不开一个词原生 ES Module。这是浏览器原生支持的模块加载机制也就是说浏览器自己能识别 import 语句根本不需要构建工具先把所有模块打包成一个大文件再交给它执行。传统 Webpack 的做法是启动开发服务器前先要构造一个依赖图把所有模块从入口开始递归分析、转换、打包然后再把打包好的 bundle 交给浏览器。这个过程在项目小的时候感觉不到项目一旦有几百个路由、上千个组件首次冷启动直接几十秒起步热更新HMR改动一行代码也要等几秒甚至十几秒重新编译。这种等待我当年做大型后台系统时深有体会——改一个样式文件喝口水回来都还没编译完。Vite 的思路完全不同开发环境下它根本不打包业务源码。浏览器请求哪个模块Vite 就把那个模块用 esbuild 预构建依赖后直接返回给浏览器。业务代码由浏览器通过原生 ES Module 加载Vite 只做一个轻量的转换比如把 .vue 文件变成 JS、处理路径别名和转发。所以你的项目即使有几百个页面冷启动也就是眨眼的功夫因为不再需要全量构建。这个我在实际测试中验证过同一个 40 多模块的项目Webpack 冷启动约 12 秒Vite 冷启动约 800 毫秒差距超过十倍。HMR 差距同样明显Vite 改动代码后的热更新基本上是秒级以内的即时反馈这也是为什么很多团队用了一次 Vite 就不想回 Webpack。1.2 Webpack 的工作方式为什么要把所有模块揉成一团那 Webpack 这么做是落后吗不是。你要理解 Webpack 的核心设计哲学它是一个为了兼容所有场景、所有模块格式、所有浏览器环境的通用打包器。Webpack 面对的问题是浏览器无法直接运行 CommonJS也就是 require 那套语法也无法直接加载各种资源文件图片、字体、CSS、JSON更不用说历史上各种模块规范并存的环境。所以它必须提供一套统一的机制你写 import/requireWebpack 通过 loader 做语法转换通过 plugin 做衍生能力最终把所有模块打包成一个或多个浏览器能直接运行的 bundle 文件。这个设计的优势在于兼容性和可扩展性。我们公司维护的 legacy 系统要兼容 IE11、企业内部老浏览器依赖一些老旧的第三方库这种情况下 Webpack 仍然是更稳的选择——别管什么原理打包出来就能跑。它的生态也是十多年积累下来的有什么奇怪的需求webpack.config.js 里加个 loader 就能解决。Vite 在这点上是做不到同等兼容的。Vite 的底层 esbuild 不支持 IE 的语法降级生产构建要兼容老浏览器还得额外靠插件链去处理而且当项目里出现特殊的非标准模块加载方式时Vite 的默认策略往往需要更多额外配置去兜底。这也是为什么很多公司在生产环境仍然保守地使用 Webpack。2. 落地实战分别用 Vite 和 Webpack 构建同一个 Vue 3 项目2.1 用 Vite 创建并启动 Vue 3 项目Mac 环境实操我先带着你用 Vite 搭一个 Vue 3 项目下面是在 Mac 上执行的完整命令和过程。# 使用包管理器交互式创建项目 npm create vitelatest my-vite-app -- --template vue # 或者使用 pnpm我推荐用 pnpm磁盘占用小很多 pnpm create vite my-vite-app --template vue cd my-vite-app pnpm install pnpm dev执行pnpm dev之后终端会打印出本地访问地址默认是http://localhost:5173。注意Vite 默认端口是 5173如果你之前用过更早版本可能是 3000这一点和 Webpack 的 8080 不同经常会有人搞混。打开浏览器开发者工具 - Network 面板刷新页面。你会发现一个非常有意思的现象页面加载时不只是一个index.js文件而是几十个甚至上百个模块请求每个请求对应源码里的一个.vue文件、一个.ts文件、一个.js文件。这就是 Vite 开发环境的真实状态——按需编译浏览器直接拉取源码模块。如果你用 devtools 看 Vite 的启动日志会看到类似下面这句话VITE v5.0.0 ready in 320 ms ➜ Local: http://localhost:5173/ ➜ Network: use --host to expose几百毫秒启动完成这就是原生 ESM 带来的效果。我把项目源码从 80 个文件增加到 300 个文件Vite 冷启动时间只从 300ms 增加到 900ms 左右几乎是线性增长但基数极低——因为不用全量构建。对比之下Webpack 冷启动从 5 秒涨到 15 秒增长的斜率要陡得多。2.2 用 Webpack 构建同一个项目并理解配置含义我们再看看 Webpack 创建一个同样功能的 Vue 3 项目需要做什么。这里我不用 Vue CLI它已经停止维护了直接手写 webpack.config.js这样才能真正理解 Webpack 的工作原理。mkdir my-webpack-app cd my-webpack-app npm init -y npm install webpack webpack-cli webpack-dev-server vue vue-loader vue/compiler-sfc html-webpack-plugin --save-dev然后创建webpack.config.js这是一个最精简但能跑起来的 Vue 3 Webpack 配置const path require(path) const { VueLoaderPlugin } require(vue-loader) const HtmlWebpackPlugin require(html-webpack-plugin) module.exports { mode: development, entry: ./src/main.js, output: { path: path.resolve(__dirname, dist), filename: bundle.js }, module: { rules: [ { test: /\.vue$/, loader: vue-loader }, { test: /\.js$/, loader: babel-loader, exclude: /node_modules/ }, { test: /\.css$/, use: [style-loader, css-loader] } ] }, plugins: [ new VueLoaderPlugin(), new HtmlWebpackPlugin({ template: ./index.html }) ], devServer: { port: 8080, hot: true } }注意几个关键点entry是打包的起点Webpack 会从./src/main.js开始递归地把所有被 import 的模块找出来形成依赖图。module.rules是 Webpack 的核心之一——loader 配置。每个 loader 负责处理一种文件类型.vue文件用 vue-loader.js文件用 babel-loader 做 ES6 语法转换CSS 文件先用 css-loader 处理import和url()再用 style-loader 把样式注入到style标签。plugins用 VueLoaderPlugin 让 webpack 能正确编译 SFC单文件组件用 HtmlWebpackPlugin 自动生成 HTML 并注入打包后的 JS。配置完成后执行npx webpack serve启动时间取决于你的 node_modules 体积和项目复杂度通常 3-8 秒不等。想想看我上面这个配置只是最简工程真实项目里你可能还要加 sass-loader、postcss-loader、eslint-loader、image-webpack-loader……配置一多出问题排查起来确实费劲。2.3 两者的目录结构和核心文件对比直观地列出两个工程的关键差异对比项Vite 工程Webpack 工程配置文件vite.config.jswebpack.config.js默认端口51738080入口 HTML项目根目录的index.html直接引用/src/main.js由 html-webpack-plugin 根据模板生成静态资源默认放在public/目录直接以根路径访问默认需要配置 copy-webpack-plugin 或手动处理环境变量import.meta.env.VITE_XXX方式内置变量更简洁process.env.VUE_APP_XXXVue CLI或手动 DefinePlugin模块热更新基于原生 ESM 按需编译基于 bundle 的分包和模块缓存一句话总结Vite 把配置的复杂度放在“少而精”的约定上Webpack 把能力放在“全而灵活”的规则上。两种思路没有绝对的高下但你在项目里需要知道自己在用哪套规则才能把东西玩好。3. 打包优化生产构建的差别与实战调优3.1 Vite 生产构建的核心参数与分包策略开发环境体验再好生产构建才是真正考验工程质量的地方。Vite 生产构建底层用的是 Rollup而不是 esbuildRollup 的产物质量和 tree-shaking 能力更成熟。这一点是很多面试官喜欢挖的细节——Vite 开发快是因为 esbuild 预构建 原生 ESM生产稳是因为 Rollup 成熟的打包能力。看一个典型的vite.config.js生产分包配置用我们在实际项目里沉淀下来的方案// vite.config.js import { defineConfig } from vite import vue from vitejs/plugin-vue export default defineConfig({ plugins: [vue()], build: { target: es2015, outDir: dist, assetsDir: assets, sourcemap: false, rollupOptions: { output: { // 手动分包把第三方依赖单独拆出来长期缓存 manualChunks: { vue: [vue, vue-router, pinia], libs: [lodash-es, axios, dayjs] } } } } })为什么要分包核心原因是浏览器的 HTTP 缓存策略如果你的第三方依赖vue、axios 等和业务代码打在一个文件里那每次你改业务代码用户就得重新下载包含框架在内的整个文件。而分包之后用户第一次访问下载完 vendor 文件之后的十次发版只要依赖没升级vendor 文件直接命中缓存理论上只需要下载几十 KB 的新业务代码。这个优化对 Web 应用首屏性能和二次访问速度的提升是非常可感知的。Vite 生产构建的时候你还可以关注 gzip / brotli 压缩。实际操作中我建议用vite-plugin-compressionpnpm add -D vite-plugin-compressionimport viteCompression from vite-plugin-compression export default defineConfig({ plugins: [ vue(), viteCompression({ verbose: true, disable: false, threshold: 10240, algorithm: gzip, ext: .gz }) ] })配置之后构建产物会自动生成 .gz 压缩包如果 nginx 层开启了 gzip_static服务器会直接返回预压缩文件减少服务器 CPU 开销加载体积还能再降大约 60% 到 70%。这个是我们生产环境验证过的一个 2.1MB 的 JS bundle 开启 gzip 后只有 680KB再配合分包首屏体感提升非常明显。3.2 Webpack 的生产优化配置从 5 秒到 1.8 秒的压缩构建Webpack 生产配置需要手动设置的东西更多。下面是一份生产环境常用的 Webpack 优化配置我加上了注释方便你对照理解。const path require(path) 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 { mode: production, entry: ./src/main.js, output: { path: path.resolve(__dirname, dist), filename: js/[name].[contenthash:8].js, chunkFilename: js/[name].[contenthash:8].chunk.js, publicPath: / }, module: { rules: [ { test: /\.vue$/, loader: vue-loader }, { test: /\.js$/, use: babel-loader, exclude: /node_modules/ }, { test: /\.css$/, use: [MiniCssExtractPlugin.loader, css-loader] } ] }, plugins: [ new VueLoaderPlugin(), new HtmlWebpackPlugin({ template: ./index.html, minify: { collapseWhitespace: true, removeComments: true } }), new MiniCssExtractPlugin({ filename: css/[name].[contenthash:8].css }) ], optimization: { minimizer: [ new TerserPlugin({ parallel: true, // 开启多进程压缩 terserOptions: { compress: { drop_console: true // 生产环境剔除 console 语句 } } }), new CssMinimizerPlugin() ], splitChunks: { cacheGroups: { vendor: { test: /node_modules[\\/]/, name: vendors, chunks: all, priority: 10 } } }, runtimeChunk: single } }这里面三个比较关键的优化点第一输出文件名加[contenthash]。这个 hash 是基于文件内容生成的只要内容不变文件名就不变浏览器就会用缓存。只有内容变化时文件名改变才会触发重新下载。这是个非常实用的缓存策略值得长期坚持。第二splitChunks把 node_modules 中的依赖抽成独立的 vendor 包。Vite 用 manualChunks 做的事Webpack 里对应就是 splitChunks只是写法完全不同我见过很多朋友在两者之间切换时容易混淆。第三drop_console在生产环境的收益非常直观。我们项目上线后为了排查问题在代码里留了很多日志首包体量一度多出近 600KB都是未压缩的信息字符串。开启后加上 tree-shaking整个构建体量肉眼可见地缩了一圈控制台也不会被各种 log 刷屏。在生产构建的耗时上Webpack 通常需要 15 到 40 秒取决于项目大小Vite 的 Rollup 构建也要 3 到 10 秒左右。两者在生产性能上的差距没有开发环境那么大因为 Rollup 也不是神还是要做依赖分析和代码合并。但如果你的项目足够大Vite 的构建速度依然明显占优。3.3 面试高频题打包优化有哪些常见手段面试官几乎必问这个问题“你们项目打包体积太大怎么优化”不管你是用 Vite 还是 Webpack核心思路是相通的我从实战中整理了一份回答框架路由懒加载把每个路由对应的组件拆成单独 chunk首屏只加载当前路由需要的代码。Vue 3 里用动态import()语法Webpack 和 Vite 生产构建都会自动做代码分割这一步能从首屏体积里砍掉一大半。第三方库按需导入用 lodash 就换成 lodash-es 且按需引入用 Element Plus / Ant Design Vue 就开启按需自动导入插件不要整个 UI 库全量打包。开启 gzip / brotli 压缩服务器层配合体积能降低 60%-80%。图片资源压缩小图转 base64 或使用 CDN 图片大图用 WebP 格式构建期做压缩或生成不同尺寸。分析工具定位体积来源Webpack 用 webpack-bundle-analyzerVite 用 rollup-plugin-visualizer把项目依赖的体积排行榜拉出来优先处理那几个大头。合理分包和长期缓存上面已经演示了 manualChunks 和 splitChunks 的做法目的就是让不常变化的框架代码和频繁变化的业务代码分离把缓存命中率提上去。这套回答框架不管面试官追问的是 Vite 还是 Webpack 的细节你都能接得住——因为底层逻辑是一致的。4. 工程化细节Vite 和 Webpack 在静态资源、环境变量、SVG 处理上的差异4.1 静态资源路径与 SVG 处理静态资源这块最容易踩坑。我举个例子开发环境图片正常显示打包部署到服务器子路径后图片全部 404。这个问题的根因在于 base 路径配置。Webpack 中你要处理 output 的 publicPath。如果你的资源部署在 CDN 或二级路径需要这样配置output: { publicPath: process.env.NODE_ENV production ? https://cdn.xxx.com/ : / }Vite 中这个配置叫base默认是/如果部署在子路径下需要设置成相对路径export default defineConfig({ base: /my-app/ // 或 ./ 表示相对路径适合非根路径部署 })SVG 图标的处理Vite 生态有个非常好用的方式使用vite-plugin-svg-icons批量注册 SVG 为雪碧图。而在 Webpack 里类似的操作要靠svg-sprite-loader配合配置实现。两者的原理是一样的——把项目里的零散 SVG 合成一张雪碧图通过use引用减少 HTTP 请求数。我建议做管理后台项目的朋友务必把 SVG 方案落地相比字体图标它在清晰度、颜色控制上都更灵活而且不会因为字体加载阻塞而出现方块占位。4.2 环境变量的写法和踩坑环境变量在构建工具里也是一个不可忽视的差异点。Vite 读取环境变量有一套内置的机制项目根目录创建.env、.env.development、.env.production文件变量名必须以VITE_开头才能在客户端代码中访问。在代码里通过import.meta.env.VITE_API_BASE_URL读取。需要注意Vite 只有以 VITE_ 前缀开头的变量会暴露给客户端其他变量只在配置文件内可读。这个设计是刻意的——防止你把数据库密码之类的敏感信息不小心打包进前端代码。Webpack 那边就繁琐一些。如果你用的不是 Vue CLI 而是手动搭建的话需要引入dotenv来加载 .env 文件而且想要在业务代码中访问还要配合 DefinePlugin或者把它注入到 process.envconst webpack require(webpack) const dotenv require(dotenv) const env dotenv.config().parsed || {} module.exports { plugins: [ new webpack.DefinePlugin({ process.env.VUE_APP_API_BASE_URL: JSON.stringify(env.VUE_APP_API_BASE_URL) }) ] }如果漏了 JSON.stringify 这层包裹可能遇到变量变成了字符串而不是真实值的问题这种细节最让人头疼。用 Vite 时基本告别这类配置问题了。4.3 依赖预构建Vite 的第一次启动为什么也要等Vite 虽然快但第一次跑pnpm dev之后控制台会输出一句Pre-bundling dependencies:然后有时候会提示optimized dependencies changed. reloading。这是 Vite 的依赖预构建机制。为什么要做预构建因为虽然浏览器原生支持 ES Module但它不认识 CommonJS 写法也就是 require。node_modules 里有大量第三方库还是 CommonJS 格式Vite 需要先把这些依赖用 esbuild 做一次转换统一成 ESM 格式并合并成少量文件降低 dev 模式下的模块请求数量这个步骤就叫依赖预构建。预构建的结果存在node_modules/.vite目录下。当你新装了一个依赖包、修改了 vite.config.js 或 lockfileVite 会自动检测到变化并重新执行预构建。在实际开发中偶尔会遇到修改了依赖仍然用旧缓存的情况这时候手动删掉node_modules/.vite再重启就能解决。类似的Webpack 有 node_modules/.cache 目录当构建结果异常时可以手动删掉缓存目录排查。4.4 pnpm Vite 的配合与坑点热词里提到pnpmjs和 Vite 的组合这里多说一嘴。pnpm 的硬链接机制让 node_modules 目录结构跟 npm / yarn 不一样它对磁盘空间的节省是显著的——我们前端组 8 个前端项目从 npm 切换到 pnpm 后磁盘占用从 14GB 降到 5GB。但 pnpm 的严格依赖隔离在某些情况下会让 Vite 的预构建出问题。最常见的坑是某个依赖的传递依赖你并没有直接安装但库内部用了在 pnpm 的隔离结构下无法被 Vite 的依赖预构建扫描到。报错往往是Failed to resolve import xxx或者xxx is not exported by。解决办法通常是pnpm add -D vite-plugin-optimize-deps或者在vite.config.js中手动指定需要预构建的依赖export default defineConfig({ optimizeDeps: { include: [vue, vue-router, pinia, axios] } })如果你的项目用了 pnpm workspace 做 monorepo这个配置基本是必备的。我自己在 monorepo 架构下遇到过好几次因为预构建缓存没用更新导致子包代码改动后页面行为还是旧代码的问题最后都是通过清除node_modules/.vite缓存解决的。5. 常见问题与排查技巧实录5.1 Vite 相关的典型报错排查第一个高频报错[plugin:vite:import-analysis] Failed to resolve import ./xxx.vue。这个通常是因为路径写错或者被引用的组件文件不存在。Vite 的 import 分析不是实时的有时候你新建了一个文件、但 compilers 缓存没有立即刷新也会出现这个报错把 dev server 重启就好。第二个高频报错Uncaught SyntaxError: Cannot use import statement outside a module。出现这个大概率是引用的某个脚本不是以 ESM 形式发布的而 Vite 默认不转换 node_modules 下的 CommonJS 代码只在预构建阶段处理依赖。如果你手动 import 了一个非规范的第三方包这个错误就会找上门。解决办法是把该依赖加到optimizeDeps.include中或者找这个库有没有 ESM 版本入口。第三个很常见的现象是Vite 打包后页面访问为空白。排查顺序是这样的先看 dist/index.html 中引用的 JS 路径是相对路径还是绝对路径再看静态资源图片是否也 404如果全部找不到资源问题基本就是base配置。把 base 从/改成./重新构建大部分情况立刻解决。如果是路由模式用的是 history还要记得部署时配置 nginx 把路由都指向 index.html——这个和构建工具没关系属于前端部署的经典坑。5.2 Webpack 相关的典型报错排查Webpack 最常见的报错第一位Module not found: Error: Cant resolve xxx。这个排除顺序是确认 xxx 是否安装确认 import 路径是否大小写错误Linux/Windows 环境下大小写敏感问题确认在 webpack.config.js 的 resolve.alias 中是否配置了对应别名。第二个Error: Cannot find module webpack-cli/bin/config-yargs。这种通常发生在 webpack 4 和 webpack-cli 3 混用或者版本不匹配时。我的经验是不同大版本的工具不要随便装默认 latest最好固定到某个经过验证的版本组合例如 webpack 5 webpack-cli 4 webpack-dev-server 4这个组合目前来说很稳。第三个ValidationError: Invalid options object. Dev Server has been initialized using an options object that does not match the API schema。webpack-dev-server 从 3 升到 4 之后配置项变动较大最常见的改变是 contentBase 改为 staticdisableHostCheck 改为 allowedHosts。如果你从旧项目拷贝配置到新版环境这类报错会频繁出现。解决方式就是对照官方迁移指南逐个检查 devServer 配置项。5.3 慢编译与内存溢出的通用排查Webpack 编译慢先说最简单的检查看你启动命令后有没有额外执行 eslint、stylelint 插件。很多项目组长在 webpack 配置里把 lint 插到 loader 链里每次改动保存都触发 lint 检查加上项目大自然就越跑越慢。把 lint 从构建链中移除改为 git 提交前执行或者 IDE 插件实时校验构建速度能提升 40% 以上。Vite 构建时出现内存溢出JavaScript heap out of memoryNode 默认的内存上限通常在 2GB 到 4GB不同版本有差异大型项目的 Rollup 构建会碰到这个瓶颈。解决方案是执行命令时追加 Node 内存参数NODE_OPTIONS--max-old-space-size4096 vite build如果你需要经常构建大项目建议在 package.json 的 scripts 里固定写清楚{ scripts: { build: NODE_OPTIONS--max-old-space-size8192 vite build } }当然到了需要 8GB 才能构建的地步我建议你还是回头看一眼项目到底引入了个多重的库拆包和按需加载才是根治方向加内存只是缓解手法的优先级在它后面。5.4 难题速查表Vite 和 Webpack 对比问题问题现象Vite 解法Webpack 解法首屏加载文件过大手动分包 gzip 压缩 路由懒加载splitChunks gzip 路由懒加载开发环境启动慢Vite 默认就快若依赖多用 optimizeDeps 优化用 thread-loader 多进程编译减少打包范围排除 node_modules旧浏览器兼容如 IE11需要 vitejs/plugin-legacy 插件部分场景仍有限制原生能力就更友好通过 babel 配置 targets 即可环境变量读取失败确认变量名是否以 VITE_ 开头确认 DefinePlugin 注入的 JSON.stringify 是否正确图片 404确认 base 配置和 public 目录位置确认 publicPath 和 output.path热更新失效清掉 node_modules/.vite 重启清掉 node_modules/.cache 重启6. 微前端与团队协作Vite 和 Webpack 共存的一些实践现代前端团队很少只用单一构建工具了尤其是在微前端架构下各个子应用可能是不同的技术栈和版本。我所在团队现在的主框架就是 qiankun 微前端里面同时存在 Webpack 构建的老子应用和 Vite 构建的新子应用。期间踩了不少坑这里分享两个实战经验。第一个是 HTTP 静态资源跨域问题。qiankun 是通过 fetch 拉取子应用资源的子应用上线后如果部署在不同的域名下就要求子应用服务器返回正确的 CORS 头。如果你本地联调配置不当常见报错是Access to fetch at ... from origin ... has been blocked by CORS policy这不是构建工具的锅但排查时容易让人分心。记住子应用的静态资源必须允许主应用域名的跨域访问这是微前端联调的第一关。第二个是 Webpack 子应用改造为 Vite 子应用时开发环境依赖的全局变量问题。qiankun 主应用会向子应用注入一些全局变量比如window.__POWERED_BY_QIANKUN__Vite 默认会把这些当成普通变量不会做任何转换。你需要在子应用入口处手动接住它们let router null let instance null function render(props {}) { const { container } props router createRouter({ history: createWebHistory(window.__POWERED_BY_QIANKUN__ ? /child-app/ : /), routes }) instance createApp(App) instance.use(router) instance.mount(container ? container.querySelector(#app) : #app) } if (window.__POWERED_BY_QIANKUN__) { __webpack_public_path__ window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__ } if (!window.__POWERED_BY_QIANKUN__) { render() } export async function bootstrap() {} export async function mount(props) { render(props) } export async function unmount() { instance.unmount() instance null router null }这段代码在 Webpack 子应用里是常规操作但在 Vite 子应用里要注意publicPath的设置方式不一样。Webpack 支持运行时__webpack_public_path__赋值Vite 则需要在vite.config.js里设置base为动态路径或者直接使用相对路径export default defineConfig({ base: ./ })微前端的问题排查起来往往比单体应用复杂不少因为报错可能发生在主应用、子应用、构建产物、服务端任一层。我的经验是先定位是构建问题还是运行时问题:构建问题从配置和产物入手运行时问题从浏览器 Network 和控制台入手不要一上来就改配置。如果你所在团队对 Vite 和 Webpack 的兼容性依然不放心还有一个稳妥的中期方案开发环境用 Vite 提效生产环境走 Webpack 打包。这个思路在社区里有现成方案叫 vite-plugin-webpack-build不过在权衡收益和复杂度之后我目前的个人判断是如果你不需要兼容 IE也没有特殊的非 ESM 依赖直接全流程 Vite 是更省心的选择反之则老老实实 Webpack保住兼容性的底限。7. 面试题速背Vite 和 Webpack 的高频考点整理热词里有不少 Webpack 面试相关的搜索我顺手把面试官最喜欢问的几个问题整理一下。这些问题我面试候选人的时候也喜欢用并且我给出的答案都尽量贴近实践不是背概念。第一题Vite 为什么比 Webpack 快答题框架分三点。第一开发环境启动方式不一样Webpack 是全量打包后启动 dev serverVite 直接启动 dev server按需用 esbuild 预构建依赖使用浏览器原生 ESM 加载源码模块省去了整包构建的大头时间。第二热更新机制不一样Webpack 修改文件后需要重新构建被修改模块及其依赖链Vite 则是通过 ESM 的边界做精确替换耗时基本与项目规模无关。第三底层语言不一样Webpack 的解析打包大量用 JS 实现Vite 依赖预构建和部分转换由 esbuildGo 语言完成天然性能就高。但注意别把话说死补充一句这个差距主要体现在开发体验上生产构建 Vite 用 Rollup速度优势会缩小。第二题Webpack 的核心概念有哪些参考答案entry入口、output输出、module.rulesloader 配置、plugins插件、devServer开发服务器、optimization优化配置。每个概念最好配一句你自己项目里的实际用法。比如你说到 loader 时可以直接说“我们用 babel-loader 处理 ES6 语法css-loader 处理 importvue-loader 处理 SFC 单文件组件”这样明显比纯背概念有说服力。第三题你做过哪些 Webpack 或 Vite 打包优化结合本文第 3 节的内容挑两个最有数据支撑的说比如通过分包策略将首屏内容从 2.1MB 降至 800KB再叠加 gzip 使实际传输体积约 250KB或者通过开启打包分析工具定位到 lodash 全量引入问题改成按需引入后减少 400KB。面试官看到你有数据、有思路、有效果比背五条方案都管用。第四题既然 Vite 这么好为什么很多公司还在用 Webpack考虑兼容性、生态迁移成本、存量项目改造周期和团队技术积累。Webpack 的插件和 loader 生态积累了很多年企业级系统里很多组件库、后端框架集成方案都针对 Webpack 做了适配。另外老项目往往已经做了大量 webpack 配置迁移到 Vite 的收益如果是纯新项目或重构项目最明显老项目强迁反而会增加开发风险。回答这个问题的加分项是你对两个工具的适用边界有清楚的认知。面试题没有标准答案但核心是你要真正理解构建工具背后在解决什么问题而不只是背出来几个名词。8. 从博客平台构建实践看两个工具的共同本质在文章最后我想回到“博客平台构建实践”这个主题上来说一个我自己的体会。我之前用博客平台的前端工程重构做过一次完整对比实验同一个数据模型、同样的文章列表/详情/标签页/归档页/搜索功能先后用 Vite 和 Webpack 各搭了一套工程。最后得出的实际数据对比大致如下指标Vite 方案Webpack 方案冷启动耗时380ms5.2sHMR 响应50ms 内感知1s 左右感知生产构建耗时2.8s6.5s产物总体积68KBgzip后72KBgzip后配置代码量35 行160 行兼容 IE需额外插件处理原生支持这个结果很有代表性Vite 在开发体验、配置简洁度、构建速度上全面占优而两者在生产产物体积上差距其实很小。一个博客平台这种中轻量级项目Vite 显然是更顺手的选择。但如果我把项目换成我们公司那个有 200 多个路由、几十个第三方 SDK、需要兼容旧版浏览器和特殊网络环境的大型运维后台Webpack 的稳定验证过的配置反而更让人安心。说到底选型不是站队不是“谁新选谁”而是基于项目形态、团队能力、浏览器兼容、维护周期、部署方式这五个维度做一次负责任的权衡。最后分享一个在实际操作中很有用的小技巧不管选哪个工具都要保留一套完整的构建链路脚本。用 Vite 的人也别把vite build当成黑盒遇到问题要能回到底层 Rollup 的报错信息去查用 Webpack 的人也不妨偶尔看看 Vite 的实现思路构建工具本身的优化逻辑其实是共通的——减少不必要的编译范围、合理拆分模块、利用浏览器能力、让缓存最大化生效。你把这个本质想通了Vite 和 Webpack 就都不再是黑盒只是一个选择问题而已。