1. import.meta.env vs process.env
Map env usage deliberately; a half-migrated define setup is a source of “works in local, 500s in preview.”
2. Aliases, static assets, and public/
Deep-link and PDF flows often break on base path changes. Verify staging URLs match production routing.
3. Use faster CI as the KPI
Time saved in npm test and typecheck loops funds more migration perf work. Measure before/after pipeline duration.
4. Sequencing: Vite first, Vue 3 first, or both at once?
The biggest decision is not how you switch bundlers, it is when. Doing both moves at once feels efficient on a slide. In a real codebase it usually means every regression has two suspects, and your git bisect sessions get long. We sequence based on what is actually blocking the team, not on which tool is trending.
Path A — Stabilize Vue 3 on Webpack, then Vite
Best when your existing Webpack config is mature, has custom loaders or DLL plugins, and your team’s muscle memory is in vue.config.js. You finish the framework migration on a known-good build, prove parity in production, then schedule the bundler move as its own ticket. Pairs well with a strangler fig rollout where Vue 3 routes are dropped in incrementally.
Path B — Vite first on the Vue 3 branch
Best when dev server cold-start is the bottleneck for the migration team itself, or your plugins (Pinia, Vue Router 4, Vitest) already have first-class Vite support. The fast feedback loop pays for itself within two weeks of refactor work.
Path C — Both at once
Only viable for small apps (under ~30k lines) or greenfield rewrites where the test suite gives you confidence. For everything else, treat it as the high-risk option, not the default.
5. Config parity: the unglamorous checklist
Most “mysterious” Vite bugs are config drift, not Vite itself. Walk through this list before you cut over staging:
- Path aliases — Webpack
resolve.aliasneeds to be re-declared invite.config.tsandtsconfig.jsonpaths. Mismatch breaks editor go-to-definition silently. - Env variables — anything reading
process.env.VUE_APP_*needs migration toimport.meta.env.VITE_*or a shim viadefine. - Static assets — files in
public/are served at root in both, butrequire()-style imports of images need to become explicitimport url from './x.png?url'. - Base path —
publicPathbecomesbase; double-check sub-path deployments behind reverse proxies. - SCSS / global imports — Webpack’s
prependDatamoves to Vite’scss.preprocessorOptions.scss.additionalData. - Polyfills — Vite assumes modern browsers in dev. If you support older targets, add
@vitejs/plugin-legacyfrom day one or LCP regresses on real devices.
A side-by-side example for env handling:
// Webpack (vue.config.js)
process.env.VUE_APP_API_URL
// Vite (vite.config.ts)
import.meta.env.VITE_API_URL
// Compatibility shim during migration:
// vite.config.ts
define: {
'process.env.VUE_APP_API_URL': JSON.stringify(process.env.VITE_API_URL),
}6. Common pitfalls we see in real migrations
CommonJS dependencies in node_modules
Vite is ESM-first. A few legacy libraries still ship CJS only, and they will appear as “default export is not a function” at runtime. The fix is usually optimizeDeps.include plus, occasionally, @originjs/vite-plugin-commonjs. Audit your dependency tree before the cutover, not after.
Dynamic imports with template strings
Patterns like import(`./locales/${lang}.json`) work in Webpack with implicit context. Vite needs an explicit import.meta.glob pattern. This catches teams during vue-i18n locale lazy loading.
SSR mismatches
If you run SSR or static generation, the Vite SSR API differs from vue-server-renderer. Plan for a separate spike on the SSR pipeline; it is rarely a one-liner.
Test runners drifting
Jest doesn’t know about Vite’s transforms. Either keep Jest with its own babel config, or adopt Vitest at the same time — but not on the same PR. Our take is in the Vue Test Utils & Jest in 2026 piece.
7. Webpack vs Vite trade-off table
Vite is not unconditionally better — it is better for most modern Vue 3 apps. Knowing why helps you defend the schedule.
| Concern | Webpack 5 | Vite 5+ |
|---|---|---|
| Dev server cold start | 20–90s on large apps | 1–5s typical |
| HMR latency | 200–2000ms | < 200ms |
| Production bundle size | Mature, fine-grained control | Comparable; uses Rollup |
| Plugin ecosystem maturity | Largest in JS | Growing, Rollup-compatible |
| Legacy browser support | Built in | Requires plugin-legacy |
| Custom loaders | Excellent, well-documented | Need Rollup/Vite plugin equivalents |
| SSR ergonomics | Manual, mature | First-class via ssrLoadModule |
8. Migration checklist for the bundler swap
Treat this as a runbook, not a vibe. Tick each item before the cutover PR is reviewed:
- Inventory all
vue.config.jsoptions and find the Vite equivalent (or decide not to port). - Audit
node_modulesfor CJS-only packages; add tooptimizeDeps. - Replace dynamic
requirepatterns withimport.meta.glob. - Convert env vars to
VITE_prefix; add a shim if non-Vue services read them. - Run production build and diff bundle output against the Webpack baseline (size + chunk count).
- Smoke-test deep links, OAuth callbacks, file uploads, and PDF/image flows.
- Re-measure CI duration: install, typecheck, unit tests, build. The win should show up here.
- Update local docs and onboarding scripts so new hires don’t install both bundlers “just in case.”
Treat the CI duration drop as the headline KPI. It is the metric that funds the next chunk of Core Web Vitals work.
9. FAQ
Should we migrate to Vite while still on Vue 2?
Generally no. Vue 2 + Vite works (via vite-plugin-vue2), but you would maintain an unusual stack for a few months and then change it again. Better to align the bundler swap with the Vue 3 branch, or keep Webpack until Vue 3 is in production.
Will our bundle size shrink?
Usually slightly, because Rollup’s tree-shaking is aggressive and Vue 3 itself is more shakeable than Vue 2. Don’t promise large wins from the bundler alone — most LCP improvements come from code-splitting and lazy routes.
What about module federation?
If you rely on Webpack Module Federation in a micro-frontend setup, vet @originjs/vite-plugin-federation early — feature parity is close but not identical, and shared dependency strategies differ.
Do we need to change ESLint?
No, but it’s a good moment to align the parser with Vue 3. See ESLint, Prettier, and the Vue 3 compiler.
Conclusion
Webpack to Vite is a bundler migration; Vue 2 to 3 is a framework migration. Either can stand alone, but when combined, sequence deliberately so you are never debugging two root causes in one git bisect session.
