import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import path from 'path' import { visualizer } from 'rollup-plugin-visualizer' import viteCompression from 'vite-plugin-compression' import viteImagemin from 'vite-plugin-imagemin' import Components from 'unplugin-vue-components/vite' import { ElementPlusResolver } from 'unplugin-vue-components/resolvers' import AutoImport from 'unplugin-auto-import/vite' export default defineConfig({ plugins: [ vue(), // Auto import components Components({ resolvers: [ElementPlusResolver()], dts: 'src/components.d.ts' }), // Auto import APIs AutoImport({ imports: ['vue', 'vue-router', 'pinia'], dts: 'src/auto-imports.d.ts', resolvers: [ElementPlusResolver()] }), // Gzip compression viteCompression({ verbose: true, disable: false, threshold: 10240, algorithm: 'gzip', ext: '.gz' }), // Brotli compression viteCompression({ verbose: true, disable: false, threshold: 10240, algorithm: 'brotliCompress', ext: '.br' }), // Image optimization viteImagemin({ gifsicle: { optimizationLevel: 7, interlaced: false }, optipng: { optimizationLevel: 7 }, mozjpeg: { quality: 80 }, pngquant: { quality: [0.8, 0.9], speed: 4 }, svgo: { plugins: [ { name: 'removeViewBox' }, { name: 'removeEmptyAttrs', active: false } ] } }), // Bundle analyzer visualizer({ open: true, gzipSize: true, brotliSize: true, filename: 'dist/bundle-analysis.html' }) ], resolve: { alias: { '@': path.resolve(__dirname, './src'), '~': path.resolve(__dirname, './src/assets') } }, server: { port: 3008, host: '0.0.0.0', proxy: { '/api': { target: 'http://localhost:3030', changeOrigin: true }, '/socket.io': { target: 'http://localhost:3030', ws: true, changeOrigin: true } } }, build: { target: 'es2015', outDir: 'dist', assetsDir: 'assets', sourcemap: false, minify: 'terser', terserOptions: { compress: { drop_console: true, drop_debugger: true } }, rollupOptions: { output: { // Manual chunks for better caching manualChunks: { 'element-plus': ['element-plus', '@element-plus/icons-vue'], 'chart': ['chart.js', 'vue-chartjs'], 'vendor': ['vue', 'vue-router', 'pinia'], 'utils': ['axios', 'dayjs', 'lodash-es'], 'i18n': ['vue-i18n'] }, // Asset naming for better caching chunkFileNames: (chunkInfo) => { const facadeModuleId = chunkInfo.facadeModuleId ? chunkInfo.facadeModuleId.split('/').pop() : 'chunk' return `js/[name]-${facadeModuleId}-[hash].js` }, entryFileNames: 'js/[name]-[hash].js', assetFileNames: (assetInfo) => { const info = assetInfo.name.split('.') const ext = info[info.length - 1] if (/png|jpe?g|svg|gif|tiff|bmp|ico/i.test(ext)) { return `images/[name]-[hash][extname]` } else if (/woff|woff2|eot|ttf|otf/i.test(ext)) { return `fonts/[name]-[hash][extname]` } else { return `[ext]/[name]-[hash][extname]` } } } }, // Chunk size warnings chunkSizeWarningLimit: 500, // CSS code splitting cssCodeSplit: true, // Asset inlining threshold assetsInlineLimit: 4096 }, css: { preprocessorOptions: { scss: { additionalData: `@import "@/styles/variables.scss";` } }, // Extract CSS for better caching extract: true, // CSS modules modules: { localsConvention: 'camelCase' } }, optimizeDeps: { include: [ 'vue', 'vue-router', 'pinia', 'axios', 'element-plus', 'dayjs', 'lodash-es' ], exclude: ['@vueuse/core'] }, // Performance optimizations esbuild: { pure: ['console.log', 'console.debug'], legalComments: 'none' } })