Files
competition-website/vite.config.js
qubiaobiao ea350fb2e0 feat(frontend): Element Plus 改按需引入,首屏 425.1->158.8KB(R36)
- vite.config.js: 新增 unplugin-vue-components + ElementPlusResolver;
  optimizeDeps.include 动态生成 118 项无扩展名 specifier(glob/.mjs 皆静默失效)
- main.js: 删 app.use(ElementPlus) 与全量 CSS;改 provideGlobalConfig(locale, app, true);
  显式引入 4 个函数式组件样式(message/message-box/notification/loading)
- views/Upload.vue: 3 处 h(resolveComponent('el-xxx')) 改显式组件引用;清 2 处死导入
- devDep: unplugin-vue-components@^32.1.0
- .gitignore: 忽略 vite.config.js.timestamp-*.mjs

验证:dist 渲染 9/9 · dist 登录后 9/9 · dev 通道致命错误 0/告警 0
瘦身:首屏 425.1->158.8KB(-62.6%);主包 JS 1112.9->281.09KB
2026-09-29 11:44:15 +08:00

114 lines
4.8 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
import fs from 'node:fs'
import path from 'node:path'
import { fileURLToPath, URL } from 'node:url'
// Element Plus 按需样式模块清单(供下方 optimizeDeps.include 使用)。
// 为什么不能用 glob:Vite 的 include 需要「运行时请求的 specifier 形式」(无扩展名,
// 形如 element-plus/es/components/table/style/css),而 glob 匹配的是文件系统条目 ——
// `style/css` 在磁盘上是目录而非文件,glob 会静默匹配 0 项(实测 '*/style/css' → 0 项);
// 写成 '*/style/css.mjs' 虽能匹配 118 项,但其 key 与运行时 specifier 不一致,
// Vite 仍会把每个组件判定为「新依赖」⇒ 等于没配。
// 因此改为扫描目录动态生成:既不硬编码 118 项,也不会随 element-plus 升级失效。
const EP_COMPONENTS_DIR = fileURLToPath(
new URL('./node_modules/element-plus/es/components', import.meta.url)
)
const EP_STYLE_INCLUDES = (() => {
try {
return fs
.readdirSync(EP_COMPONENTS_DIR)
.filter((name) => fs.existsSync(path.join(EP_COMPONENTS_DIR, name, 'style', 'css.mjs')))
.map((name) => `element-plus/es/components/${name}/style/css`)
} catch {
return [] // 依赖尚未安装时降级为空(vite build 不依赖此项)
}
})()
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
// Element Plus 按需引入(替代 main.js 中的 app.use(ElementPlus) 全量注册)。
// 构建期静态扫描模板,只把「实际用到的组件」及其样式打进产物:
// <el-table> → element-plus/es/components/table + table/style/css
// v-loading → ElLoadingDirective(resolver 的 directives 默认开启)
// 注意两处边界(改前已核对):
// ① 运行时字符串解析(h('el-button') / resolveComponent('el-icon'))不归它管
// —— 已改为显式组件引用,见 views/Upload.vue;
// ② 函数式 API(ElMessage/ElMessageBox/...)不会被注入样式,
// —— 已在 main.js 里显式引入这四个样式。
// dirs 置空 = 不额外扫描 src/components 目录,保持原有显式 import 行为不变,
// 只让本插件负责 Element Plus 一个职责(最小改动)。
Components({
dts: 'src/components.d.ts',
dirs: [],
resolvers: [ElementPlusResolver()],
}),
// 修复 Vite 内部 viteTransformMiddleware 调用 decodeURI 时遇到格式错误的 URL
// 导致 "URI malformed" 错误的问题。在 Vite 中间件之前拦截并安全处理。
{
name: 'fix-uri-malformed',
configureServer(server) {
server.middlewares.use((req, res, next) => {
if (req.url) {
try {
// 预检 URL 是否可被 decodeURI 安全解码
decodeURI(req.url)
} catch (_e) {
// URL 包含无效的百分号编码序列,直接返回 400,避免进入 Vite 内部中间件
console.warn(`[fix-uri-malformed] 拦截到格式错误的请求 URL,已返回 400`)
res.statusCode = 400
res.end()
return
}
}
next()
})
},
},
],
// 依赖预构建:显式声明「按需引入」注入的 Element Plus 样式模块。
// 必要性:unplugin-vue-components 是在 transform 阶段「运行时注入」style import 的,
// 而 Vite 启动期的依赖扫描基于 index.html,看不到这些被注入的模块 ⇒ 运行中才发现
// ⇒ 触发「二次预构建」(日志里的 new dependencies optimized + reloading)。
// 后果不只是「访问新页面时整页重载」;在带 safe-delete 钩子的受限沙箱里,
// 二次预构建清理临时目录会被钩子拦下,导致 dev server 进程直接退出。
// 显式声明后首次预构建即完整,dev 启动一次成型。
// ⚠️ 本项只作用于 dev(vite build 不做依赖预构建),不影响任何构建产物。
optimizeDeps: {
include: ['element-plus/es', ...EP_STYLE_INCLUDES],
},
// 开发服务器配置
server: {
host: '0.0.0.0',
port: 5173,
// 禁用 HMR 错误覆盖层,避免非致命错误(如 URI malformed)遮挡页面
hmr: {
overlay: false,
},
// 代理配置:将 /api 和 /public 请求转发到后端服务
proxy: {
'/api': {
target: 'http://127.0.0.1:7001',
changeOrigin: true,
},
'/public': {
target: 'http://127.0.0.1:7001',
changeOrigin: true,
},
},
},
// 路径别名配置
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url)),
},
},
})