import { fileURLToPath, URL } from 'node:url' import { defineConfig, loadEnv } from 'vite' import vue from '@vitejs/plugin-vue' import vueJsx from '@vitejs/plugin-vue-jsx' import AutoImport from 'unplugin-auto-import/vite' import Components from 'unplugin-vue-components/vite' import { ElementPlusResolver } from 'unplugin-vue-components/resolvers' import viteCompression from 'vite-plugin-compression' import vueSetupExtend from 'vite-plugin-vue-setup-extend' import viteImagemin from 'vite-plugin-imagemin' import { createSvgIconsPlugin } from 'vite-plugin-svg-icons' import { visualizer } from 'rollup-plugin-visualizer' import Unocss from 'unocss/vite' import VueDevTools from 'vite-plugin-vue-devtools' import { resolve } from 'path' import { createHtmlPlugin } from 'vite-plugin-html' // https://vitejs.dev/config/ export default defineConfig(({ mode }) => { let root = process.cwd() const env = loadEnv(mode, root) return { base: env.VITE_PUBLIC_PATH, root, resolve: { alias: { '@': fileURLToPath(new URL('./src', import.meta.url)) } }, css: { preprocessorOptions: { scss: { additionalData: '@use "@/styles/element/index.scss";', api: 'modern-compiler', }, }, }, server: { host: '0.0.0.0', port: 3200, open: true, proxy: { '/api': { // target: 'http://localhost:9089', target: 'https://demo.lowflow.vip/api', changeOrigin: true, ws: true, rewrite: (path) => path.replace(/^\/api/, ''), secure: false } } }, plugins: [ vue(), vueJsx(), Unocss({ configFile: './uno.config.ts' }), VueDevTools(), // 允许 setup 语法糖上添加组件名属性 vueSetupExtend(), // 打包后生产依赖大小分析 visualizer({ filename: './node_modules/.cache/visualizer/stats.html', open: true, gzipSize: true, brotliSize: true }), AutoImport({ imports: ['vue', 'vue-router'], resolvers: [ElementPlusResolver()], dts: 'src/typings/auto-imports.d.ts', eslintrc: { enabled: true, filepath: './.eslintrc-auto-import.json' } }), // 注入模板数据 createHtmlPlugin({ inject: { data: { title: env.VITE_GLOB_APP_TITLE } } }), // svg 图标 createSvgIconsPlugin({ iconDirs: [resolve(root, 'src/assets/icons')], symbolId: 'icon-[dir]-[name]' }), Components({ extensions: ['vue', 'tsx', 'md'], globs: ['src/components/*/*.vue', 'src/components/*/*.tsx'], include: [/\.vue$/, /\.vue\?vue/, /\.md$/, /\.[tj]sx?$/], resolvers: [ ElementPlusResolver({ importStyle: 'sass' }) ], dts: 'src/typings/components.d.ts' }), // 图片压缩 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' } ] } }), // gzip 压缩资源 viteCompression({ // 压缩算法使用 gzip algorithm: 'gzip', // 压缩后的文件后缀 ext: '.gz', // 是否删除原始文件 deleteOriginFile: false, // 资源文件大于1024B=1kB时会被压缩 threshold: 1024 }), // 生产环境 brotli 压缩资源 viteCompression({ // 压缩算法使用 brotli algorithm: 'brotliCompress', // 压缩后的文件后缀 ext: '.br', // 是否删除原始文件 deleteOriginFile: false, // 资源文件大于1024B=1kB时会被压缩 threshold: 1024 }) ], build: { rollupOptions: { output: { // 打包分类 chunkFileNames: 'assets/js/[name]-[hash].js', entryFileNames: 'assets/js/[name]-[hash].js', assetFileNames: 'assets/[ext]/[name]-[hash].[ext]', // 分包策略 manualChunks: { vue: ['vue'], 'element-plus-icons': ['@element-plus/icons-vue'], 'vue-router': ['vue-router'], 'element-plus': ['element-plus'], 'vue-i18n': ['vue-i18n'] }, // 打包后的文件夹名称生成规则-->解决部分静态服务器无法正常返回_plugin-vue_export-helper文件 sanitizeFileName(name) { const match = /^[a-z]:/i.exec(name) const driveLetter = match ? match[0] : '' return ( driveLetter + name .substring(driveLetter.length) // eslint-disable-next-line no-control-regex .replace(/[\x00-\x1F\x7F<>*#"{}|^[\]`;?:&=+$,]/g, '') ) } } } } } })