vite.config.ts 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. import vue from '@vitejs/plugin-vue';
  2. import { resolve } from 'path';
  3. import { defineConfig, loadEnv, ConfigEnv } from 'vite';
  4. import vueSetupExtend from 'vite-plugin-vue-setup-extend';
  5. import compression from 'vite-plugin-compression2';
  6. import { buildConfig } from './src/utils/build';
  7. import vueJsx from '@vitejs/plugin-vue-jsx';
  8. import { CodeInspectorPlugin } from 'code-inspector-plugin';
  9. import fs from 'fs';
  10. import { visualizer } from 'rollup-plugin-visualizer';
  11. import { webUpdateNotice } from '@plugin-web-update-notification/vite';
  12. import vitePluginsAutoI18n, { EmptyTranslator } from 'vite-auto-i18n-plugin';
  13. const pathResolve = (dir: string) => {
  14. return resolve(__dirname, '.', dir);
  15. };
  16. const alias: Record<string, string> = {
  17. '/@': pathResolve('./src/'),
  18. 'vue-i18n': 'vue-i18n/dist/vue-i18n.cjs.js',
  19. 'ezuikit-js': pathResolve('node_modules/ezuikit-js/ezuikit.js'),
  20. };
  21. const viteConfig = defineConfig((mode: ConfigEnv) => {
  22. const env = loadEnv(mode.mode, process.cwd());
  23. /** dev 代理目标:始终本机后端。勿用浏览器用的公网 VITE_API_URL,避免绕一圈且易配错 */
  24. const apiProxyTarget = env.VITE_PROXY_TARGET || 'http://127.0.0.1:5005';
  25. const devPort = Number(env.VITE_PORT) || 8888;
  26. /** 浏览器访问用公网 IP/域名;勿写入 server.hmr.host,否则 WS 会 bind 该地址,云主机 EIP 常不在网卡上 → EADDRNOTAVAIL */
  27. const devPublicHost = String(env.VITE_DEV_PUBLIC_HOST || '').trim();
  28. fs.writeFileSync('./public/config.js', `window.__env__ = ${JSON.stringify(env, null, 2)} `);
  29. return {
  30. plugins: [
  31. visualizer({ open: false }),
  32. CodeInspectorPlugin({
  33. bundler: 'vite',
  34. hotKeys: ['shiftKey'],
  35. }),
  36. vue(),
  37. vueJsx(),
  38. webUpdateNotice({
  39. versionType: 'build_timestamp',
  40. notificationConfig: {
  41. placement: 'topLeft',
  42. },
  43. notificationProps: {
  44. title: '📢 系统更新',
  45. description: '系统更新啦,请刷新页面!',
  46. buttonText: '刷新',
  47. dismissButtonText: '忽略',
  48. },
  49. }),
  50. vueSetupExtend(),
  51. ...(mode.command === 'build'
  52. ? [
  53. compression({
  54. deleteOriginalAssets: false,
  55. threshold: 5120,
  56. skipIfLargerOrEqual: true,
  57. }),
  58. ]
  59. : []),
  60. JSON.parse(env.VITE_OPEN_CDN) ? buildConfig.cdn() : null,
  61. vitePluginsAutoI18n({
  62. enabled: false,
  63. originLang: 'zh-cn',
  64. targetLangList: ['zh-hk', 'zh-tw', 'en', 'it'],
  65. translator: new EmptyTranslator(),
  66. }),
  67. ],
  68. root: process.cwd(),
  69. resolve: { alias },
  70. base: mode.command === 'serve' ? './' : env.VITE_PUBLIC_PATH,
  71. optimizeDeps: { exclude: ['vue-demi'] },
  72. server: {
  73. host: '0.0.0.0',
  74. port: env.VITE_PORT as unknown as number,
  75. open: JSON.parse(env.VITE_OPEN),
  76. ...(mode.command === 'serve' && devPublicHost
  77. ? { origin: `http://${devPublicHost}:${devPort}` }
  78. : {}),
  79. // VITE_HMR=false 可关热更新。公网访问时不要给 hmr.host 填 EIP(见 devPublicHost 注释)
  80. // 勿设 hmr.port === server.port:Vite 8 会先把 WS 占住该端口,HTTP 再监听会冲突并退到 8889,浏览器仍访问 8888 会 426
  81. hmr: env.VITE_HMR === 'false' ? false : true,
  82. // 文件监视过多时 inotify 会 ENOSPC;设 VITE_WATCH_POLLING=true 可改用轮询(略耗 CPU)
  83. watch:
  84. env.VITE_WATCH_POLLING === 'true'
  85. ? { usePolling: true, interval: 1500 }
  86. : undefined,
  87. proxy: {
  88. // 勿对 /api 开 ws:true,在部分环境下会导致普通 GET 挂起无响应
  89. '/api': {
  90. target: apiProxyTarget,
  91. changeOrigin: true,
  92. },
  93. '/upload': {
  94. target: apiProxyTarget,
  95. changeOrigin: true,
  96. },
  97. '/Upload': {
  98. target: apiProxyTarget,
  99. changeOrigin: true,
  100. },
  101. '/hubs': {
  102. target: apiProxyTarget,
  103. changeOrigin: true,
  104. ws: true,
  105. },
  106. '/schedule': {
  107. target: apiProxyTarget,
  108. changeOrigin: true,
  109. },
  110. },
  111. },
  112. build: {
  113. outDir: 'dist',
  114. chunkSizeWarningLimit: 1500,
  115. assetsInlineLimit: 5000, // 小于此阈值的导入或引用资源将内联为 base64 编码
  116. sourcemap: false, // 构建后是否生成 source map 文件
  117. extractComments: false, // 移除注释
  118. // esbuild 压缩内存占用远低于 terser;maxParallelFileOps 降低并发,便于小内存环境完成构建
  119. minify: 'esbuild',
  120. esbuild: {
  121. drop: mode.mode === 'production' ? ['console', 'debugger'] : [],
  122. },
  123. rollupOptions: {
  124. output: {
  125. chunkFileNames: 'assets/js/[name]-[hash].js', // 引入文件名的名称
  126. entryFileNames: 'assets/js/[name]-[hash].js', // 包的入口文件名称
  127. assetFileNames: 'assets/[ext]/[name]-[hash].[ext]', // 资源文件像 字体,图片等
  128. // 小内存环境构建时可去掉 manualChunks,降低 Rollup 分析内存(大机器可恢复分包策略)
  129. ...(process.env.VITE_LOW_MEM_BUILD === '1'
  130. ? {}
  131. : {
  132. manualChunks(id: string) {
  133. if (id.includes('node_modules')) {
  134. return id.toString().match(/\/node_modules\/(?!.pnpm)(?<moduleName>[^\/]*)\//)?.groups!.moduleName ?? 'vender';
  135. }
  136. },
  137. }),
  138. },
  139. ...(JSON.parse(env.VITE_OPEN_CDN) ? { external: buildConfig.external } : {}),
  140. },
  141. },
  142. css: { preprocessorOptions: { css: { charset: false }, scss: { silenceDeprecations: ['legacy-js-api', 'global-builtin', 'fs-importer-cwd', 'import'] } } },
  143. define: {
  144. __VUE_I18N_LEGACY_API__: JSON.stringify(false),
  145. __VUE_I18N_FULL_INSTALL__: JSON.stringify(false),
  146. __INTLIFY_PROD_DEVTOOLS__: JSON.stringify(false),
  147. __NEXT_VERSION__: JSON.stringify(process.env.npm_package_version),
  148. __NEXT_NAME__: JSON.stringify(process.env.npm_package_name),
  149. },
  150. };
  151. });
  152. export default viteConfig;