Building and Deploying a Vue App
A Vue application built with the standard tooling (Vite) is developed against a fast dev server that compiles modules on demand, but shipping it to users requires a separate production build step that bundles, minifies, and optimizes the code into a small set of static files. Running vite build triggers this process: it resolves the module graph starting from your entry point, tree-shakes unused code, splits chunks according to your dynamic imports and configuration, hashes filenames for cache-busting, and emits everything into a dist directory ready to be served by any static file host.
Cricket analogy: A net practice session (dev server) lets a batter try shots instantly without pressure, but before a real match a team runs full fitness drills, trims the squad to the best XI, and packs kit bags (bundling/minifying) into a ready travel case (the dist folder).
What the production build actually does
During the build, Vite uses Rollup under the hood to produce highly optimized output: dead code elimination removes unused exports, CSS is extracted and minified, and each generated file gets a content hash in its filename (like app.4f9a1c2e.js), which lets you set aggressive, long-lived cache headers on static assets since any code change produces a new filename rather than overwriting an old one. The build also inlines small assets as base64 data URIs below a configurable size threshold, reducing the number of separate HTTP requests for tiny images or icons.
Cricket analogy: Like a coach cutting players who never get game time, Rollup trims unused code from the bundle, while giving each match a unique scorecard ID (content hash) means fans always reference the correct result instead of an old overwritten one.
Environment variables and build modes
Vite supports environment-specific configuration through .env files (.env, .env.production, .env.staging, etc.) and exposes any variable prefixed with VITE_ to your client code via import.meta.env. This prefix requirement is a deliberate security boundary: it prevents server-only secrets from accidentally being bundled into client-facing JavaScript, since only explicitly prefixed variables are ever included in the build output. Running vite build --mode staging loads .env.staging in addition to the base .env file, letting the same codebase produce different builds for different environments.
Cricket analogy: Just as a team keeps its dressing-room strategy sealed from the opposition and only shares approved statements with the media, Vue only exposes explicitly VITE_-prefixed variables to the client, keeping server secrets hidden; a staging series against a weaker side tests tactics before the real tournament build.
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
base: '/app/', // adjust if deploying under a subpath
build: {
outDir: 'dist',
sourcemap: true,
rollupOptions: {
output: {
manualChunks: {
vendor: ['vue', 'vue-router', 'pinia'],
},
},
},
},
})
// Usage in application code:
// const apiBaseUrl = import.meta.env.VITE_API_BASE_URLDeployment targets and routing considerations
Because the output of vite build is a set of static HTML, CSS, and JS files, a Vue single-page application can be deployed to virtually any static hosting provider — object storage behind a CDN, a platform like Netlify or Vercel, or a simple Nginx container. The one deployment detail that trips people up most often is client-side routing: if the app uses Vue Router in history mode, the server must be configured to serve index.html for any unmatched route (a fallback/rewrite rule), otherwise refreshing the browser on a deep link like /reports/42 returns a 404 because no physical file exists at that path on the server.
Cricket analogy: A stadium can host any team's match on generic turf, but if a fan expects to find seat 'Block C-42' (a deep link like /reports/42) and the gate staff haven't been told to redirect confused fans to the main entrance, they'll be turned away at the gate with a 404.
The base option in vite.config.js matters when the app is not served from the domain root — for example, if it's hosted at https://example.com/app/ rather than https://example.com/. Setting base: '/app/' ensures all generated asset URLs and router links are correctly prefixed, avoiding broken references to JS, CSS, and images after deployment. A frequent production issue is forgetting the history-mode fallback rule described above, which manifests as working navigation via in-app links (handled client-side by the router) but broken behavior on hard refresh or direct URL entry, since those requests go straight to the server rather than through Vue Router's client-side logic.
vite buildproduces an optimized, staticdistoutput using Rollup: minified, tree-shaken, and content-hashed for caching.- Only environment variables prefixed with
VITE_are exposed to client code viaimport.meta.env, as a deliberate security boundary. .env.<mode>files let the same codebase produce different builds for different environments (staging, production, etc.).- The
baseconfig option must match the app's deployed subpath, or asset URLs will be broken. - Static hosts serving a Vue app using Router's
historymode need a fallback rule servingindex.htmlfor unmatched routes, or deep links will 404 on refresh. - Content-hashed filenames allow safe, long-lived caching of static assets since any change produces a new filename.
Practice what you learned
1. What is the purpose of content-hashed filenames (e.g. `app.4f9a1c2e.js`) in a Vite production build?
2. Why must client-exposed environment variables in a Vite project be prefixed with `VITE_`?
3. A Vue Router app in `history` mode works fine when navigating via in-app links but returns a 404 on hard refresh at `/reports/42`. What is the most likely cause?
4. What does setting `base: '/app/'` in `vite.config.js` accomplish?
5. How does `.env.staging` differ from the base `.env` file in a Vite project?
Was this page helpful?
You May Also Like
Vue Router Basics
Learn how Vue Router maps URLs to components, enabling single-page applications to feel like multi-page sites without full browser reloads.
Lazy Loading and Code Splitting
Covers how to split a Vue application into smaller chunks using dynamic imports for routes and components, reducing initial bundle size and improving load performance.
Creating a Vue Application
How to scaffold, structure, and bootstrap a Vue 3 project using create-vue and Vite, including the root app instance and mounting process.
Testing Vue Components
Introduces strategies and tools for testing Vue 3 components, from unit testing composables to mounting components with Vue Test Utils and simulating user interaction.
Related Reading
Related Study Notes in Web Development
Browse all study notesWebSockets Study Notes
Web Development · 30 topics
Web DevelopmentWebAssembly Study Notes
WebAssembly · 30 topics
Web DevelopmentgRPC Study Notes
Protocol Buffers · 30 topics
Web DevelopmentSpring Boot Study Notes
Java · 30 topics
Web DevelopmentFlask Study Notes
Python · 30 topics
Web DevelopmentDjango Study Notes
Python · 30 topics