test
Some checks failed
Deploy / deploy (push) Has been cancelled

This commit is contained in:
2025-09-29 21:44:59 +08:00
parent ad7ea92769
commit a2617a2625
7 changed files with 716 additions and 188 deletions

View File

@@ -1,187 +0,0 @@
# 分页组件重构总结
## 🎯 重构目标
通过创建 `usePagination` composable 来减少项目中分页相关的重复代码,统一分页逻辑。
## 📋 已完成的更新
### 1. 核心 Composable
-**创建 `usePagination` composable** (`/shared/composables/pagination.ts`)
- 自动处理分页状态管理(页码、每页条数)
- 自动同步 URL 查询参数
- 支持自定义查询条件
- 提供便捷的清空、重置方法
- 完整的 TypeScript 类型支持
### 2. 前台用户页面
-**问题列表页** (`/oj/problem/list.vue`)
- 移除 25+ 行重复代码
- 简化查询逻辑
- 保留防抖搜索功能
-**提交记录页** (`/oj/submission/list.vue`)
- 简化复杂的查询条件处理
- 统一 URL 同步逻辑
- 保留所有筛选功能
-**比赛列表页** (`/oj/contest/list.vue`)
- 减少路由处理代码
- 统一分页行为
### 3. 管理员页面
-**用户管理页** (`/admin/user/list.vue`)
- 简化用户查询和筛选逻辑
- 统一分页处理
-**题目管理页** (`/admin/problem/list.vue`)
- 减少重复的路由同步代码
- 保留题目搜索功能
## 📊 重构效果
### 代码减少统计
- **每个页面减少代码量**: 20-30 行
- **总计减少代码量**: 约 150+ 行
- **重复逻辑消除**: 100%
### 重构前后对比
#### 重构前(每个分页页面都需要)
```vue
<script setup>
const route = useRoute()
const router = useRouter()
const query = reactive({
page: parseInt(route.query.page) || 1,
limit: parseInt(route.query.limit) || 10,
keyword: route.query.keyword ?? "",
// ... 其他查询条件
})
function routerPush() {
router.push({
path: route.path,
query: filterEmptyValue(query),
})
}
async function loadData() {
// 从 URL 同步状态
query.keyword = route.query.keyword ?? ""
query.page = parseInt(route.query.page) || 1
query.limit = parseInt(route.query.limit) || 10
// ... 其他同步逻辑
// API 调用
const res = await api.getData(...)
}
// 大量的 watch 逻辑
watch(() => query.page, routerPush)
watch(() => [query.limit, /* 其他条件 */], () => {
query.page = 1
routerPush()
})
watchDebounced(() => query.keyword, () => {
query.page = 1
routerPush()
}, { debounce: 500 })
</script>
```
#### 重构后(简洁高效)
```vue
<script setup>
// 一行代码完成所有分页逻辑
const { query, clearQuery } = usePagination({
keyword: "",
// ... 其他查询条件
})
async function loadData() {
// 直接使用 query无需同步逻辑
const res = await api.getData({
offset: (query.page - 1) * query.limit,
limit: query.limit,
keyword: query.keyword,
})
}
// 简化的监听逻辑
watchDebounced(() => query.keyword, loadData, { debounce: 500 })
watch(() => [query.page, query.limit], loadData)
</script>
```
## 🚀 主要优势
### 1. 代码复用性
- 所有分页逻辑集中管理
- 统一的行为模式
- 减少维护成本
### 2. 类型安全
- 完整的 TypeScript 支持
- 自定义查询条件的类型推导
- 编译时错误检查
### 3. 自动化处理
- 自动 URL 同步
- 自动页码重置
- 自动路由监听
- 浏览器前进后退支持
### 4. 灵活配置
- 可自定义默认值
- 可选择重置行为
- 支持复杂查询条件
## 🔧 使用方法
### 基本用法
```typescript
const { query } = usePagination()
// query.page, query.limit 自动可用
```
### 带查询条件
```typescript
const { query, clearQuery } = usePagination({
keyword: "",
status: "",
category: "",
})
```
### 配置选项
```typescript
const { query } = usePagination(
{ keyword: "" },
{
defaultLimit: 20,
defaultPage: 1,
resetPageOnChange: true,
}
)
```
## 📝 迁移指南
1. 导入 `usePagination`
2. 替换原有的 `query` 定义
3. 移除 `routerPush` 函数
4. 简化 `watch` 逻辑
5. 更新 `clear` 函数使用 `clearQuery`
## 🎉 总结
这次重构成功地:
- **大幅减少了重复代码**(每个页面减少 20-30 行)
- **统一了分页行为**(所有页面行为一致)
- **提升了开发效率**(新页面只需 1 行代码即可获得完整分页功能)
- **增强了类型安全**(完整的 TypeScript 支持)
- **保持了组件纯净性**Pagination.vue 组件职责单一)
通过这个 composable未来添加新的分页页面将变得非常简单只需要一行代码就能获得完整的分页功能。

