Vite Complete Guide | Fast Dev Server· Build Optimization
이 글의 핵심
Vite starts a dev server in under 300ms and uses native ESM to avoid bundling during development — resulting in near-instant Hot Module Replacement regardless of project size. This guide covers everything from basic setup to plugin authoring and library mode.
Why Vite?
webpack dev server startup (large project): 15-60 seconds
Vite dev server startup: ~300ms
webpack HMR (large project): 2-10 seconds
Vite HMR: <100ms (often <50ms)
Vite achieves this by eliminating the bundling step during development:
webpack: source → bundle all modules → serve bundle → browser runs bundle
Vite: source → serve files directly → browser imports via native ESM
(browser only loads what it actually needs)
Quick Start
# React + TypeScript
npm create vite@latest my-app -- --template react-ts
cd my-app && npm install && npm run dev
# Vue + TypeScript
npm create vite@latest my-app -- --template vue-ts
# Vanilla TypeScript
npm create vite@latest my-app -- --template vanilla-ts
Available templates: vanilla, vanilla-ts, vue, vue-ts, react, react-ts, react-swc, react-swc-ts, preact, preact-ts, lit, lit-ts, svelte, svelte-ts, solid, solid-ts
Project Structure
my-app/
├── public/ # Static assets (served as-is, no processing)
│ └── favicon.ico
├── src/
│ ├── main.tsx # Entry point
│ ├── App.tsx
│ └── assets/ # Imported assets (processed by Vite)
├── index.html # Entry HTML (not in public/ — Vite processes it)
├── vite.config.ts
├── tsconfig.json
└── package.json
<!-- index.html — Vite processes this -->
<!DOCTYPE html>
<html>
<body>
<div id="root"></div>
<!-- Entry point referenced directly — Vite handles bundling -->
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';
export default defineConfig({
plugins: [react()],
// Path aliases
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
'@components': path.resolve(__dirname, './src/components'),
'@hooks': path.resolve(__dirname, './src/hooks'),
},
},
// Dev server
server: {
port: 3000,
open: true, // Open browser on start
// Proxy API calls to backend (avoids CORS in dev)
proxy: {
'/api': {
target: 'http://localhost:8000',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''),
},
},
},
// Build settings
build: {
outDir: 'dist',
sourcemap: true, // Source maps for debugging
minify: 'esbuild', // Fast minification
target: 'es2020', // Browser target
rollupOptions: {
output: {
// Code splitting: split vendor libraries into separate chunk
manualChunks: {
vendor: ['react', 'react-dom'],
router: ['react-router-dom'],
},
},
},
},
// CSS
css: {
modules: {
localsConvention: 'camelCase', // CSS Module class names as camelCase
},
preprocessorOptions: {
scss: {
additionalData: '@import "@/styles/variables.scss";', // Global SCSS
},
},
},
});
Environment Variables
Vite uses .env files. Variables must be prefixed with VITE_ to be exposed to the browser:
# .env (committed — default values)
VITE_APP_NAME=MyApp
VITE_API_URL=http://localhost:8000
# .env.local (not committed — overrides .env locally)
VITE_API_URL=http://localhost:3001
# .env.production (used when building for production)
VITE_API_URL=https://api.myapp.com
# .env.development (used during dev server)
VITE_DEBUG=true
// Access in your code
const apiUrl = import.meta.env.VITE_API_URL;
const appName = import.meta.env.VITE_APP_NAME;
// Built-in Vite env vars
const isDev = import.meta.env.DEV; // true in dev
const isProd = import.meta.env.PROD; // true in production
const mode = import.meta.env.MODE; // 'development' | 'production'
// TypeScript: add types for your env vars
// src/vite-env.d.ts
/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_API_URL: string;
readonly VITE_APP_NAME: string;
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}
Asset Handling
// Import images — Vite returns the URL
import logoUrl from './logo.svg';
import heroUrl from './hero.png';
function App() {
return <img src={logoUrl} alt="Logo" />;
}
// Import as raw string
import svgRaw from './icon.svg?raw';
document.body.innerHTML = svgRaw;
// Import as URL explicitly
import fileUrl from './data.json?url';
// Import as inline base64 (small files)
import inlined from './small-icon.png?inline';
// Dynamic import (code splitting)
const { default: Chart } = await import('./Chart');
// vite.config.ts — configure asset handling
export default defineConfig({
build: {
assetsInlineLimit: 4096, // Inline assets < 4KB as base64 (default)
},
});
Hot Module Replacement (HMR)
Vite’s HMR updates only the changed module — not the whole page.
// Opt into HMR in custom modules
if (import.meta.hot) {
import.meta.hot.accept((newModule) => {
// Called when this module or its deps update
console.log('Module updated:', newModule);
});
import.meta.hot.dispose(() => {
// Clean up before the module is replaced
clearInterval(timer);
});
}
For React, @vitejs/plugin-react uses React Refresh — components update without losing state.
Plugins
Official Plugins
npm install -D @vitejs/plugin-react # React with Babel transforms
npm install -D @vitejs/plugin-react-swc # React with SWC (faster)
npm install -D @vitejs/plugin-vue # Vue 3
npm install -D @vitejs/plugin-legacy # Polyfills for older browsers
Popular Community Plugins
# Auto-import — import React, useState, etc. without explicit import
npm install -D unplugin-auto-import
# SVG as React components
npm install -D vite-plugin-svgr
# Bundle analysis
npm install -D rollup-plugin-visualizer
# PWA support
npm install -D vite-plugin-pwa
// vite.config.ts with plugins
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import svgr from 'vite-plugin-svgr';
import { visualizer } from 'rollup-plugin-visualizer';
export default defineConfig({
plugins: [
react(),
svgr({
svgrOptions: { icon: true },
}),
visualizer({
filename: 'dist/stats.html', // Open after build to see bundle breakdown
open: true,
gzipSize: true,
}),
],
});
Writing a Custom Plugin
// plugins/my-plugin.ts
import type { Plugin } from 'vite';
export function myPlugin(): Plugin {
return {
name: 'my-plugin',
// Transform source files
transform(code, id) {
if (!id.endsWith('.ts')) return null;
// Replace __BUILD_TIME__ with actual timestamp
return code.replace(/__BUILD_TIME__/g, Date.now().toString());
},
// Inject HTML
transformIndexHtml(html) {
return html.replace(
'<head>',
`<head>\n <meta name="build-time" content="${Date.now()}" />`
);
},
// Handle virtual modules
resolveId(id) {
if (id === 'virtual:my-module') return id;
},
load(id) {
if (id === 'virtual:my-module') {
return `export const message = 'Hello from virtual module!';`;
}
},
};
}
Library Mode
Build a reusable library instead of an app:
// vite.config.ts — library mode
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { resolve } from 'path';
import dts from 'vite-plugin-dts';
export default defineConfig({
plugins: [
react(),
dts({ include: ['src'] }), // Generate .d.ts files
],
build: {
lib: {
entry: resolve(__dirname, 'src/index.ts'),
name: 'MyLibrary',
fileName: (format) => `my-library.${format}.js`,
formats: ['es', 'cjs'], // ESM + CommonJS
},
rollupOptions: {
// Exclude peer dependencies from bundle
external: ['react', 'react-dom'],
output: {
globals: {
react: 'React',
'react-dom': 'ReactDOM',
},
},
},
},
});
// package.json for the library
{
"name": "my-library",
"main": "./dist/my-library.cjs.js",
"module": "./dist/my-library.es.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": "./dist/my-library.es.js",
"require": "./dist/my-library.cjs.js"
}
}
}
Multi-Page App (MPA)
// vite.config.ts — multiple entry points
export default defineConfig({
build: {
rollupOptions: {
input: {
main: resolve(__dirname, 'index.html'),
admin: resolve(__dirname, 'admin/index.html'),
login: resolve(__dirname, 'login/index.html'),
},
},
},
});
Migration from webpack
// Common webpack → Vite equivalents
// webpack: require('./image.png')
// Vite: import imageUrl from './image.png'
// webpack: require.context
// Vite: import.meta.glob
// Glob import all files matching a pattern
const modules = import.meta.glob('./routes/*.tsx');
// Returns: { './routes/home.tsx': () => import('./routes/home.tsx'), ... }
// Eager loading (synchronous)
const modules = import.meta.glob('./routes/*.tsx', { eager: true });
// webpack: process.env.REACT_APP_*
// Vite: import.meta.env.VITE_*
// webpack: DefinePlugin
// Vite: define in vite.config.ts
export default defineConfig({
define: {
__APP_VERSION__: JSON.stringify(process.env.npm_package_version),
},
});
npm run preview — Test Production Build
npm run build # Build production output to dist/
npm run preview # Serve the dist/ folder locally on port 4173
Always test the production build before deploying — dev and prod can behave differently (env vars, asset paths, code splitting).
Quick Reference
| Task | Config |
|---|---|
| Dev server port | server: { port: 3000 } |
| API proxy | server: { proxy: { '/api': 'http://localhost:8000' } } |
| Path alias | resolve: { alias: { '@': './src' } } |
| Source maps | build: { sourcemap: true } |
| Code splitting | build: { rollupOptions: { output: { manualChunks: {...} } } } |
| Global SCSS | css: { preprocessorOptions: { scss: { additionalData: '...' } } } |
| Analyze bundle | rollup-plugin-visualizer |
Related posts:
Frequently Asked Questions (FAQ)
Q. When would I use this in practice?
A. Vite starts a dev server in under 300ms and uses native ESM to avoid bundling during development — resulting in near-instant Hot Module Replacement regardless of project size. This guide covers everything from basic setup to plugin authoring and library mode.
Q. What should I read before this?
A. Follow the previous article or related articles links at the bottom of each post to learn in sequence.
Q. Where can I study this more deeply?
A. Check cppreference and the relevant library’s official documentation. The reference links at the end of the article are also worth using.
Related Articles (Internal Links)
Other articles related to this topic.
- TypeScript 5 Complete Guide | Decorators· satisfies
- React 18 Deep Dive | Concurrent Features· Suspense
- GitHub Actions Complete Guide | CI/CD· Workflows
Keywords Covered in This Article (Related Search Terms)
This article covers Vite, Build Tool, Frontend, JavaScript, TypeScript, React, Vue.