/// import { defineConfig, loadEnv } from 'vite' import vue from '@vitejs/plugin-vue' import path from 'path' import { visualizer } from 'rollup-plugin-visualizer' import { createHtmlPlugin } from 'vite-plugin-html' import autoprefixer from 'autoprefixer' import cssnano from 'cssnano' // https://vitejs.dev/config/ export default defineConfig(({ command, mode }) => { const env = loadEnv(mode, process.cwd(), '') const isProduction = mode === 'production' return { plugins: [ vue(), // HTML 模板处理 createHtmlPlugin({ minify: isProduction, inject: { data: { title: env.VITE_APP_TITLE || '考培练系统', description: '考培练系统 - 智能学习平台' } } }), // 打包分析(仅在分析模式下) ...(process.env.ANALYZE === 'true' ? [visualizer({ filename: 'dist/stats.html', open: true, gzipSize: true, brotliSize: true })] : []) ].filter(Boolean), resolve: { alias: { '@': path.resolve(__dirname, './src') } }, // CSS 配置 css: { preprocessorOptions: { scss: { additionalData: `@import "@/style/variables.scss";` } }, // 生产环境压缩CSS ...(isProduction && { postcss: { plugins: [ autoprefixer, cssnano({ preset: 'default' }) ] } }) }, // 构建配置 build: { target: 'es2015', outDir: 'dist', assetsDir: 'assets', sourcemap: !isProduction, minify: isProduction ? 'terser' : false, // Terser 压缩配置 terserOptions: { compress: { drop_console: isProduction, drop_debugger: isProduction, pure_funcs: isProduction ? ['console.log', 'console.info'] : [] } }, // 代码分割配置 rollupOptions: { output: { // 手动分割代码块 manualChunks: { // Vue 相关 vue: ['vue', 'vue-router', 'pinia'], // UI 库 'element-plus': ['element-plus', '@element-plus/icons-vue'], // 图表库 echarts: ['echarts'], // 工具库 utils: ['axios'] }, // 文件命名 chunkFileNames: 'assets/js/[name]-[hash].js', entryFileNames: 'assets/js/[name]-[hash].js', assetFileNames: (assetInfo) => { const info = assetInfo.name.split('.') let extType = info[info.length - 1] if (/\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/i.test(assetInfo.name)) { extType = 'media' } else if (/\.(png|jpe?g|gif|svg)(\?.*)?$/i.test(assetInfo.name)) { extType = 'img' } else if (/\.(woff2?|eot|ttf|otf)(\?.*)?$/i.test(assetInfo.name)) { extType = 'fonts' } return `assets/${extType}/[name]-[hash].[ext]` } } }, // 警告大小限制 chunkSizeWarningLimit: 1000, // 压缩资源 assetsInlineLimit: 4096 }, // 开发服务器配置 server: { port: 3001, host: '0.0.0.0', strictPort: false, open: false, cors: true, allowedHosts: [ 'aiedu.ireborn.com.cn', 'kpl.ireborn.com.cn', 'kaopeilian-frontend-dev', 'kpl-frontend-dev', '120.79.247.16', 'localhost' ], hmr: { clientPort: 443, protocol: 'wss' }, proxy: { '/api': { target: 'http://backend-dev:8000', changeOrigin: true, rewrite: (path) => path, configure: (proxy, options) => { proxy.on('proxyRes', (proxyRes, req, res) => { // 确保代理响应正确设置 UTF-8 编码 proxyRes.headers['content-type'] = 'application/json; charset=utf-8' }) } }, '/ws': { target: 'ws://backend-dev:8000', ws: true, changeOrigin: true } } }, // 预览服务器配置 preview: { port: 4173, host: true, cors: true }, // 优化配置 optimizeDeps: { include: [ 'vue', 'vue-router', 'pinia', 'element-plus', '@element-plus/icons-vue', 'echarts' ], exclude: ['@vitejs/plugin-vue'] }, // 测试配置 test: { environment: 'jsdom', globals: true, setupFiles: ['./src/test/setup.ts'], include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], exclude: ['node_modules', 'dist', '.idea', '.git', '.cache'], coverage: { provider: 'v8', reporter: ['text', 'json', 'html'], exclude: [ 'node_modules/', 'src/test/', '**/*.d.ts', '**/*.config.*', '**/coverage/**', '**/dist/**', '**/.{idea,git,cache,output,temp}/**' ], thresholds: { global: { branches: 60, functions: 60, lines: 60, statements: 60 } } } }, // 环境变量配置 define: { __VUE_OPTIONS_API__: true, __VUE_PROD_DEVTOOLS__: !isProduction, __APP_VERSION__: JSON.stringify(process.env.npm_package_version || '1.0.0') }, // 日志级别 logLevel: isProduction ? 'info' : 'warn' } })