View File

@@ -0,0 +1,331 @@
# Naive UI 组件颗粒度拆分优化指南
本文档详细介绍了如何对 Naive UI 进行组件颗粒度的拆分,以优化应用的性能和加载速度。
## 📋 目录
1. [优化概述](#优化概述)
2. [已实施的优化](#已实施的优化)
3. [使用方法](#使用方法)
4. [性能监控](#性能监控)
5. [最佳实践](#最佳实践)
6. [故障排除](#故障排除)
## 🎯 优化概述
### 主要优化目标
- **减少初始包大小**: 通过按需加载减少首次加载时间
- **提高缓存效率**: 将组件按功能模块分组,提高缓存命中率
- **优化用户体验**: 根据页面类型预加载相关组件
- **降低内存占用**: 避免加载不必要的组件代码
### 优化策略
1. **代码分割**: 将 Naive UI 组件按功能分组打包
2. **懒加载**: 按需加载组件,避免一次性加载所有组件
3. **预加载**: 根据路由智能预加载相关组件
4. **Tree Shaking**: 确保只打包实际使用的组件代码
## ✅ 已实施的优化
### 1. 精细化代码分割
`rsbuild.config.ts` 中配置了以下分割策略:
```typescript
// 按功能模块分割 Naive UI 组件
cacheGroups: {
"naive-ui-core": { // 核心组件(必需)
test: /config-provider|theme|locale|loading-bar|message|notification|dialog/,
priority: 30
},
"naive-ui-form": { // 表单组件
test: /form|input|select|date-picker|time-picker|upload/,
priority: 25
},
"naive-ui-data": { // 数据展示组件
test: /table|data-table|list|tree|transfer|pagination/,
priority: 25
},
"naive-ui-layout": { // 布局组件
test: /layout|grid|space|divider|card|collapse/,
priority: 25
},
"naive-ui-feedback": { // 反馈组件
test: /modal|drawer|popover|tooltip|spin|skeleton/,
priority: 25
},
"naive-ui-navigation": { // 导航组件
test: /menu|dropdown|tabs|steps|breadcrumb/,
priority: 25
},
"naive-ui-display": { // 显示组件
test: /tag|badge|avatar|image|carousel|calendar/,
priority: 25
}
}
```
### 2. 组件懒加载系统
创建了 `src/utils/lazy-components.ts` 提供:
- **懒加载组件工厂**: 创建按需加载的组件
- **预加载策略**: 根据页面类型预加载相关组件
- **路由守卫集成**: 在路由切换时自动预加载
### 3. 智能预加载
根据不同页面类型预加载相应组件:
- **首页**: 预加载核心组件Button, Input, Card, Space, Flex
- **列表页**: 预加载数据组件DataTable, Pagination, Tag, Badge
- **编辑页**: 预加载表单组件Form, FormItem, Select, DatePicker, Upload
- **管理员页面**: 预加载管理组件Modal, Drawer, Popconfirm, Editor
### 4. 包体积分析工具
提供了 `scripts/analyze-bundle.js` 用于:
- 分析打包结果
- 识别大文件和重复代码
- 生成优化建议
- 监控 Naive UI 组件占比
## 🚀 使用方法
### 运行包体积分析
```bash
# 分析当前构建
npm run analyze
# 构建并分析
npm run build:analyze
```
### 使用懒加载组件
```vue
<script setup lang="ts">
import { LazyComponents } from '~/utils/lazy-components'
// 使用懒加载的数据表格
const LazyDataTable = LazyComponents.DataTable()
</script>
<template>
<LazyDataTable :data="tableData" :columns="columns" />
</template>
```
### 手动预加载组件
```typescript
import { PreloadStrategies } from '~/utils/lazy-components'
// 在需要时预加载表单组件
await PreloadStrategies.preloadForm()
```
## 📊 性能监控
### 关键指标
监控以下指标来评估优化效果:
1. **首次内容绘制 (FCP)**: 应该有显著提升
2. **最大内容绘制 (LCP)**: 页面主要内容加载时间
3. **首次输入延迟 (FID)**: 用户交互响应时间
4. **累积布局偏移 (CLS)**: 页面稳定性
### 使用分析工具
```bash
# 查看详细的包分析报告
npm run build:analyze
# 检查生成的分析文件
cat bundle-analysis.json
```
### 浏览器开发者工具
1. 打开 Network 面板
2. 刷新页面观察资源加载
3. 查看 Coverage 面板了解代码使用率
4. 使用 Lighthouse 进行性能评估
## 💡 最佳实践
### 1. 组件选择原则
- **优先使用轻量级组件**: 如 `n-button` 而不是 `n-popconfirm`
- **按需导入具体组件**: 避免导入整个 Naive UI 库
- **合理使用复合组件**: 在需要时才使用复杂的组合组件
### 2. 路由级优化
```typescript
// ✅ 好的做法:按功能模块组织路由
const routes = [
{
path: '/admin',
component: () => import('~/layouts/AdminLayout.vue'), // 管理员布局
children: [
{
path: 'users',
component: () => import('~/admin/UserManagement.vue') // 用户管理
}
]
}
]
// ❌ 避免:在一个组件中使用过多不同类型的组件
```
### 3. 组件导入策略
```vue
<script setup lang="ts">
// ✅ 好的做法:明确导入需要的组件
import { NButton, NInput, NForm } from 'naive-ui'
// ✅ 或者使用自动导入(已配置)
// 组件会自动按需导入
// ❌ 避免:导入整个库
// import * as naive from 'naive-ui'
</script>
```
### 4. 样式优化
```css
/* ✅ 好的做法:使用 CSS 变量自定义主题 */
:root {
--n-color-primary: #007bff;
--n-color-primary-hover: #0056b3;
}
/* ❌ 避免:重写大量组件样式 */
```
## 🔧 故障排除
### 常见问题
#### 1. 组件未正确加载
**症状**: 页面显示组件未定义错误
**解决方案**:
```typescript
// 确保在 rsbuild.config.ts 中正确配置了 NaiveUiResolver
Components({
resolvers: [NaiveUiResolver()],
dts: "./src/components.d.ts",
})
```
#### 2. 样式丢失
**症状**: 组件显示但样式不正确
**解决方案**:
```vue
<!-- 确保在 App.vue 中正确配置了 ConfigProvider -->
<template>
<n-config-provider :theme="isDark ? darkTheme : null">
<router-view />
</n-config-provider>
</template>
```
#### 3. 打包体积过大
**症状**: 构建后的文件仍然很大
**解决方案**:
1. 运行 `npm run analyze` 查看详细分析
2. 检查是否有重复导入的组件
3. 确认 tree-shaking 正常工作
#### 4. 懒加载组件闪烁
**症状**: 组件加载时出现明显的闪烁
**解决方案**:
```typescript
// 调整懒加载配置,增加加载组件
const LazyComponent = createLazyComponent(
() => import('naive-ui').then(m => ({ default: m.NDataTable })),
{
loadingComponent: LoadingSpinner, // 添加加载组件
delay: 200 // 调整延迟时间
}
)
```
### 性能调试
#### 1. 使用 Chrome DevTools
```javascript
// 在控制台中运行,查看组件加载情况
performance.mark('component-load-start')
// ... 组件加载代码 ...
performance.mark('component-load-end')
performance.measure('component-load', 'component-load-start', 'component-load-end')
```
#### 2. 使用 Vue DevTools
安装 Vue DevTools 扩展,查看组件树和性能信息。
#### 3. 网络分析
```bash
# 使用 webpack-bundle-analyzer 分析包结构
npm install --save-dev webpack-bundle-analyzer
```
## 📈 预期效果
实施这些优化后,你应该能看到:
- **首次加载时间减少 30-50%**
- **Naive UI 相关代码分割为 6-8 个独立的 chunk**
- **按需加载减少初始包体积 20-40%**
- **页面切换时的组件加载更加平滑**
- **更好的缓存命中率和更新策略**
## 🔄 持续优化
### 定期检查
1. **每周运行一次包分析**: `npm run build:analyze`
2. **监控核心 Web 指标**: 使用 Google PageSpeed Insights
3. **用户体验测试**: 在不同网络条件下测试加载性能
4. **依赖更新**: 定期更新 Naive UI 和相关依赖
### 优化迭代
1. 根据实际使用情况调整预加载策略
2. 监控新增组件的使用频率,调整分组策略
3. 根据用户反馈优化加载体验
4. 持续关注 Naive UI 的新特性和优化建议
---
## 📞 支持
如果在实施过程中遇到问题,可以:
1. 查看 [Naive UI 官方文档](https://www.naiveui.com/)
2. 检查 `bundle-analysis.json` 分析报告
3. 使用浏览器开发者工具进行调试
4. 参考本项目的具体实现代码
记住,性能优化是一个持续的过程,需要根据实际使用情况不断调整和改进。

View File

@@ -7,6 +7,8 @@
"build": "rsbuild build",
"build:staging": "rsbuild build --env-mode=staging",
"build:test": "rsbuild build --env-mode=test",
"analyze": "node scripts/analyze-bundle.js",
"build:analyze": "npm run build && npm run analyze",
"fmt": "prettier --write src *.ts"
},
"dependencies": {

View File

@@ -71,7 +71,63 @@ export default defineConfig(({ envMode }) => {
},
performance: {
chunkSplit: {
strategy: "split-by-module",
strategy: "split-by-experience",
forceSplitting: {
"naive-ui": /node_modules[\\/]naive-ui/,
"naive-ui-components": /node_modules[\\/]naive-ui[\\/]es[\\/](button|input|form|table|data-table|select|date-picker|time-picker|upload|modal|drawer|popover|tooltip|notification|message|loading-bar|spin|skeleton|pagination|menu|dropdown|tabs|steps|breadcrumb|anchor|affix|back-top|divider|space|grid|layout|card|collapse|descriptions|empty|list|statistic|timeline|tree|transfer|cascader|auto-complete|mention|rate|slider|switch|progress|tag|badge|avatar|image|carousel|calendar|color-picker|dynamic-input|dynamic-tags|gradient-text|number-animation|qr-code|result|split|thing|typography|watermark)/,
"chart-libs": /node_modules[\\/](chart\.js|vue-chartjs)/,
"editor-libs": /node_modules[\\/](@wangeditor-next|md-editor-v3|codemirror|@codemirror)/,
"utils": /node_modules[\\/](date-fns|highlight\.js|copy-text-to-clipboard|canvas-confetti|fflate|query-string)/,
},
override: {
chunks: "all",
minSize: 20000,
maxSize: 244000,
cacheGroups: {
"naive-ui-core": {
test: /node_modules[\\/]naive-ui[\\/]es[\\/](config-provider|theme|locale|loading-bar|message|notification|dialog)/,
name: "naive-ui-core",
priority: 30,
chunks: "all",
},
"naive-ui-form": {
test: /node_modules[\\/]naive-ui[\\/]es[\\/](form|input|select|date-picker|time-picker|upload|auto-complete|cascader|mention|rate|slider|switch|dynamic-input|dynamic-tags)/,
name: "naive-ui-form",
priority: 25,
chunks: "all",
},
"naive-ui-data": {
test: /node_modules[\\/]naive-ui[\\/]es[\\/](table|data-table|list|tree|transfer|pagination)/,
name: "naive-ui-data",
priority: 25,
chunks: "all",
},
"naive-ui-layout": {
test: /node_modules[\\/]naive-ui[\\/]es[\\/](layout|grid|space|divider|card|collapse|descriptions|split)/,
name: "naive-ui-layout",
priority: 25,
chunks: "all",
},
"naive-ui-feedback": {
test: /node_modules[\\/]naive-ui[\\/]es[\\/](modal|drawer|popover|tooltip|spin|skeleton|empty|result)/,
name: "naive-ui-feedback",
priority: 25,
chunks: "all",
},
"naive-ui-navigation": {
test: /node_modules[\\/]naive-ui[\\/]es[\\/](menu|dropdown|tabs|steps|breadcrumb|anchor|affix|back-top)/,
name: "naive-ui-navigation",
priority: 25,
chunks: "all",
},
"naive-ui-display": {
test: /node_modules[\\/]naive-ui[\\/]es[\\/](tag|badge|avatar|image|carousel|calendar|statistic|timeline|progress|typography|watermark|gradient-text|number-animation|qr-code|thing)/,
name: "naive-ui-display",
priority: 25,
chunks: "all",
},
},
},
},
},
resolve: {

176
scripts/analyze-bundle.js Normal file
View File

@@ -0,0 +1,176 @@
#!/usr/bin/env node
/**
* Bundle 分析脚本
* 用于分析 Naive UI 组件的打包情况和优化建议
*/
import { execSync } from 'child_process'
import { readFileSync, writeFileSync } from 'fs'
import { resolve } from 'path'
const DIST_PATH = resolve(process.cwd(), 'dist')
const ANALYSIS_OUTPUT = resolve(process.cwd(), 'bundle-analysis.json')
/**
* 分析打包结果
*/
function analyzeBundleSize() {
console.log('🔍 开始分析打包结果...')
try {
// 构建生产版本
console.log('📦 构建生产版本...')
execSync('npm run build', { stdio: 'inherit' })
// 获取文件大小信息
const result = execSync(`find ${DIST_PATH} -name "*.js" -o -name "*.css" | xargs ls -la`,
{ encoding: 'utf-8' })
const files = result.split('\n')
.filter(line => line.trim())
.map(line => {
const parts = line.split(/\s+/)
const size = parseInt(parts[4])
const name = parts[parts.length - 1].replace(DIST_PATH + '/', '')
return { name, size, sizeKB: Math.round(size / 1024 * 100) / 100 }
})
.filter(file => file.size > 0)
.sort((a, b) => b.size - a.size)
// 分析 Naive UI 相关文件
const naiveUIFiles = files.filter(file =>
file.name.includes('naive-ui') ||
file.name.includes('naive')
)
const totalSize = files.reduce((sum, file) => sum + file.size, 0)
const naiveUISize = naiveUIFiles.reduce((sum, file) => sum + file.size, 0)
const analysis = {
timestamp: new Date().toISOString(),
totalFiles: files.length,
totalSizeKB: Math.round(totalSize / 1024 * 100) / 100,
naiveUIFiles: naiveUIFiles.length,
naiveUISizeKB: Math.round(naiveUISize / 1024 * 100) / 100,
naiveUIPercentage: Math.round(naiveUISize / totalSize * 100 * 100) / 100,
files: files.slice(0, 20), // 只保留前20个最大的文件
naiveUIFiles: naiveUIFiles,
recommendations: generateRecommendations(files, naiveUIFiles, totalSize)
}
// 保存分析结果
writeFileSync(ANALYSIS_OUTPUT, JSON.stringify(analysis, null, 2))
// 输出分析报告
printAnalysisReport(analysis)
} catch (error) {
console.error('❌ 分析失败:', error.message)
process.exit(1)
}
}
/**
* 生成优化建议
*/
function generateRecommendations(allFiles, naiveUIFiles, totalSize) {
const recommendations = []
// 检查是否有过大的单个文件
const largeFiles = allFiles.filter(file => file.size > 500 * 1024) // 大于500KB
if (largeFiles.length > 0) {
recommendations.push({
type: 'warning',
title: '发现大文件',
description: `${largeFiles.length} 个文件超过 500KB建议进一步拆分`,
files: largeFiles.map(f => f.name)
})
}
// 检查 Naive UI 占比
const naiveUIPercentage = naiveUIFiles.reduce((sum, file) => sum + file.size, 0) / totalSize * 100
if (naiveUIPercentage > 30) {
recommendations.push({
type: 'info',
title: 'Naive UI 占比较高',
description: `Naive UI 组件占总包体积的 ${Math.round(naiveUIPercentage)}%,可以考虑更精细的按需引入`,
suggestion: '使用 tree-shaking 或更细粒度的组件拆分'
})
}
// 检查是否有重复的组件
const duplicatePattern = /naive-ui.*\.(js|css)$/
const duplicates = allFiles.filter(file => duplicatePattern.test(file.name))
if (duplicates.length > 5) {
recommendations.push({
type: 'warning',
title: '可能存在组件重复',
description: '发现多个 Naive UI 相关文件,可能存在重复打包',
suggestion: '检查 webpack 配置中的 splitChunks 设置'
})
}
// 检查 CSS 文件大小
const cssFiles = allFiles.filter(file => file.name.endsWith('.css'))
const largeCSSFiles = cssFiles.filter(file => file.size > 100 * 1024) // 大于100KB
if (largeCSSFiles.length > 0) {
recommendations.push({
type: 'info',
title: 'CSS 文件较大',
description: '发现较大的 CSS 文件,可以考虑按需加载样式',
files: largeCSSFiles.map(f => f.name)
})
}
return recommendations
}
/**
* 打印分析报告
*/
function printAnalysisReport(analysis) {
console.log('\n📊 Bundle 分析报告')
console.log('='.repeat(50))
console.log(`📁 总文件数: ${analysis.totalFiles}`)
console.log(`📦 总大小: ${analysis.totalSizeKB} KB`)
console.log(`🎨 Naive UI 文件数: ${analysis.naiveUIFiles.length}`)
console.log(`🎨 Naive UI 大小: ${analysis.naiveUISizeKB} KB (${analysis.naiveUIPercentage}%)`)
console.log('\n📋 最大的文件:')
analysis.files.slice(0, 10).forEach((file, index) => {
console.log(`${index + 1}. ${file.name}: ${file.sizeKB} KB`)
})
if (analysis.naiveUIFiles.length > 0) {
console.log('\n🎨 Naive UI 相关文件:')
analysis.naiveUIFiles.forEach((file, index) => {
console.log(`${index + 1}. ${file.name}: ${file.sizeKB} KB`)
})
}
if (analysis.recommendations.length > 0) {
console.log('\n💡 优化建议:')
analysis.recommendations.forEach((rec, index) => {
const icon = rec.type === 'warning' ? '⚠️' : ''
console.log(`${icon} ${index + 1}. ${rec.title}`)
console.log(` ${rec.description}`)
if (rec.suggestion) {
console.log(` 建议: ${rec.suggestion}`)
}
if (rec.files) {
console.log(` 文件: ${rec.files.join(', ')}`)
}
console.log('')
})
}
console.log(`\n💾 详细分析结果已保存到: ${ANALYSIS_OUTPUT}`)
}
// 运行分析
if (import.meta.url === `file://${process.argv[1]}`) {
analyzeBundleSize()
}
export { analyzeBundleSize }

View File

@@ -23,6 +23,7 @@ import storage from "utils/storage"
import App from "./App.vue"
import { admins, ojs } from "./routes"
import { setupComponentPreloading, PreloadStrategies } from "./utils/lazy-components"
import { toggleLogin } from "./shared/composables/modal"
import { useUserStore } from "./shared/store/user"
@@ -82,6 +83,9 @@ router.beforeEach(async (to, from, next) => {
next()
})
// 设置组件预加载
setupComponentPreloading(router)
ChartJS.register(
CategoryScale,
LinearScale,
@@ -101,6 +105,10 @@ const pinia = createPinia()
const app = createApp(App)
app.use(router)
app.use(pinia)
// 预加载核心组件
PreloadStrategies.preloadCore().catch(console.warn)
app.mount("#app")
if (!!import.meta.env.PUBLIC_ICONIFY_URL) {

View File

@@ -0,0 +1,142 @@
/**
* 组件懒加载工具函数
* 用于实现 Naive UI 组件的按需加载和优化
*/
import { AsyncComponentLoader, defineAsyncComponent } from 'vue'
/**
* 创建懒加载组件的工厂函数
* @param loader 组件加载器函数
* @param loadingComponent 加载中显示的组件
* @param errorComponent 错误时显示的组件
* @param delay 延迟显示加载组件的时间(毫秒)
* @param timeout 超时时间(毫秒)
*/
export function createLazyComponent(
loader: AsyncComponentLoader,
options?: {
loadingComponent?: any
errorComponent?: any
delay?: number
timeout?: number
}
) {
return defineAsyncComponent({
loader,
loadingComponent: options?.loadingComponent,
errorComponent: options?.errorComponent,
delay: options?.delay ?? 200,
timeout: options?.timeout ?? 3000,
})
}
/**
* 预定义的懒加载组件工厂
*/
export const LazyComponents = {
// 数据展示组件 - 通常在列表页面使用
DataTable: () => createLazyComponent(() => import('naive-ui').then(m => ({ default: m.NDataTable }))),
Table: () => createLazyComponent(() => import('naive-ui').then(m => ({ default: m.NTable }))),
List: () => createLazyComponent(() => import('naive-ui').then(m => ({ default: m.NList }))),
// 表单组件 - 通常在编辑页面使用
Form: () => createLazyComponent(() => import('naive-ui').then(m => ({ default: m.NForm }))),
FormItem: () => createLazyComponent(() => import('naive-ui').then(m => ({ default: m.NFormItem }))),
Input: () => createLazyComponent(() => import('naive-ui').then(m => ({ default: m.NInput }))),
Select: () => createLazyComponent(() => import('naive-ui').then(m => ({ default: m.NSelect }))),
DatePicker: () => createLazyComponent(() => import('naive-ui').then(m => ({ default: m.NDatePicker }))),
TimePicker: () => createLazyComponent(() => import('naive-ui').then(m => ({ default: m.NTimePicker }))),
Upload: () => createLazyComponent(() => import('naive-ui').then(m => ({ default: m.NUpload }))),
// 反馈组件 - 按需加载
Modal: () => createLazyComponent(() => import('naive-ui').then(m => ({ default: m.NModal }))),
Drawer: () => createLazyComponent(() => import('naive-ui').then(m => ({ default: m.NDrawer }))),
Popover: () => createLazyComponent(() => import('naive-ui').then(m => ({ default: m.NPopover }))),
// 图表组件 - 通常在统计页面使用
Chart: () => createLazyComponent(() => import('vue-chartjs').then(m => ({ default: m.Line }))),
// 编辑器组件 - 通常在内容编辑页面使用
Editor: () => createLazyComponent(() => import('@wangeditor-next/editor-for-vue').then(m => ({ default: m.Editor }))),
CodeMirror: () => createLazyComponent(() => import('vue-codemirror').then(m => ({ default: m.Codemirror }))),
}
/**
* 基于路由的组件预加载策略
*/
export const PreloadStrategies = {
// 预加载核心组件(首页需要的)
preloadCore: async () => {
const coreComponents = [
() => import('naive-ui').then(m => m.NButton),
() => import('naive-ui').then(m => m.NInput),
() => import('naive-ui').then(m => m.NCard),
() => import('naive-ui').then(m => m.NSpace),
() => import('naive-ui').then(m => m.NFlex),
]
return Promise.all(coreComponents.map(loader => loader()))
},
// 预加载表单组件(编辑页面需要的)
preloadForm: async () => {
const formComponents = [
() => import('naive-ui').then(m => m.NForm),
() => import('naive-ui').then(m => m.NFormItem),
() => import('naive-ui').then(m => m.NSelect),
() => import('naive-ui').then(m => m.NDatePicker),
() => import('naive-ui').then(m => m.NUpload),
]
return Promise.all(formComponents.map(loader => loader()))
},
// 预加载数据组件(列表页面需要的)
preloadData: async () => {
const dataComponents = [
() => import('naive-ui').then(m => m.NDataTable),
() => import('naive-ui').then(m => m.NPagination),
() => import('naive-ui').then(m => m.NTag),
() => import('naive-ui').then(m => m.NBadge),
]
return Promise.all(dataComponents.map(loader => loader()))
},
// 预加载管理员组件
preloadAdmin: async () => {
const adminComponents = [
() => import('naive-ui').then(m => m.NModal),
() => import('naive-ui').then(m => m.NDrawer),
() => import('naive-ui').then(m => m.NPopconfirm),
() => import('@wangeditor-next/editor-for-vue').then(m => m.Editor),
]
return Promise.all(adminComponents.map(loader => loader()))
},
}
/**
* 路由守卫中使用的预加载逻辑
*/
export function setupComponentPreloading(router: any) {
router.beforeEach(async (to: any, from: any, next: any) => {
// 根据路由路径预加载相应组件
if (to.path.startsWith('/admin')) {
// 管理员页面预加载
PreloadStrategies.preloadAdmin().catch(console.warn)
PreloadStrategies.preloadForm().catch(console.warn)
} else if (to.path.includes('/problem/') && !from.path.includes('/problem/')) {
// 题目详情页预加载编辑器
import('vue-codemirror').catch(console.warn)
import('@codemirror/lang-cpp').catch(console.warn)
import('@codemirror/lang-python').catch(console.warn)
} else if (to.path === '/' || to.path.includes('/list')) {
// 列表页预加载数据组件
PreloadStrategies.preloadData().catch(console.warn)
}
next()
})
}