- 使用 loadEnv 读取 VITE_DEV_PROXY_TARGET/VITE_DEV_PORT - 注入 public settings 与 dev proxy 使用同源后端地址
125 lines
3.5 KiB
TypeScript
125 lines
3.5 KiB
TypeScript
import { defineConfig, loadEnv, Plugin } from 'vite'
|
|
import vue from '@vitejs/plugin-vue'
|
|
import checker from 'vite-plugin-checker'
|
|
import { resolve } from 'path'
|
|
|
|
/**
|
|
* Vite 插件:开发模式下注入公开配置到 index.html
|
|
* 与生产模式的后端注入行为保持一致,消除闪烁
|
|
*/
|
|
function injectPublicSettings(backendUrl: string): Plugin {
|
|
return {
|
|
name: 'inject-public-settings',
|
|
transformIndexHtml: {
|
|
order: 'pre',
|
|
async handler(html) {
|
|
try {
|
|
const response = await fetch(`${backendUrl}/api/v1/settings/public`, {
|
|
signal: AbortSignal.timeout(2000)
|
|
})
|
|
if (response.ok) {
|
|
const data = await response.json()
|
|
if (data.code === 0 && data.data) {
|
|
const script = `<script>window.__APP_CONFIG__=${JSON.stringify(data.data)};</script>`
|
|
return html.replace('</head>', `${script}\n</head>`)
|
|
}
|
|
}
|
|
} catch (e) {
|
|
console.warn('[vite] 无法获取公开配置,将回退到 API 调用:', (e as Error).message)
|
|
}
|
|
return html
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
export default defineConfig(({ mode }) => {
|
|
// 加载环境变量
|
|
const env = loadEnv(mode, process.cwd(), '')
|
|
const backendUrl = env.VITE_DEV_PROXY_TARGET || 'http://localhost:8080'
|
|
const devPort = Number(env.VITE_DEV_PORT || 3000)
|
|
|
|
return {
|
|
plugins: [
|
|
vue(),
|
|
checker({
|
|
typescript: true,
|
|
vueTsc: true
|
|
}),
|
|
injectPublicSettings(backendUrl)
|
|
],
|
|
resolve: {
|
|
alias: {
|
|
'@': resolve(__dirname, 'src'),
|
|
// 使用 vue-i18n 运行时版本,避免 CSP unsafe-eval 问题
|
|
'vue-i18n': 'vue-i18n/dist/vue-i18n.runtime.esm-bundler.js'
|
|
}
|
|
},
|
|
define: {
|
|
// 启用 vue-i18n JIT 编译,在 CSP 环境下处理消息插值
|
|
// JIT 编译器生成 AST 对象而非 JS 代码,无需 unsafe-eval
|
|
__INTLIFY_JIT_COMPILATION__: true
|
|
},
|
|
build: {
|
|
outDir: '../backend/internal/web/dist',
|
|
emptyOutDir: true,
|
|
rollupOptions: {
|
|
output: {
|
|
/**
|
|
* 手动分包配置
|
|
* 分离第三方库并按功能合并应用代码,避免循环依赖
|
|
*/
|
|
manualChunks(id: string) {
|
|
if (id.includes('node_modules')) {
|
|
// Vue 核心库
|
|
if (
|
|
id.includes('/vue/') ||
|
|
id.includes('/vue-router/') ||
|
|
id.includes('/pinia/') ||
|
|
id.includes('/@vue/')
|
|
) {
|
|
return 'vendor-vue'
|
|
}
|
|
|
|
// UI 工具库(较大,单独分离)
|
|
if (id.includes('/@vueuse/') || id.includes('/xlsx/')) {
|
|
return 'vendor-ui'
|
|
}
|
|
|
|
// 图表库
|
|
if (id.includes('/chart.js/') || id.includes('/vue-chartjs/')) {
|
|
return 'vendor-chart'
|
|
}
|
|
|
|
// 国际化
|
|
if (id.includes('/vue-i18n/') || id.includes('/@intlify/')) {
|
|
return 'vendor-i18n'
|
|
}
|
|
|
|
// 其他小型第三方库合并
|
|
return 'vendor-misc'
|
|
}
|
|
|
|
// 应用代码:按入口点自动分包,不手动干预
|
|
// 这样可以避免循环依赖,同时保持合理的 chunk 数量
|
|
}
|
|
}
|
|
}
|
|
},
|
|
server: {
|
|
host: '0.0.0.0',
|
|
port: devPort,
|
|
proxy: {
|
|
'/api': {
|
|
target: backendUrl,
|
|
changeOrigin: true
|
|
},
|
|
'/setup': {
|
|
target: backendUrl,
|
|
changeOrigin: true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
})
|