import { resolve } from 'path'; import { defineConfig } from 'vite'; import vue from '@vitejs/plugin-vue'; import vueJsx from '@vitejs/plugin-vue-jsx'; import svgLoader from 'vite-svg-loader'; import AutoImport from 'unplugin-auto-import/vite'; import Components from 'unplugin-vue-components/vite'; import UnoCSS from 'unocss/vite'; import configArcoStyleImportPlugin from './config/plugin/arcoStyleImport'; import configCompressPlugin from './config/plugin/compress'; export default defineConfig({ server: { open: true, fs: { strict: true, }, historyApiFallback: true, proxy: { // RBAC 权限管理服务代理 - http://43.139.210.35:5281 // 注意:后端 API 路径包含 /rbac 前缀,所以不需要 rewrite '/rbac': { target: 'http://43.139.210.35:5281', changeOrigin: true, secure: false, configure: (proxy, options) => { proxy.on('proxyReq', (proxyReq, req, res) => { console.log('[RBAC-5281] Sending Request:', req.method, req.url); console.log('[RBAC-5281] Target Path:', proxyReq.path); }); proxy.on('proxyRes', (proxyRes, req, res) => { console.log( '[RBAC-5281] Received Response:', proxyRes.statusCode, req.url ); }); }, }, // 对应 nginx: location /api/trackApi -> http://43.139.210.35:8889 '/api/trackApi': { target: 'http://43.139.210.35:8889', changeOrigin: true, rewrite: (path) => path.replace(/^\/api\/trackApi/, ''), configure: (proxy, options) => { proxy.on('error', (err, req, res) => { console.log('proxy error', err); }); proxy.on('proxyReq', (proxyReq, req, res) => { console.log('[trackApi-8889] Sending Request to the Target:', req.method, req.url); }); proxy.on('proxyRes', (proxyRes, req, res) => { console.log( '[trackApi-8889] Received Response from the Target:', proxyRes.statusCode, req.url ); }); }, }, // 对应 nginx: location /api -> http://43.139.210.35:8888 '/api': { target: 'http://43.139.210.35:8888', changeOrigin: true, rewrite: (path) => path.replace(/^\/api/, ''), timeout: 30000, proxyTimeout: 30000, configure: (proxy, options) => { proxy.on('proxyReq', (proxyReq, req, res) => { console.log('[8888] Sending Request:', req.method, req.url); console.log('[8888] Proxy Target:', proxyReq.path); }); proxy.on('proxyRes', (proxyRes, req, res) => { console.log( '[8888] Received Response:', proxyRes.statusCode, req.url ); }); }, }, }, }, plugins: [ // eslint({ // cache: false, // include: ['src/**/*.ts', 'src/**/*.tsx', 'src/**/*.vue'], // exclude: ['node_modules'], // }), vue(), vueJsx(), svgLoader({ svgoConfig: {} }), configArcoStyleImportPlugin(), UnoCSS(), AutoImport({ imports: ['vue', 'vue-router', 'vue-i18n', 'pinia', '@vueuse/core'], dts: 'src/auto-imports.d.ts', dirs: ['src/composables', 'src/store'], vueTemplate: true, eslintrc: { enabled: true, }, }), Components({ extensions: ['vue'], include: [/\.vue$/, /\.vue\?vue/], dts: 'src/components.d.ts', dirs: ['src/components'], resolvers: [], // directoryAsNamespace: true, }), // 生产环境启用gzip压缩 ...(process.env.NODE_ENV === 'production' ? configCompressPlugin('gzip') : []), ], resolve: { alias: [ { find: '@', replacement: resolve(__dirname, './src'), }, { find: 'assets', replacement: resolve(__dirname, './src/assets'), }, { find: 'vue-i18n', replacement: 'vue-i18n/dist/vue-i18n.cjs.js', // Resolve the i18n warning issue }, { find: 'vue', replacement: 'vue/dist/vue.esm-bundler.js', // compile template }, { find: 'xe-utils', replacement: resolve( __dirname, './node_modules/.pnpm/xe-utils@3.7.5/node_modules/xe-utils' ), // Add xe-utils alias }, ], extensions: ['.ts', '.js'], }, define: { 'process.env': {}, __VUE_PROD_HYDRATION_MISMATCH_DETAILS__: 'false', __VUE_OPTIONS_API__: 'true', __VUE_PROD_DEVTOOLS__: 'false', }, optimizeDeps: { // 移除 monaco-editor,让它按需加载,减少初始加载时间 include: ['vue', 'vue-router', 'pinia'], // 排除 monaco-editor,因为它很大且只在特定页面使用 exclude: ['monaco-editor'], // 确保 Vue 相关依赖的预构建顺序 esbuildOptions: { target: 'es2015', }, }, build: { // 启用压缩(使用esbuild,Vite默认,性能更好) minify: 'esbuild', // 生产环境移除console和debugger esbuild: { drop: process.env.NODE_ENV === 'production' ? ['console', 'debugger'] : [], }, // 启用CSS代码分割 cssCodeSplit: true, // 生成source map(可选,生产环境可以关闭以减小体积) sourcemap: false, // 构建输出配置 rollupOptions: { output: { // 手动代码分割,优化缓存 manualChunks: (id) => { // node_modules中的包单独打包 if (id.includes('node_modules')) { // vue核心库和vue相关库打包在一起,确保加载顺序 if (id.includes('vue') || id.includes('vue-router') || id.includes('pinia') || id.includes('@vue')) { return 'vue-vendor'; } // monaco-editor单独打包(按需加载,不阻塞初始加载) if (id.includes('monaco-editor')) { return 'monaco-editor'; } // echarts 相关库单独打包 if (id.includes('echarts') || id.includes('zrender')) { return 'echarts-vendor'; } // arco-design相关 if (id.includes('@arco-design')) { return 'arco-vendor'; } // vxe-table相关 if (id.includes('vxe-table')) { return 'vxe-vendor'; } // 其他第三方库 return 'vendor'; } }, // 确保文件名包含hash,便于浏览器缓存 chunkFileNames: 'assets/js/[name]-[hash].js', entryFileNames: 'assets/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 `assets/media/[name]-[hash].${ext}`; } if (/\.(png|jpe?g|gif|svg|webp|avif)(\?.*)?$/.test(assetInfo.name)) { return `assets/img/[name]-[hash].${ext}`; } if (/\.(woff2?|eot|ttf|otf)(\?.*)?$/i.test(assetInfo.name)) { return `assets/fonts/[name]-[hash].${ext}`; } return `assets/[name]-[hash].${ext}`; }, }, }, // 设置chunk大小警告限制 chunkSizeWarningLimit: 1000, }, css: { preprocessorOptions: { less: { modifyVars: { hack: `true; @import (reference) "${resolve( './src/assets/style/breakpoint.less' )}";`, }, javascriptEnabled: true, }, }, }, });