# 借用列表加载速度优化方案 ## 一、问题分析 列表加载慢可能来自以下方面: | 来源 | 可能原因 | |------|----------| | **后端 API** | 数据库查询慢、N+1 查询、缺少索引、返回字段过多 | | **前端请求** | 首次加载时并发请求多、请求参数冗余 | | **前端渲染** | 列过多、复杂 formatter/cellRender、compute 计算量大 | --- ## 二、后端优化(需后端配合) ### 2.1 数据库层面 - **索引**:为常用筛选字段建索引(如 `status`、`create_datetime`、`borrow_type`) - **分页**:确保使用 `limit/offset` 或游标分页,避免全表扫描 ### 2.2 接口层面(Django 示例) ```python # 使用 select_related / prefetch_related 减少 N+1 查询 queryset = BorrowApplication.objects.select_related( 'borrower_info', 'creator' ).prefetch_related( 'items' ).only('id', 'application_no', 'status', ...) # 只返回列表需要的字段 ``` ### 2.3 返回字段精简 - 列表接口只返回表格展示所需字段 - 详情接口再返回完整数据 --- ## 三、前端优化(可立即实施) ### 3.1 移除调试日志 `crud.tsx` 中 `pageRequest` 的 `console.log(query)` 会在每次请求时执行,生产环境应移除。 ### 3.2 减少首屏列数 - 将非核心列默认 `show: false`,通过列设置由用户按需显示 - 当前已有部分列隐藏,可继续评估是否可再精简 ### 3.3 优化 formatter - 将 `dayjs` 等格式化逻辑尽量复用,避免每行重复创建 - 简单格式化可考虑后端直接返回格式化字符串 ### 3.4 延迟加载弹窗组件 - 使用 `defineAsyncComponent` 或路由懒加载,减少首屏 bundle - 弹窗在首次打开时再加载对应组件 ### 3.5 请求参数优化 - 确保 `transformQuery` 只传递有值的搜索条件,减少无效参数 ### 3.6 避免重复刷新 - `handleBorrowSubmit` 中已去除重复的 `doRefresh` 调用,仅保留 `crudExpose.doRefresh()` ### 3.7 首屏加载与 doSearch - 若通过 URL 的 `status` 做初始筛选,`onMounted` 中会调用 `doSearch`。若 fast-crud 默认也会做一次初始请求,可能造成首屏请求两次,可考虑在 crud 配置中关闭默认首次加载,仅依赖 `doSearch` 触发 --- ## 四、排查步骤 1. **定位瓶颈**:用浏览器 DevTools → Network 查看 `/api/system/borrow/application/` 请求耗时 - 若接口耗时 > 1s,重点优化后端 - 若接口很快但页面渲染慢,重点优化前端 2. **后端慢**:检查数据库慢查询、接口是否有 N+1、是否缺少索引 3. **前端慢**:检查列数量、formatter 复杂度、是否有大量 compute --- ## 五、列表缓存(已实现) ### 5.1 实现方式 - **工具**:`src/utils/requestCache.ts` 提供通用 `createRequestCache` - **接入**:`crud.tsx` 中 `pageRequest` 使用缓存,相同查询参数在 TTL 内直接返回缓存 - **失效**:增删改、领取、归还等操作后调用 `invalidateBorrowListCache()` 再 `doRefresh()`,确保展示最新数据 ### 5.2 配置 - **TTL**:默认 30 秒,可在 `crud.tsx` 中调整 `createRequestCache({ ttl: 30 * 1000 })` - **keyPrefix**:`borrow:list:`,用于按业务隔离 ### 5.3 适用场景 - 分页来回切换(如 1→2→1)时,相同页可立即展示 - 短时间内重复搜索相同条件时,减少后端请求 ### 5.4 其他列表复用 其他 crud 列表如需缓存,可参考借用列表: ```ts import { createRequestCache } from '/@/utils/requestCache'; const cache = createRequestCache({ ttl: 30 * 1000, keyPrefix: 'xxx:list:' }); // pageRequest 中:先 cache.get(query),命中则返回;否则请求后 cache.set(query, res) // 变更后:export invalidateXxxListCache,在刷新前调用 ``` --- ## 六、快速见效项(优先级) 1. 移除 `console.log`(立即) 2. 后端:为 `status`、`create_datetime` 等建索引 3. 后端:列表接口使用 `select_related`/`prefetch_related` 并精简返回字段 4. 前端:确认 `pageSize` 合理(当前 10 条/页已较小)