# testProjectConfiguration 页面性能分析报告 **项目**:广西北港新材料检化验系统(beiHai-ui / beiHai-lims) **页面**:`src/views/standardManagement/components/testProjectConfiguration/testProjectConfiguration.vue` **现象**:页面从打开到渲染完毕约需 **20 秒** **分析时间**:2026-08-28 --- ## 一、结论速览 该页面 20 秒耗时**不是数据库单条 SQL 慢**(已实测全部核心 SQL 均在 250ms 以内),而是**请求数量爆炸 + 前端渲染过重 + 无索引全表扫描**三者叠加: 1. **页面一打开并发发出约 19 个 HTTP 请求**(父组件 5 个 + 3 个弹窗子组件 14 个),其中 5+ 个是 `pageSize=9999 / 99999` 的全量查询 —— 这是最大的元凶 2. **3 个弹窗子组件在页面挂载时即被实例化并执行 mounted 请求**,弹窗根本没打开数据就全拉回来了 3. **页面同时渲染 3 个巨型表格**(25 / 32 / 12 列),全部列 `sortable + show-overflow-tooltip + show-summary 合计 + fixed 左列`,`el-tabs` 非激活页也渲染,DOM 与 tooltip 实例开销巨大 4. 后端核心表**除主键外几乎无索引**,`ORDER BY CREATE_TIME DESC` 每次全表排序;数据量增长后(生产库)单条 SQL 会指数级变慢 5. 存在**纯冗余请求**:`gettestItemNameTypeList()` 请求的 `testItemNameType` 在模板中根本未使用 --- ## 二、实测证据(数据库直连验证,只读) 连接:`jdbc:oracle:thin:@172.16.4.164:1521/lims` ### 2.1 数据量(开发库) | 表 | 行数 | |---|---| | LIMS_STD_TEST_ITEM(测试项目) | 1050 | | LIMS_STD_ANLY_ITEM(分析项目) | 741 | | LIMS_STD_TEST_ITEM_D(子表) | 8309 | | LIMS_STD_TEST_MATCH(方法标准) | 318 | | LIMS_BASE_INFO | 542 | ### 2.2 索引情况 | 表 | 索引 | |---|---| | LIMS_STD_TEST_ITEM | **仅主键 TEST_ITEM_NO** | | LIMS_STD_TEST_ITEM_D | **仅主键 TEST_ITEM_NO_D**(TEST_ITEM_NO 无索引!) | | LIMS_STD_TEST_MATCH | **仅主键 TEST_STD_NO**(TEST_ITEM_NO 无索引!) | | LIMS_STD_ANLY_ITEM | **无任何索引** | 执行计划确认:子表按 TEST_ITEM_NO 查询走 **TABLE ACCESS FULL(全表扫描)**。 ### 2.3 核心 SQL 实测耗时(开发库,重复 3 次取最优) | SQL | 耗时 | 行数 | |---|---|---| | 测试项目分页 50 条 | 52 ms | 50 | | 测试项目全量 validFlag=1 | 127 ms | 329 | | 分析项目全量 ORDER BY | 235 ms | 741 | | 子表 D 按 TEST_ITEM_NO 查 | 24~49 ms | 5 | | 方法标准按 TEST_ITEM_NO 查 | 25 ms | 0 | | 各字典全量(规则/岗位/单位/修约/公式) | 48~124 ms | 12~383 | | COUNT(*) | 25 ms | 1 | > ⚠️ 单条 SQL 都不慢,但 **19 个请求 ×(count 查询 + 数据查询 + JSON 序列化 + 网络传输)** 叠加,且生产库数据量可能是开发库的 10~100 倍,无索引的全表扫描会显著放大耗时。 --- ## 三、问题清单(按严重程度排序) ### 🔴 P0-1 弹窗子组件挂载即发请求 —— 页面一打开并发 19 个请求 页面底部 3 个弹窗组件**没有任何懒加载控制**,组件在父页面 `mounted` 时就被实例化并执行各自的 `mounted`: ```html
``` 各子组件 `mounted` 发出的请求: **alertComponents.vue(新增/修改测试项目弹窗)**: - `queryBaseInfoByBaseCode(4840)` - `queryLimsBaseRuleInfoPage` **pageSize=9999** ← 全量 - `queryLimsBasePostPage` **pageSize=99999** ← 全量 - `queryBaseInfoByBaseCode(4808)` - `queryBaseInfoByBaseCode(4804)` **alertComponents3.vue(分析项目弹窗)**: - `queryLimsStdAnlyItemPage` **pageSize=9999** ← 全量 - `queryLimsBaseUnitPage` **pageSize=9999** ← 全量 - `queryBaseInfoByBaseCode(4825)` - `queryBaseInfoByBaseCode(4810)` - `queryLimsBaseReviseRulePage` **pageSize=9999** ← 全量 - `queryBaseInfoByBaseCode(4804)` - `queryLimsBaseFormulaPage` **pageSize=9999** ← 全量 **alertComponents4.vue(方法标准弹窗)**: - `queryBaseInfoByBaseCode(4805)` **父组件 mounted(L414-426)**: - `queryBaseInfoByBaseCode(4804)` - `getList()` → 分析项目分页 **pageSize=500**(仅弹窗使用,页面打开就拉) - `getDataList()` → 主表分页 50 - `gettestItemNameTypeList()` → GET 全量(见 P0-2) - `addScreen / addScreen2 / addScreen3` → 操作 DOM > **合计约 19 个请求并发发出**,浏览器同一域名并发上限通常 6 个,大量请求排队 + 后端逐个处理 + 响应体全量传输,是 20 秒的主要构成。 ### 🔴 P0-2 冗余请求:gettestItemNameTypeList 结果从未使用 ```js // L430-435 gettestItemNameTypeList () { this.axios.get('pass/baseManagement/v1/limsstdtestitems/'+ '?validFlag=1') .then(res => { this.testItemNameType = res.data.list; }) }, ``` - 该请求拉取**全部测试项目**(GET 无分页参数),结果存入 `testItemNameType` - 但模板中搜索 `testItemNameType` —— **从未被引用**!下拉框用的是 `itemTypeNameType`(来自 `queryBaseInfoByBaseCode`) - 属于死代码请求,白拉全量数据,应删除 ### 🟠 P1-3 表格渲染过重(3 个表格 × 全列 sortable/tooltip/fixed/合计) - 主表 25 列、分析项目表 32 列、方法标准表 12 列,**所有列**都加了 `sortable` + `:show-overflow-tooltip="true"` - 每个表都开启 `show-summary` 合计行 + `:icore-filter-flag`(自定义筛选) - `el-tabs` 的两个 tab **非激活也渲染 DOM**(未配置 lazy) - `el-table` 的 `show-overflow-tooltip` 会给每个单元格注册 tooltip 实例,50 行 × 25 列 = 1250 个单元格 + tooltip 初始化,加上 fixed 列双份渲染,DOM 量巨大 - `tableRowClassName` 等行回调每行每列执行,渲染阶段开销翻倍 ### 🟠 P1-4 后端索引缺失,生产库风险高 - 4 张核心表**除主键外无业务索引** - `TEST_ITEM_NO` 作为高频查询条件(子表 D、MATCH 表)**无索引 → 全表扫描**(已用执行计划证实) - `ORDER BY CREATE_TIME DESC` 无索引,每次全表排序;`LIKE '%${x}%'` 无法走索引 - 开发库 1 千~8 千行还撑得住;**生产库一旦上万行,单条 SQL 就会从 50ms 恶化到数秒**,19 个请求全部变慢 → 20 秒甚至更久 ### 🟡 P2-5 交互触发额外请求风暴 ```js // L799-807 每次勾选变化就发 2 个请求 handleSelectionChange (val) { this.multipleSelection = val; if (val.length > 0) { this.testItemNo = val[val.length-1].testItemNo this.getDataList2(this.testItemNo); // pageSize=990 this.getDataList3(this.testItemNo); // pageSize=990 } }, // L846-858 每次点击行也发 2 个请求 handleCurrentChange (val) { ... this.getDataList2(...); this.getDataList3(...); } ``` - 点击/勾选任意一行,立即触发 **2 个 pageSize=990 的子表查询** - 勾选多行时会连续触发多次,形成请求风暴 - 且 `getDataList2/getDataList3` 的 pageSize 高达 **990**,每次几乎全量拉取子表 ### 🟡 P2-6 后端日志级别 DEBUG + SQL 全量输出 ```properties logging.level.com.steerinfo=DEBUG mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl ``` 每执行一条 SQL 都打印到控制台;19 个请求 × 多条 SQL = 几十行日志输出,生产环境日志量大时也会拖慢接口响应。 --- ## 四、请求链路全景图 ``` 页面打开 (mounted) ├─ 父组件 testProjectConfiguration.vue │ ├─ queryBaseInfoByBaseCode(4804) 字典 │ ├─ getList() → 分析项目 pageSize=500 ← 仅弹窗用,提前拉 │ ├─ getDataList() → 测试项目 pageSize=50 主表 │ ├─ gettestItemNameTypeList() → GET 全量 ← 死代码,结果未使用 │ └─ addScreen ×3 DOM 操作 │ ├─ alertComponents (弹窗1,挂载即请求) │ ├─ queryBaseInfoByBaseCode(4840) │ ├─ queryLimsBaseRuleInfoPage pageSize=9999 │ ├─ queryLimsBasePostPage pageSize=99999 │ ├─ queryBaseInfoByBaseCode(4808) │ └─ queryBaseInfoByBaseCode(4804) │ ├─ alertComponents3 (弹窗2,挂载即请求) │ ├─ queryLimsStdAnlyItemPage pageSize=9999 │ ├─ queryLimsBaseUnitPage pageSize=9999 │ ├─ queryBaseInfoByBaseCode(4825) │ ├─ queryBaseInfoByBaseCode(4810) │ ├─ queryLimsBaseReviseRulePage pageSize=9999 │ ├─ queryBaseInfoByBaseCode(4804) │ └─ queryLimsBaseFormulaPage pageSize=9999 │ └─ alertComponents4 (弹窗3,挂载即请求) └─ queryBaseInfoByBaseCode(4805) ≈ 19 个并发请求(其中 6 个为全量查询) ``` --- ## 五、优化建议(按优先级) ### 立即执行(改动小、收益大) 1. **弹窗组件懒加载**:将 `