🎯 empfohlene Sammlungen
Balanced sample collections from various categories for you to explore
Vite Examples
Vite build tool examples - Modern development server, HMR, bundling, and plugin system
💻 Vite Fundamentals and Setup javascript
🟢 simple
⭐⭐
Basic Vite configuration, project setup, development server, and core features
⏱️ 20 min
🏷️ vite, build, configuration, development
Prerequisites:
Node.js, JavaScript/TypeScript, Basic understanding of build tools
// Vite Fundamentals and Setup Examples
// 1. vite.config.js - Basic Configuration
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import react from '@vitejs/plugin-react'
export default defineConfig({
// Development server configuration
server: {
port: 3000,
host: true, // Expose to network
open: true, // Open browser automatically
cors: true, // Enable CORS
proxy: {
// API proxy
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
rewrite: (path) => path.replace(/^/api/, '')
}
}
},
// Build configuration
build: {
outDir: 'dist',
assetsDir: 'assets',
sourcemap: true,
minify: 'esbuild', // 'terser' or false
target: 'es2015',
// Rollup configuration
rollupOptions: {
input: 'src/main.js',
output: {
manualChunks: {
vendor: ['vue', 'react'],
utils: ['lodash', 'date-fns']
}
}
},
// Report compressed sizes
reportCompressedSize: true,
// CSS code splitting
cssCodeSplit: true,
// Chunk size warning limit
chunkSizeWarningLimit: 1000
},
// Preview server configuration
preview: {
port: 4173,
host: true
},
// Dependency optimization
optimizeDeps: {
include: ['vue', 'react', 'axios'],
exclude: ['@types/lodash']
},
// Plugins
plugins: [
vue(),
react({
// React fast refresh
fastRefresh: true
})
],
// Environment variables
define: {
__APP_VERSION__: JSON.stringify(process.env.npm_package_version)
},
// CSS configuration
css: {
// CSS modules configuration
modules: {
localsConvention: 'camelCase',
generateScopedName: '[name]__[hash:base64:5]'
},
// PostCSS configuration
postcss: {
plugins: [
require('autoprefixer'),
require('tailwindcss')
]
},
// Preprocessor options
preprocessorOptions: {
scss: {
additionalData: `@import "./src/styles/variables.scss";`
}
}
},
// Assets configuration
assetsInclude: ['**/*.gltf'],
// Experimental features
experimental: {
renderBuiltUrl(filename, { hostType }) {
if (hostType === 'js') {
return { js: `window.__toPath(${JSON.stringify(filename)})` }
} else {
return { relative: true }
}
}
},
// Global style preprocessing
cssPreprocessOptions: {
less: {
modifyVars: {
'primary-color': '#1890ff'
}
}
}
})
// 2. package.json - Project Setup
{
"name": "vite-project",
"private": true,
"version": "1.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"serve": "vite preview",
"build:analyze": "vite build --mode analyze && npx vite-bundle-analyzer dist/stats.html",
"test": "vitest",
"test:ui": "vitest --ui",
"test:coverage": "vitest --coverage",
"lint": "eslint . --ext vue,js,jsx,cjs,mjs,ts,tsx,cts,mts --fix --ignore-path .gitignore",
"format": "prettier --write src/",
"type-check": "vue-tsc --noEmit"
},
"dependencies": {
"vue": "^3.4.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"axios": "^1.6.0",
"lodash": "^4.17.21",
"date-fns": "^2.30.0"
},
"devDependencies": {
"@vitejs/plugin-vue": "^5.0.0",
"@vitejs/plugin-react": "^4.2.0",
"vite": "^5.0.0",
"vite-plugin-windicss": "^1.9.0",
"windicss": "^3.5.6",
"autoprefixer": "^10.4.0",
"tailwindcss": "^3.4.0",
"sass": "^1.69.0",
"typescript": "^5.3.0",
"vue-tsc": "^1.8.0",
"vitest": "^1.0.0",
"@vitest/ui": "^1.0.0",
"@vitest/coverage-v8": "^1.0.0",
"jsdom": "^23.0.0",
"eslint": "^8.56.0",
"eslint-plugin-vue": "^9.19.0",
"eslint-plugin-react": "^7.33.0",
"eslint-plugin-react-hooks": "^4.6.0",
"prettier": "^3.1.0",
"prettier-plugin-tailwindcss": "^0.5.0"
}
}
// 3. Environment Variables (.env)
# Development environment variables
VITE_APP_TITLE=My Vite App
VITE_API_BASE_URL=http://localhost:8080/api
VITE_APP_VERSION=1.0.0
VITE_DEBUG_MODE=true
// .env.production
VITE_APP_TITLE=Production App
VITE_API_BASE_URL=https://api.example.com
VITE_DEBUG_MODE=false
// 4. Using Environment Variables in JavaScript
// src/config.js
export const config = {
appName: import.meta.env.VITE_APP_TITLE || 'Default App',
apiUrl: import.meta.env.VITE_API_BASE_URL || 'http://localhost:3000',
version: import.meta.env.VITE_APP_VERSION || '1.0.0',
isDebug: import.meta.env.VITE_DEBUG_MODE === 'true',
isDevelopment: import.meta.env.DEV,
isProduction: import.meta.env.PROD
}
// 5. Basic React Example with Vite (src/App.jsx)
import React, { useState, useEffect } from 'react'
import './App.css'
function App() {
const [count, setCount] = useState(0)
const [users, setUsers] = useState([])
useEffect(() => {
fetchUsers()
}, [])
const fetchUsers = async () => {
try {
const response = await fetch(`${config.apiUrl}/users`)
const data = await response.json()
setUsers(data)
} catch (error) {
console.error('Error fetching users:', error)
}
}
return (
<div className="app">
<header>
<h1>{config.appName}</h1>
<p>Version: {config.version}</p>
<p>Environment: {config.isDevelopment ? 'Development' : 'Production'}</p>
</header>
<main>
<div className="counter-section">
<h2>Counter Example</h2>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>
Increment
</button>
<button onClick={() => setCount(count - 1)}>
Decrement
</button>
</div>
<div className="users-section">
<h2>Users List</h2>
{users.length > 0 ? (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
) : (
<p>Loading users...</p>
)}
</div>
</main>
</div>
)
}
export default App
// 6. Basic Vue Example with Vite (src/App.vue)
<template>
<div id="app">
<header>
<h1>{{ config.appName }}</h1>
<p>Version: {{ config.version }}</p>
<p>Environment: {{ config.isDevelopment ? 'Development' : 'Production' }}</p>
</header>
<main>
<section class="counter-section">
<h2>Counter Example</h2>
<p>Count: {{ count }}</p>
<button @click="increment">Increment</button>
<button @click="decrement">Decrement</button>
</section>
<section class="users-section">
<h2>Users List</h2>
<ul v-if="users.length">
<li v-for="user in users" :key="user.id">
{{ user.name }}
</li>
</ul>
<p v-else>Loading users...</p>
</section>
</main>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { config } from './config'
const count = ref(0)
const users = ref([])
const increment = () => {
count.value++
}
const decrement = () => {
count.value--
}
const fetchUsers = async () => {
try {
const response = await fetch(`${config.apiUrl}/users`)
const data = await response.json()
users.value = data
} catch (error) {
console.error('Error fetching users:', error)
}
}
onMounted(() => {
fetchUsers()
})
</script>
<style scoped>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
header {
margin-bottom: 2rem;
}
.counter-section, .users-section {
margin-bottom: 2rem;
}
button {
margin: 0 0.5rem;
padding: 0.5rem 1rem;
background-color: #42b983;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background-color: #369870;
}
ul {
list-style-type: none;
padding: 0;
}
li {
margin: 0.5rem 0;
padding: 0.5rem;
background-color: #f9f9f9;
border-radius: 4px;
}
</style>
// 7. JavaScript Module Example (src/modules/api.js)
import axios from 'axios'
// Create axios instance with base configuration
const api = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL,
timeout: 10000,
headers: {
'Content-Type': 'application/json'
}
})
// Request interceptor
api.interceptors.request.use(
(config) => {
// Add authentication token if available
const token = localStorage.getItem('authToken')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
// Add request timestamp
config.metadata = { startTime: new Date() }
return config
},
(error) => {
return Promise.reject(error)
}
)
// Response interceptor
api.interceptors.response.use(
(response) => {
// Calculate request duration
const endTime = new Date()
const duration = endTime - response.config.metadata.startTime
console.log(`Request took ${duration}ms`)
return response
},
(error) => {
// Handle common errors
if (error.response?.status === 401) {
// Redirect to login
window.location.href = '/login'
}
return Promise.reject(error)
}
)
// API methods
export const userApi = {
getUsers: () => api.get('/users'),
getUser: (id) => api.get(`/users/${id}`),
createUser: (userData) => api.post('/users', userData),
updateUser: (id, userData) => api.put(`/users/${id}`, userData),
deleteUser: (id) => api.delete(`/users/${id}`)
}
export const productApi = {
getProducts: (params = {}) => api.get('/products', { params }),
getProduct: (id) => api.get(`/products/${id}`),
createProduct: (productData) => api.post('/products', productData),
updateProduct: (id, productData) => api.put(`/products/${id}`, productData),
deleteProduct: (id) => api.delete(`/products/${id}`)
}
export default api
// 8. CSS Example with CSS Modules (src/components/Button.module.css)
.button {
padding: 0.75rem 1.5rem;
border: none;
border-radius: 0.5rem;
font-weight: 600;
cursor: pointer;
transition: all 0.2s ease;
display: inline-flex;
align-items: center;
gap: 0.5rem;
}
.primary {
background-color: #3b82f6;
color: white;
}
.primary:hover {
background-color: #2563eb;
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.4);
}
.secondary {
background-color: #6b7280;
color: white;
}
.secondary:hover {
background-color: #4b5563;
}
.danger {
background-color: #ef4444;
color: white;
}
.danger:hover {
background-color: #dc2626;
}
.large {
padding: 1rem 2rem;
font-size: 1.125rem;
}
.small {
padding: 0.5rem 1rem;
font-size: 0.875rem;
}
// Usage in React component
import styles from './Button.module.css'
function Button({ variant = 'primary', size = 'medium', children, ...props }) {
return (
<button
className={`${styles.button} ${styles[variant]} ${styles[size]}`}
{...props}
>
{children}
</button>
)
}
// 9. Testing with Vitest (src/components/Button.test.js)
import { render, screen, fireEvent } from '@testing-library/react'
import { describe, it, expect } from 'vitest'
import Button from './Button'
describe('Button Component', () => {
it('renders with default props', () => {
render(<Button>Click me</Button>)
const button = screen.getByRole('button', { name: /click me/i })
expect(button).toBeInTheDocument()
expect(button).toHaveClass('button')
expect(button).toHaveClass('primary')
})
it('applies variant classes correctly', () => {
render(<Button variant="danger">Delete</Button>)
const button = screen.getByRole('button', { name: /delete/i })
expect(button).toHaveClass('danger')
})
it('handles click events', () => {
const handleClick = vi.fn()
render(<Button onClick={handleClick}>Click me</Button>)
fireEvent.click(screen.getByRole('button'))
expect(handleClick).toHaveBeenCalledTimes(1)
})
it('is disabled when disabled prop is true', () => {
render(<Button disabled>Disabled</Button>)
const button = screen.getByRole('button')
expect(button).toBeDisabled()
})
})
// 10. PostCSS Configuration (postcss.config.js)
export default {
plugins: {
'tailwindcss': {},
'autoprefixer': {},
'postcss-px-to-viewport': {
viewportWidth: 375, // 设计稿的视口宽度
unitPrecision: 5, // 单位转换后保留的精度
viewportUnit: 'vw', // 希望使用的视口单位
selectorBlackList: ['ignore'], // 需要忽略的CSS选择器
minPixelValue: 1, // 设置最小的转换数值
mediaQuery: false // 媒体查询里的单位是否需要转换单位
}
}
}
💻 Vite Plugin Development javascript
🟡 intermediate
⭐⭐⭐⭐
Creating custom Vite plugins, using official plugins, and plugin ecosystem
⏱️ 35 min
🏷️ vite, plugins, development, api, hooks
Prerequisites:
Vite basics, JavaScript/TypeScript, Node.js API, Build tool concepts
// Vite Plugin Development Examples
// 1. Custom Vite Plugin Structure
// vite-plugin-custom.js
export function customPlugin(options = {}) {
const {
transform = true,
serve = true,
build = true
} = options
return {
// Plugin name
name: 'vite-plugin-custom',
// enforce plugin execution order
enforce: 'pre', // 'post' or undefined
// Apply plugin to specific files
apply: 'serve', // 'build', 'both', or config
// Config hook - Modify Vite config
config(config, { command, mode }) {
console.log('Config hook:', { command, mode })
// Modify config
if (command === 'serve') {
config.server.hmr = {
overlay: false
}
}
return {
define: {
...config.define,
__CUSTOM_PLUGIN__: JSON.stringify(true)
}
}
},
// Config resolved hook
configResolved(config) {
console.log('Config resolved hook')
},
// Configure server
configureServer(server) {
server.middlewares.use((req, res, next) => {
if (req.url === '/custom-api') {
res.setHeader('Content-Type', 'application/json')
res.end(JSON.stringify({ message: 'Custom API response' }))
return
}
next()
})
// Return cleanup function
return () => {
console.log('Server cleanup')
}
},
// Transform modules
transform(code, id, options) {
if (!transform) return null
// Only transform .js files
if (!id.endsWith('.js')) return null
console.log('Transforming:', id)
// Add custom code injection
if (id.includes('main.js')) {
return {
code: code + '\nconsole.log("Custom plugin injected!");',
map: null // Generate source map if needed
}
}
return null
},
// Load id
load(id) {
if (id === 'virtual:custom-module') {
return `export default 'Custom virtual module content'`
}
},
// Resolve id
resolveId(id, importer) {
if (id === 'virtual:custom-module') {
return id
}
},
// Build hooks
buildStart() {
console.log('Build started')
},
buildEnd() {
console.log('Build ended')
},
generateBundle(options, bundle) {
console.log('Generate bundle:', Object.keys(bundle))
},
writeBundle(options, bundle) {
console.log('Write bundle completed')
},
// Handle hot module replacement
handleHotUpdate({ file, modules, timestamp }) {
console.log('HMR update:', file)
// Custom HMR logic
if (file.endsWith('.custom')) {
// Invalidate all modules
return modules
}
}
}
}
// 2. Vue Plugin Development
// vite-plugin-vue-enhanced.js
import vue from '@vitejs/plugin-vue'
import { compileTemplate } from '@vue/compiler-sfc'
export function vueEnhancedPlugin(options = {}) {
return [
vue(),
{
name: 'vite-plugin-vue-enhanced',
transform(code, id) {
if (!id.endsWith('.vue')) return null
// Custom Vue template transformations
if (options.enableAutoTranslate) {
// Auto-translate text content
code = code.replace(
/>([^<]+)</g,
(match, text) => {
const trimmed = text.trim()
if (trimmed && !trimmed.includes('{') && !trimmed.includes('v-')) {
return `>{{ t('${trimmed}') }}<`
}
return match
}
)
}
return { code, map: null }
},
// Vue-specific hooks
vueTransform(code, filename) {
console.log('Vue transform:', filename)
return { code }
}
}
]
}
// 3. CSS Plugin Development
// vite-plugin-css-enhanced.js
export function cssEnhancedPlugin(options = {}) {
const {
enableVariables = false,
enableAutoPrefix = false,
enableMinification = false
} = options
return {
name: 'vite-plugin-css-enhanced',
transform(code, id) {
if (!id.endsWith('.css')) return null
let transformedCode = code
// Auto prefix CSS properties
if (enableAutoPrefix) {
transformedCode = transformedCode.replace(
/([^:]+):([^;]+)/g,
(match, property, value) => {
const prefixes = {
'transform': '-webkit-transform',
'transition': '-webkit-transition',
'animation': '-webkit-animation',
'user-select': '-webkit-user-select',
'box-sizing': '-webkit-box-sizing'
}
const prefixed = prefixes[property.trim()]
return prefixed ? `${prefixed}: ${value}; ${match}` : match
}
)
}
// CSS variables transformation
if (enableVariables) {
transformedCode = transformedCode.replace(
/\$([a-zA-Z0-9-_]+):/g,
':root { --$1:'
)
}
if (transformedCode !== code) {
return { code: transformedCode, map: null }
}
},
// CSS processing hooks
cssTransform(code, filename) {
console.log('CSS transform:', filename)
return { code }
}
}
}
// 4. Image Optimization Plugin
// vite-plugin-image-optimize.js
import sharp from 'sharp'
import fs from 'fs/promises'
import path from 'path'
export function imageOptimizePlugin(options = {}) {
const {
formats = ['webp', 'avif'],
quality = 80,
sizes = [480, 768, 1024, 1920]
} = options
return {
name: 'vite-plugin-image-optimize',
async load(id) {
if (!id.match(/\.(png|jpg|jpeg)$/i)) return null
const originalPath = id
const publicDir = path.dirname(id).includes('/public')
? path.dirname(id).split('/public/')[1]
: ''
// Generate responsive images
const optimizedImages = []
for (const format of formats) {
for (const size of sizes) {
const filename = `optimized-${size}w.${format}`
const outputPath = `public/optimized/${filename}`
try {
await fs.mkdir(path.dirname(outputPath), { recursive: true })
await sharp(originalPath)
.resize(size, null, { withoutEnlargement: true })
.toFormat(format, { quality })
.toFile(outputPath)
optimizedImages.push({
src: `/optimized/${filename}`,
width: size,
format,
type: format.toUpperCase()
})
} catch (error) {
console.warn('Image optimization failed:', error)
}
}
}
// Return JavaScript module with image data
return `
export default {
src: `${publicDir}/${path.basename(originalPath)}`,
optimized: ${JSON.stringify(optimizedImages)}
}
`
}
}
}
// 5. Markdown Enhancement Plugin
// vite-plugin-markdown-enhanced.js
import { marked } from 'marked'
import Prism from 'prismjs'
export function markdownEnhancedPlugin(options = {}) {
const {
enableCodeHighlight = true,
enableTOC = true,
enableFrontMatter = true
} = options
return {
name: 'vite-plugin-markdown-enhanced',
async load(id) {
if (!id.endsWith('.md')) return null
let content = await fs.readFile(id, 'utf-8')
let frontMatter = {}
// Parse front matter
if (enableFrontMatter && content.startsWith('---')) {
const frontMatterEnd = content.indexOf('---', 3)
const frontMatterStr = content.slice(3, frontMatterEnd).trim()
content = content.slice(frontMatterEnd + 3).trim()
// Simple YAML parser (in production, use js-yaml)
frontMatterStr.split('\n').forEach(line => {
const [key, ...valueParts] = line.split(':')
if (key && valueParts.length) {
frontMatter[key.trim()] = valueParts.join(':').trim()
}
})
}
// Generate table of contents
let toc = ''
if (enableTOC) {
const headings = content.match(/^#{1,6}\s.+$/gm) || []
toc = '## Table of Contents\n\n'
headings.forEach(heading => {
const level = heading.match(/^#/)?.length || 1
const text = heading.replace(/^#+\s/, '')
const anchor = text.toLowerCase().replace(/[^a-z0-9]+/g, '-')
toc += `${' '.repeat(level - 1)}* [${text}](#${anchor})\n`
})
}
// Convert markdown to HTML
let html = marked(content)
// Add code highlighting
if (enableCodeHighlight) {
html = html.replace(
/<pre><code class="language-(\w+)">([\s\S]*?)<\/code><\/pre>/g,
(match, lang, code) => {
try {
const highlighted = Prism.highlight(
code.replace(/</g, '<').replace(/>/g, '>'),
Prism.languages[lang],
lang
)
return `<pre><code class="language-${lang}">${highlighted}</code></pre>`
} catch (e) {
return match
}
}
)
}
// Return JavaScript module
return `
export default {
frontMatter: ${JSON.stringify(frontMatter)},
toc: `${toc}`,
html: `${html}`,
content: `${content}`
}
`
}
}
}
// 6. Bundle Analysis Plugin
// vite-plugin-bundle-analyzer.js
export function bundleAnalyzerPlugin(options = {}) {
const {
outputFile = 'bundle-analysis.html',
open = true
} = options
return {
name: 'vite-plugin-bundle-analyzer',
generateBundle(options, bundle) {
const analysis = {}
for (const [fileName, chunk] of Object.entries(bundle)) {
if (chunk.type === 'chunk') {
analysis[fileName] = {
size: chunk.code.length,
modules: Object.keys(chunk.modules).length,
imports: chunk.imports || [],
dynamicImports: chunk.dynamicImports || []
}
}
}
// Generate HTML report
const html = `
<!DOCTYPE html>
<html>
<head>
<title>Bundle Analysis</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
</head>
<body>
<h1>Bundle Analysis</h1>
<canvas id="bundleChart"></canvas>
<script>
const data = ${JSON.stringify(analysis)};
const ctx = document.getElementById('bundleChart').getContext('2d');
new Chart(ctx, {
type: 'bar',
data: {
labels: Object.keys(data),
datasets: [{
label: 'Bundle Size (bytes)',
data: Object.values(data).map(d => d.size),
backgroundColor: 'rgba(54, 162, 235, 0.2)',
borderColor: 'rgba(54, 162, 235, 1)',
borderWidth: 1
}]
},
options: {
scales: {
y: {
beginAtZero: true
}
}
}
});
</script>
<pre>${JSON.stringify(analysis, null, 2)}</pre>
</body>
</html>
`
// Write analysis file
this.emitFile({
type: 'asset',
fileName: outputFile,
source: html
})
if (open) {
console.log(`Bundle analysis saved to: ${outputFile}`)
}
}
}
}
// 7. Advanced Plugin Example - PWA Manifest Generator
// vite-plugin-pwa-manifest.js
export function pwaManifestPlugin(options = {}) {
const {
name = 'My PWA App',
shortName = 'PWA',
themeColor = '#000000',
backgroundColor = '#ffffff',
display = 'standalone',
startUrl = '/',
icons = []
} = options
const manifest = {
name,
shortName,
themeColor,
backgroundColor,
display,
startUrl,
icons: icons.map(size => ({
src: `/icon-${size}.png`,
sizes: `${size}x${size}`,
type: 'image/png'
}))
}
return {
name: 'vite-plugin-pwa-manifest',
configResolved(config) {
// Ensure public directory exists
const publicDir = path.resolve(config.root, 'public')
fs.mkdir(publicDir, { recursive: true })
},
generateBundle(options, bundle) {
// Generate manifest.json
this.emitFile({
type: 'asset',
fileName: 'manifest.json',
source: JSON.stringify(manifest, null, 2)
})
// Generate service worker template
const serviceWorker = `
const CACHE_NAME = '${name.toLowerCase().replace(/\s+/g, '-')}-v1'
const urlsToCache = ${JSON.stringify(Object.keys(bundle))}
self.addEventListener('install', event => {
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => cache.addAll(urlsToCache))
)
})
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request)
.then(response => {
return response || fetch(event.request)
})
)
})
`
this.emitFile({
type: 'asset',
fileName: 'sw.js',
source: serviceWorker
})
}
}
}
// 8. Using Multiple Plugins in vite.config.js
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { customPlugin } from './vite-plugin-custom.js'
import { vueEnhancedPlugin } from './vite-plugin-vue-enhanced.js'
import { cssEnhancedPlugin } from './css-enhanced.js'
import { imageOptimizePlugin } from './image-optimize.js'
import { pwaManifestPlugin } from './pwa-manifest.js'
export default defineConfig({
plugins: [
// Core plugins
react(),
// Custom plugins
customPlugin({
transform: true,
serve: true,
build: false // Disable in build for performance
}),
cssEnhancedPlugin({
enableAutoPrefix: true,
enableVariables: true
}),
// Production-only plugins
process.env.NODE_ENV === 'production' && imageOptimizePlugin({
formats: ['webp', 'avif'],
quality: 85
}),
// PWA plugin
pwaManifestPlugin({
name: 'My Awesome App',
icons: [192, 512]
})
].filter(Boolean) // Remove falsy plugins
})
// 9. Plugin Testing with Vitest
// vite-plugin-custom.test.js
import { describe, it, expect, vi } from 'vitest'
import { customPlugin } from './vite-plugin-custom.js'
describe('Custom Vite Plugin', () => {
it('should have correct plugin name', () => {
const plugin = customPlugin()
expect(plugin.name).toBe('vite-plugin-custom')
})
it('should apply custom config in development', () => {
const plugin = customPlugin()
const mockConfig = { define: {} }
const result = plugin.config(mockConfig, { command: 'serve', mode: 'development' })
expect(result.define.__CUSTOM_PLUGIN__).toBe(true)
expect(result.server.hmr.overlay).toBe(false)
})
it('should transform main.js file', () => {
const plugin = customPlugin()
const code = 'console.log("original");'
const id = '/path/to/main.js'
const result = plugin.transform(code, id)
expect(result.code).toContain('Custom plugin injected!')
})
it('should handle virtual module', () => {
const plugin = customPlugin()
const result = plugin.load('virtual:custom-module')
expect(result).toBe('export default \'Custom virtual module content\'')
})
it('should resolve virtual module id', () => {
const plugin = customPlugin()
const result = plugin.resolveId('virtual:custom-module')
expect(result).toBe('virtual:custom-module')
})
})
// 10. Plugin Development Best Practices
// Documentation: Always include README.md with examples
/*
# Vite Custom Plugin
## Usage
```js
// vite.config.js
import { customPlugin } from 'vite-plugin-custom'
export default {
plugins: [
customPlugin({
// Plugin options
})
]
}
```
## API
### Options
- `transform` (boolean): Enable code transformation (default: true)
- `serve` (boolean): Apply to dev server (default: true)
- `build` (boolean): Apply to build (default: true)
### Hooks
- `config`: Modify Vite configuration
- `transform`: Transform module code
- `load`: Load virtual modules
- `configureServer`: Configure development server
## Examples
```js
// Basic usage
export default {
plugins: [customPlugin()]
}
// With options
export default {
plugins: [
customPlugin({
transform: true,
serve: true,
build: false
})
]
}
```
*/
💻 Vite Advanced Features and Optimization javascript
🔴 complex
⭐⭐⭐⭐⭐
Performance optimization, SSR, advanced bundling, deployment, and production considerations
⏱️ 45 min
🏷️ vite, optimization, production, deployment, ssr
Prerequisites:
Advanced Vite knowledge, Production deployment, Performance optimization, Build tools expertise
// Vite Advanced Features and Optimization Examples
// 1. Advanced Vite Configuration for Production
// vite.config.prod.js
import { defineConfig } from 'vite'
import { createRequire } from 'module'
import { fileURLToPath } from 'url'
import { dirname, resolve } from 'path'
import { visualizer } from 'rollup-plugin-visualizer'
import { VitePWA } from 'vite-plugin-pwa'
const __dirname = dirname(fileURLToPath(import.meta.url))
const require = createRequire(import.meta.url)
export default defineConfig({
// Advanced build configuration
build: {
outDir: 'dist',
assetsDir: 'assets',
sourcemap: false, // Disabled for production
minify: 'terser',
target: 'es2020',
// Rollup optimization
rollupOptions: {
input: {
main: resolve(__dirname, 'index.html'),
admin: resolve(__dirname, 'admin.html'),
},
output: {
// Chunk splitting strategy
manualChunks: {
// Vendor libraries
vendor: ['react', 'react-dom', 'vue', 'vue-router'],
// Utility libraries
utils: ['lodash-es', 'date-fns', 'axios'],
// UI libraries
ui: ['antd', '@headlessui/vue', 'element-plus'],
// Charts
charts: ['chart.js', 'echarts'],
// Icons
icons: ['@ant-design/icons', '@fortawesome/fontawesome-free'],
},
// Asset naming
chunkFileNames: (chunkInfo) => {
const facadeModuleId = chunkInfo.facadeModuleId ?
chunkInfo.facadeModuleId.split('/').pop() : 'chunk'
return `js/${facadeModuleId}-[hash].js`
},
entryFileNames: 'js/[name]-[hash].js',
assetFileNames: (assetInfo) => {
const info = assetInfo.name.split('.')
const ext = info[info.length - 1]
if (/.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/i.test(assetInfo.name)) {
return `media/${info[info.length - 2]}-[hash][extname]`
}
if (/\.(png|jpe?g|gif|svg)(\?.*)?$/i.test(assetInfo.name)) {
return `img/${info[info.length - 2]}-[hash][extname]`
}
if (/\.css$/i.test(assetInfo.name)) {
return `css/[name]-[hash][extname]`
}
return `assets/${info[info.length - 2]}-[hash][extname]`
}
},
// External dependencies
external: ['react', 'react-dom', 'vue'],
// Plugin configuration
plugins: [
// Bundle analyzer
process.env.ANALYZE && visualizer({
filename: 'dist/stats.html',
open: true,
gzipSize: true,
brotliSize: true
})
].filter(Boolean)
},
// Build report
reportCompressedSize: true,
// Chunk size warning
chunkSizeWarningLimit: 1000,
// CSS configuration
cssCodeSplit: true,
// Manifest generation
manifest: true
},
// Development server optimizations
server: {
port: 3000,
host: true,
cors: true,
// HMR configuration
hmr: {
overlay: true,
port: 3001
},
// Proxy configuration
proxy: {
'/api': {
target: 'https://api.example.com',
changeOrigin: true,
secure: true,
rewrite: (path) => path.replace(/^\/api/, ''),
configure: (proxy, _options) => {
proxy.on('error', (err, _req, _res) => {
console.log('proxy error', err)
})
proxy.on('proxyReq', (proxyReq, req, _res) => {
console.log('Sending Request to the Target:', req.method, req.url)
})
proxy.on('proxyRes', (proxyRes, req, _res) => {
console.log('Received Response from the Target:', proxyRes.statusCode, req.url)
})
}
},
// WebSocket proxy
'/socket.io': {
target: 'ws://localhost:3001',
ws: true,
}
},
// Watch configuration
watch: {
usePolling: false,
interval: 100,
ignored: ['**/node_modules/**', '**/.git/**']
}
},
// Preview server
preview: {
port: 4173,
host: true,
cors: true
},
// Dependency optimization
optimizeDeps: {
include: [
'react',
'react-dom',
'vue',
'vue-router',
'pinia',
'axios',
'lodash-es'
],
exclude: [
'@types/lodash-es',
'fsevents',
'electron'
],
entries: [
'src/main.js',
'src/admin.js'
]
},
// CSS configuration
css: {
// PostCSS configuration
postcss: {
plugins: [
require('autoprefixer'),
require('postcss-flexbugs-fixes'),
require('postcss-preset-env')({
stage: 3,
features: {
'nesting-rules': true,
'custom-properties': true
},
browsers: ['> 1%', 'last 2 versions', 'not ie <= 8']
})
]
},
// CSS modules
modules: {
localsConvention: 'camelCaseOnly',
generateScopedName: (name, filename, css) => {
const hash = require('hash-sum')(css)
return `${name}__${hash}`
}
},
// Preprocessor configuration
preprocessorOptions: {
scss: {
additionalData: `@import "@/styles/variables.scss"; @import "@/styles/mixins.scss";`,
sassOptions: {
includePaths: ['./src/styles'],
outputStyle: 'compressed'
}
},
less: {
javascriptEnabled: true,
modifyVars: {
'primary-color': '#1890ff',
'border-radius-base': '6px'
}
},
styl: {
imports: [
'./src/styles/variables.styl'
]
}
},
// Dev sourcemap
devSourcemap: true
},
// Advanced plugins
plugins: [
// PWA Plugin
VitePWA({
registerType: 'autoUpdate',
workbox: {
globPatterns: ['**/*.{js,css,html,ico,png,svg,jpg,jpeg}'],
runtimeCaching: [
{
urlPattern: /^https:\/\/api\.example\.com\/.*/i,
handler: 'NetworkFirst',
options: {
cacheName: 'api-cache',
expiration: {
maxEntries: 100,
maxAgeSeconds: 60 * 60 * 24 // 24 hours
}
}
}
]
},
includeAssets: ['favicon.ico', 'apple-touch-icon.png', 'masked-icon.svg'],
manifest: {
name: 'My Awesome App',
short_name: 'MyApp',
description: 'A modern web application built with Vite',
theme_color: '#1890ff',
background_color: '#ffffff',
display: 'standalone',
icons: [
{
src: 'pwa-192x192.png',
sizes: '192x192',
type: 'image/png'
},
{
src: 'pwa-512x512.png',
sizes: '512x512',
type: 'image/png'
}
]
}
}),
// Legacy browser support
require('@vitejs/plugin-legacy')({
targets: ['defaults', 'not IE 11'],
additionalLegacyPolyfills: ['regenerator-runtime/runtime']
}),
// Compression
require('vite-plugin-compression')({
verbose: true,
disable: false,
threshold: 10240,
algorithm: 'gzip',
ext: '.gz'
}),
// Bundle analyzer (conditional)
process.env.ANALYZE && visualizer({
filename: 'dist/stats.html',
open: true,
gzipSize: true,
brotliSize: true
})
].filter(Boolean),
// Environment configuration
define: {
__APP_VERSION__: JSON.stringify(process.env.npm_package_version),
__BUILD_TIME__: JSON.stringify(new Date().toISOString()),
__GIT_COMMIT__: JSON.stringify(process.env.GIT_COMMIT || 'unknown')
},
// Experimental features
experimental: {
// Build target support
buildTarget: undefined, // 'node', 'webworker', etc.
// Advanced asset handling
renderBuiltUrl(filename, { hostType, type }) {
if (type === 'asset') {
// CDN URLs for assets
if (process.env.NODE_ENV === 'production') {
return `https://cdn.example.com/assets/${filename}`
}
}
return { relative: true }
}
},
// Preview configuration
preview: {
port: 4173,
host: true,
cors: true
},
// Clear screen
clearScreen: false,
// Environment files
envPrefix: 'VITE_',
// Base path
base: process.env.VITE_BASE_PATH || '/',
// Public directory
publicDir: 'public',
// Cache directory
cacheDir: 'node_modules/.vite'
})
// 2. Server-Side Rendering (SSR) Configuration
// vite.config.ssr.js
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
build: {
target: 'node',
rollupOptions: {
input: 'src/entry-server.js',
output: {
format: 'es',
entryFileNames: '[name].js'
}
},
ssr: true
},
ssr: {
// Server-side rendering options
noExternal: ['@vitejs/plugin-react']
},
define: {
'process.env': process.env
}
})
// 3. Multi-Application Configuration
// vite.config.multi-app.js
import { defineConfig } from 'vite'
import { resolve } from 'path'
import reactRefresh from '@vitejs/plugin-react-refresh'
const sharedConfig = {
plugins: [reactRefresh()],
resolve: {
alias: {
'@': resolve(__dirname, 'src'),
'@shared': resolve(__dirname, 'shared')
}
},
server: {
port: 3000,
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true
}
}
}
}
export default defineConfig([
// Main app
{
...sharedConfig,
build: {
...sharedConfig.build,
outDir: 'dist/main',
rollupOptions: {
input: resolve(__dirname, 'index.html')
}
}
},
// Admin app
{
...sharedConfig,
build: {
...sharedConfig.build,
outDir: 'dist/admin',
rollupOptions: {
input: resolve(__dirname, 'admin.html')
}
}
}
])
// 4. Library Mode Configuration
// vite.config.lib.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'path'
export default defineConfig({
plugins: [vue()],
build: {
lib: {
entry: resolve(__dirname, 'src/index.js'),
name: 'MyLibrary',
fileName: (format) => `my-library.${format}.js`
},
rollupOptions: {
external: ['vue'],
output: {
globals: {
vue: 'Vue'
}
}
},
sourcemap: true,
minify: 'terser'
}
})
// 5. Advanced Plugin Development
// vite-plugin-performance.js
import { performance } from 'perf_hooks'
export function performancePlugin(options = {}) {
const {
enableMetrics = process.env.NODE_ENV === 'development',
enableMonitoring = false,
reportInterval = 5000
} = options
let startTime
let metrics = {
transforms: 0,
loads: 0,
resolves: 0,
totalTransformTime: 0,
totalLoadTime: 0
}
return {
name: 'vite-plugin-performance',
buildStart() {
if (enableMetrics) {
startTime = performance.now()
console.log('🚀 Build started')
}
},
transform(code, id) {
if (!enableMetrics) return null
const start = performance.now()
metrics.transforms++
// Continue with original transform
return null
},
transformEnd() {
if (!enableMetrics) return
const end = performance.now()
const buildTime = end - startTime
console.log('📊 Build Performance Metrics:')
console.log(` Total time: ${buildTime.toFixed(2)}ms`)
console.log(` Transforms: ${metrics.transforms}`)
console.log(` Loads: ${metrics.loads}`)
console.log(` Resolves: ${metrics.resolves}`)
},
generateBundle(options, bundle) {
if (enableMonitoring) {
const bundleSize = Object.values(bundle)
.filter(chunk => chunk.type === 'chunk')
.reduce((size, chunk) => size + chunk.code.length, 0)
console.log(`📦 Bundle size: ${(bundleSize / 1024).toFixed(2)} KB`)
}
}
}
}
// 6. Custom Asset Handling
// vite-plugin-custom-assets.js
import { createHash } from 'crypto'
import { readFile } from 'fs/promises'
export function customAssetsPlugin(options = {}) {
const {
extensions = ['.glb', '.gltf', '.fbx'],
outputPath = 'assets/3d'
} = options
return {
name: 'vite-plugin-custom-assets',
async load(id) {
const ext = id.split('.').pop()
if (!extensions.includes(`.${ext}`)) {
return null
}
try {
const buffer = await readFile(id)
const hash = createHash('md5').update(buffer).digest('hex').slice(0, 8)
const filename = `${id.split('/').pop().split('.')[0]}-${hash}.${ext}`
// Emit the asset
this.emitFile({
type: 'asset',
fileName: `${outputPath}/${filename}`,
source: buffer
})
// Return JavaScript module
return `
export default `${outputPath}/${filename}`
export const url = `${outputPath}/${filename}`
export const hash = '${hash}'
`
} catch (error) {
console.error(`Failed to load asset ${id}:`, error)
return null
}
}
}
}
// 7. Advanced HMR Configuration
// vite-plugin-advanced-hmr.js
export function advancedHmrPlugin() {
return {
name: 'vite-plugin-advanced-hmr',
configureServer(server) {
// Custom HMR for specific file types
server.ws.on('connection', (ws) => {
ws.on('message', (data) => {
const parsed = JSON.parse(data)
if (parsed.type === 'custom-reload') {
// Handle custom reload messages
server.ws.send({
type: 'full-reload'
})
}
})
})
// Watch additional files
server.watcher.add(['config/*.json', 'data/*.json'])
// Custom file watching
server.watcher.on('change', (file) => {
if (file.includes('.env')) {
// Reload environment variables
server.ws.send({
type: 'custom',
event: 'env-updated',
data: { file }
})
}
})
}
}
}
// 8. Progressive Web App (PWA) Configuration
// vite-pwa.config.js
import { VitePWA } from 'vite-plugin-pwa'
export const pwaConfig = VitePWA({
registerType: 'autoUpdate',
includeAssets: ['favicon.ico', 'apple-touch-icon.png', 'mask-icon.svg'],
manifest: {
name: 'My Progressive Web App',
short_name: 'MyPWA',
description: 'A modern PWA built with Vite',
theme_color: '#0d47a1',
background_color: '#ffffff',
display: 'standalone',
orientation: 'portrait',
scope: '/',
start_url: '/',
icons: [
{
src: 'pwa-192x192.png',
sizes: '192x192',
type: 'image/png'
},
{
src: 'pwa-512x512.png',
sizes: '512x512',
type: 'image/png'
},
{
src: 'pwa-512x512.png',
sizes: '512x512',
type: 'image/png',
purpose: 'any maskable'
}
]
},
workbox: {
globPatterns: ['**/*.{js,css,html,ico,png,svg,jpg,jpeg,json}'],
runtimeCaching: [
{
urlPattern: /^https:\/\/api\.example\.com\/.*/i,
handler: 'NetworkFirst',
options: {
cacheName: 'api-cache',
expiration: {
maxEntries: 100,
maxAgeSeconds: 60 * 60 * 24
},
cacheableResponse: {
statuses: [0, 200]
}
}
},
{
urlPattern: /^https:\/\/cdn\.example\.com\/.*/i,
handler: 'CacheFirst',
options: {
cacheName: 'static-cache',
expiration: {
maxEntries: 1000,
maxAgeSeconds: 60 * 60 * 24 * 365
}
}
}
]
},
devOptions: {
enabled: true,
type: 'module'
}
})
// 9. Deployment Configuration Examples
// deploy.config.js
export const deploymentConfigs = {
// Vercel deployment
vercel: {
server: {
host: true,
port: 3000
},
build: {
target: 'es2018',
outDir: 'dist'
}
},
// Netlify deployment
netlify: {
build: {
outDir: 'dist'
},
base: '/',
plugins: [
// Redirect rules
{
name: 'redirects',
configureServer(server) {
server.middlewares.use((req, res, next) => {
if (req.path.endsWith('/')) {
const indexPath = path.join(process.cwd(), 'dist', req.path, 'index.html')
if (fs.existsSync(indexPath)) {
return res.sendFile(indexPath)
}
}
next()
})
}
}
]
},
// AWS S3 deployment
s3: {
base: '/my-app/',
build: {
outDir: 'dist',
assetsDir: 'assets',
rollupOptions: {
output: {
assetFileNames: 'assets/[name].[hash].[ext]'
}
}
}
},
// Docker deployment
docker: {
server: {
host: '0.0.0.0',
port: 3000
},
build: {
outDir: 'dist'
}
}
}
// 10. Performance Monitoring Plugin
// vite-plugin-monitoring.js
export function monitoringPlugin(options = {}) {
const {
apiKey = 'your-api-key',
endpoint = 'https://monitoring.example.com/api/metrics',
environment = process.env.NODE_ENV || 'development'
} = options
return {
name: 'vite-plugin-monitoring',
buildStart() {
const startTime = Date.now()
return () => {
const buildTime = Date.now() - startTime
sendMetrics({
type: 'build_time',
value: buildTime,
environment,
timestamp: Date.now()
})
}
},
generateBundle(options, bundle) {
const metrics = {
type: 'bundle_analysis',
environment,
timestamp: Date.now(),
data: {
totalSize: 0,
chunks: 0,
assets: 0
}
}
for (const [fileName, chunk] of Object.entries(bundle)) {
if (chunk.type === 'chunk') {
metrics.data.totalSize += chunk.code.length
metrics.data.chunks++
} else {
metrics.data.totalSize += chunk.source.length
metrics.data.assets++
}
}
sendMetrics(metrics)
}
}
async function sendMetrics(metrics) {
try {
await fetch(endpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': apiKey
},
body: JSON.stringify(metrics)
})
} catch (error) {
console.warn('Failed to send metrics:', error)
}
}
}