import { ConfigEnv, UserConfig, defineConfig, loadEnv } from 'vite' import viteCompression from 'vite-plugin-compression' import vue from '@vitejs/plugin-vue' import path from 'path' import { visualizer } from 'rollup-plugin-visualizer' // 打包分析插件 import { constants } from 'zlib' // 静态引入zlib常量 import Components from 'unplugin-vue-components/vite' import AutoImport from 'unplugin-auto-import/vite' import { ElementPlusResolver } from 'unplugin-vue-components/resolvers' // 获取当前时间戳,用于构建输出的文件名 const Timestamp = new Date().getTime(); // 路径解析辅助函数 function _resolve(dir: string) { return path.resolve(__dirname, dir); } export default defineConfig(({ mode }: ConfigEnv): UserConfig => { // 判断当前是否为生产环境 const isProd = mode === 'production' // 加载环境变量文件(.env) const env = loadEnv(mode, process.cwd()) const enableVisualizer = isProd && env.VITE_VISUALIZER === 'true' return { // 基础公共路径,从环境变量VITE_ADDRESS_BASE_URL获取 base: env.VITE_ADDRESS_BASE_URL, // CSS预处理器配置 css: { preprocessorOptions: { scss: { api: 'modern-compiler', // 使用现代Sass编译器API silenceDeprecations: ['legacy-js-api'], additionalData: `@use "@/assets/styles/variables" as *; @use "@/assets/styles/mixins" as *;` } } }, // 插件配置 plugins: [ // Vue插件 vue(), // Gzip压缩插件(仅生产环境启用) viteCompression({ verbose: true, // 显示压缩日志 disable: !isProd, // 非生产环境禁用 deleteOriginFile: false, // 不删除源文件 threshold: 1024, // 文件大小1KB才压缩(降低阈值) algorithm: 'gzip', // 压缩算法 ext: '.gz', // 压缩文件扩展名 compressionOptions: { level: 9, // 最高压缩级别 } }), // 添加Brotli压缩支持(更高效的压缩算法) isProd && viteCompression({ verbose: true, disable: !isProd, deleteOriginFile: false, threshold: 1024, algorithm: 'brotliCompress', ext: '.br', compressionOptions: { params: { [constants.BROTLI_PARAM_QUALITY]: 11, // 最高质量 } } }), // Auto-import Element Plus components from templates Components({ resolvers: [ElementPlusResolver({ importStyle: 'css' })], dts: false }), // Auto-import Element Plus APIs (ElMessage, ElNotification, etc.) AutoImport({ resolvers: [ElementPlusResolver()], imports: ['vue', 'vue-router', 'pinia'], dts: false }), // 打包分析插件(仅在显式启用时生效;容器/CI 默认不打开浏览器) enableVisualizer && visualizer({ open: false }) ].filter(Boolean), // 过滤掉false的插件 // 开发服务器配置 server: { host: '0.0.0.0', // 监听所有IP port: 3000, // 端口号 open: true, // 自动打开浏览器 // https: false, // 不启用HTTPS cors: true, // 启用CORS // 开发环境热更新优化 hmr: { overlay: true }, // WSL2热更新修复 watch: { usePolling: true, interval: 1000 }, }, // 依赖优化配置 optimizeDeps: { include: [ 'vue', 'vue-router', 'pinia', 'element-plus', '@element-plus/icons-vue', 'axios', 'vue-i18n', 'echarts/core', 'echarts/charts', 'echarts/components', 'echarts/renderers', 'vue-echarts', '@vueuse/core' ] }, // 模块解析配置 resolve: { alias: { // 路径别名 '@': _resolve('src'), // 源码目录 '@assets': _resolve('src/assets'), // 资源目录 '@views': _resolve('src/views'), // 视图目录 '@components': _resolve('src/components'), // 组件目录 '@utils': _resolve('src/utils'), // 工具函数 '@router': _resolve('src/router'), // 路由配置 '@store': _resolve('src/store'), // 状态管理 } }, // ESBuild配置 esbuild: { pure: isProd ? ["console.log", "debugger"] : [] // 生产环境移除console.log和debugger }, // 构建配置 build: { minify: 'terser', // 使用terser进行代码压缩 assetsInlineLimit: 4 * 1024, // 小于4KB的资源转为base64(优化) sourcemap: !isProd, // 开发环境生成sourcemap,生产环境不生成 outDir: env.VITE_OUTDIR || 'dist', // 输出目录 emptyOutDir: true, // 构建前清空输出目录 chunkSizeWarningLimit: 1000, // 降低块大小警告阈值至1MB cssCodeSplit: true, // CSS代码分割 terserOptions: { compress: { drop_console: isProd, // 生产环境移除console drop_debugger: isProd, // 生产环境移除debugger pure_funcs: isProd ? ['console.log'] : [], // 移除纯函数调用 // 额外的压缩优化 dead_code: true, // 移除无用代码 keep_infinity: true, // 保持Infinity toplevel: isProd, // 顶级作用域压缩 }, mangle: { toplevel: isProd, // 顶级作用域变量名压缩 } }, rollupOptions: { // Rollup打包配置 // 启用构建缓存以提升重复构建速度 cache: isProd, // 生产环境启用Rollup缓存 output: { // 优化的手动分块策略 manualChunks: { // Vue core vue: ['vue', 'vue-router', 'pinia'], // Element Plus (tree-shaken via auto-import, but base bundle still needed) elementPlus: ['element-plus'], // Element Plus icons elementIcons: ['@element-plus/icons-vue'], // ECharts (large library, isolated for better caching) echarts: ['echarts', 'vue-echarts'], // Utilities utils: ['axios'], // i18n vendor: ['vue-i18n'] }, // 输出文件命名规则(优化缓存策略) chunkFileNames: `static/js/[name].[hash].js`, entryFileNames: `static/js/[name].[hash].js`, assetFileNames: (assetInfo) => { const name = assetInfo.name || '' if (/\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/i.test(name)) { return `static/media/[name].[hash][extname]` } else if (/\.(png|jpe?g|gif|svg|ico|webp)(\?.*)?$/i.test(name)) { return `static/images/[name].[hash][extname]` } else if (/\.(woff2?|eot|ttf|otf)(\?.*)?$/i.test(name)) { return `static/fonts/[name].[hash][extname]` } else { return `static/assets/[name].[hash][extname]` } } }, // 外部化依赖(CDN优化,可选) external: isProd ? [] : [] } }, // JSON文件处理配置 json: { stringify: false } } })