u
This commit is contained in:
1 parent
e7c84a82a2
commit
025d3232c4
65 files changed
+9838
-9111
No files matched your search
@@ -0,0 +1,338 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>/table-test 表格性能排查报告</title>
|
||||
<style>
|
||||
:root {
|
||||
--text: #1f2329;
|
||||
--text-2: #4e5969;
|
||||
--text-3: #86909c;
|
||||
--border: #e5e6eb;
|
||||
--bg: #f7f8fa;
|
||||
--card: #ffffff;
|
||||
--primary: #165dff;
|
||||
--danger: #ec4246;
|
||||
--warning: #e79a0d;
|
||||
--ok: #00b42a;
|
||||
--code-bg: #f2f3f5;
|
||||
}
|
||||
* { box-sizing: border-box; }
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 40px 24px 80px;
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
|
||||
font-size: 14px;
|
||||
line-height: 1.75;
|
||||
}
|
||||
.page { max-width: 920px; margin: 0 auto; }
|
||||
header.doc {
|
||||
background: var(--card);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 10px;
|
||||
padding: 28px 32px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
header.doc h1 { margin: 0 0 8px; font-size: 22px; }
|
||||
header.doc .meta { color: var(--text-3); font-size: 13px; }
|
||||
header.doc .meta span { margin-right: 18px; }
|
||||
section {
|
||||
background: var(--card);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 10px;
|
||||
padding: 24px 32px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
h2 {
|
||||
font-size: 17px;
|
||||
margin: 0 0 14px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
h3 { font-size: 15px; margin: 22px 0 8px; }
|
||||
p { margin: 8px 0; }
|
||||
ul, ol { margin: 8px 0; padding-left: 24px; }
|
||||
li { margin: 4px 0; }
|
||||
code {
|
||||
background: var(--code-bg);
|
||||
border-radius: 4px;
|
||||
padding: 1px 6px;
|
||||
font-family: Consolas, Monaco, "Courier New", monospace;
|
||||
font-size: 12.5px;
|
||||
color: #c0341d;
|
||||
word-break: break-all;
|
||||
}
|
||||
.file { color: var(--primary); }
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin: 12px 0;
|
||||
font-size: 13px;
|
||||
}
|
||||
th, td {
|
||||
border: 1px solid var(--border);
|
||||
padding: 7px 12px;
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
}
|
||||
th { background: #f2f3f5; font-weight: 600; white-space: nowrap; }
|
||||
td.num { font-variant-numeric: tabular-nums; white-space: nowrap; }
|
||||
.bad { color: var(--danger); font-weight: 600; }
|
||||
.good { color: var(--ok); font-weight: 600; }
|
||||
.warn { color: var(--warning); font-weight: 600; }
|
||||
.tldr {
|
||||
background: #f0f5ff;
|
||||
border: 1px solid #bedaff;
|
||||
border-radius: 8px;
|
||||
padding: 14px 18px;
|
||||
margin: 12px 0;
|
||||
}
|
||||
.tldr ol { margin: 6px 0 0; }
|
||||
.note {
|
||||
background: #fff7e8;
|
||||
border: 1px solid #f5d8a8;
|
||||
border-radius: 8px;
|
||||
padding: 12px 16px;
|
||||
margin: 12px 0;
|
||||
font-size: 13px;
|
||||
color: #6b5300;
|
||||
}
|
||||
.chain {
|
||||
counter-reset: step;
|
||||
list-style: none;
|
||||
padding-left: 0;
|
||||
margin: 12px 0;
|
||||
}
|
||||
.chain > li {
|
||||
counter-increment: step;
|
||||
position: relative;
|
||||
padding-left: 34px;
|
||||
margin: 10px 0;
|
||||
}
|
||||
.chain > li::before {
|
||||
content: counter(step);
|
||||
position: absolute;
|
||||
left: 0; top: 2px;
|
||||
width: 22px; height: 22px;
|
||||
border-radius: 50%;
|
||||
background: var(--primary);
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
line-height: 22px;
|
||||
text-align: center;
|
||||
font-weight: 600;
|
||||
}
|
||||
.fix {
|
||||
border: 1px solid var(--border);
|
||||
border-left: 4px solid var(--primary);
|
||||
border-radius: 8px;
|
||||
padding: 14px 18px;
|
||||
margin: 12px 0;
|
||||
}
|
||||
.fix.p1 { border-left-color: var(--danger); }
|
||||
.fix.p2 { border-left-color: var(--warning); }
|
||||
.fix h4 { margin: 0 0 6px; font-size: 14.5px; }
|
||||
.fix .tag {
|
||||
display: inline-block;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
border-radius: 4px;
|
||||
padding: 0 8px;
|
||||
margin-right: 8px;
|
||||
vertical-align: 1px;
|
||||
}
|
||||
.tag.high { background: #ffece8; color: var(--danger); }
|
||||
.tag.mid { background: #fff7e8; color: var(--warning); }
|
||||
.tag.low { background: #e8ffea; color: var(--ok); }
|
||||
.fix p { margin: 6px 0; color: var(--text-2); font-size: 13.5px; }
|
||||
.kv { color: var(--text-3); font-size: 12.5px; }
|
||||
footer { color: var(--text-3); font-size: 12.5px; text-align: center; margin-top: 8px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="page">
|
||||
|
||||
<header class="doc">
|
||||
<h1>/table-test 表格性能排查报告</h1>
|
||||
<div class="meta">
|
||||
<span>日期:2026-10-16</span>
|
||||
<span>对象:<code>web/src/views/table-test/index.vue</code> + <code>@g3soft/ui</code> G3Table(底座 stk-table-vue 1.2.7)</span>
|
||||
<span>状态:已定位根因,未改代码</span>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<section>
|
||||
<h2>摘要(TL;DR)</h2>
|
||||
<div class="tldr">
|
||||
<ol>
|
||||
<li><b>滚动卡顿的根因</b>:数据量 > 100 行时纵向虚拟滚动开启,每滚一步都触发<b>整个表格组件重渲染</b>(可见约 31 行 × 23 列 ≈ 713 个单元格重算 + 行窗口 DOM 进出),CPU profile 显示 67% 时间消耗在浏览器端样式重算/布局/绘制。同为 1000 行关掉虚拟滚动(全量渲染 23000 个 td)反而满帧 60fps —— 卡的是"每帧重渲染",不是表格大。</li>
|
||||
<li><b>点击卡顿的根因</b>:首次点击使 <code>editingActive</code> 翻真,触发<b>全量列重建</b>(23 个新列对象 → 列宽/样式缓存重建 → 全表重排重绘),实测主线程阻塞约 400~550ms;后续点击约 130~200ms。</li>
|
||||
<li><b>点击后滚动会更卡</b>(44ms → 75ms/帧):编辑列全部注入 customCell 每格跑校验;且底座区域选择的 watcher 依赖滚动窗口索引,每次滚动都全量遍历可见格更新选区 DOM。</li>
|
||||
<li><b>页面自身还有一处</b>:<code>errorCount</code> computed 每次提交都全量重跑「行数 × 22 列」校验,1000 行多付 ~60ms、5000 行多付 ~190ms,20000 行约 +0.8s/次。</li>
|
||||
<li>横向滚动基本正常(p50 16.7ms),问题集中在纵向滚动与点击编辑。</li>
|
||||
</ol>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>一、测试方法</h2>
|
||||
<ul>
|
||||
<li><code>demo_zhangwei</code> 没有 <code>/table-test</code> 菜单权限(访问会被守卫送到 <code>/403</code>),因此在 Vite dev server 下搭建了<b>复刻探针页</b>:同样的 22 列定义、<code>rowHeight=28</code>、1000 行同分布假数据、同样的 <code>fixed-col-shadow</code> 与校验规则,排除 ERP 外壳干扰。</li>
|
||||
<li>测量手段:Playwright 驱动真实鼠标/滚轮;页内 <code>requestAnimationFrame</code> 采样帧间隔;<code>PerformanceObserver</code> 抓 longtask;CDP <code>Profiler</code> 抓 CPU 热点(自底向上 self time 聚合)。</li>
|
||||
<li>对照组:裸 <code>div</code> 5000 行滚动(浏览器基线,满帧 60fps,验证测法可靠);纯展示列表格;完整编辑配置表格;virtual on/off 对比。</li>
|
||||
</ul>
|
||||
<div class="note">
|
||||
<b>数值口径说明</b>:headless Chromium 为软件渲染(无 GPU 合成),绝对帧时间比实机差;但相对倍数、瓶颈归属(浏览器端 vs JS)、longtask 分布在真实环境同样成立。文中所有结论均经「开/关某一因素」的对照实验确认,非单纯推断。
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>二、症状一:滚动 FPS 特别低</h2>
|
||||
|
||||
<h3>2.1 实测数据(1000 行,视口 1440×900,容器高约 866px)</h3>
|
||||
<table>
|
||||
<tr><th>场景</th><th>平均帧</th><th>p50</th><th>p95</th><th>卡顿帧(>32ms)</th></tr>
|
||||
<tr><td>纵向滚动,virtual <b>on</b>(页面默认 1000 行)</td><td class="num bad">34~75ms(13~29 FPS)</td><td class="num bad">33~100ms</td><td class="num bad">83~167ms</td><td class="num">30~41 / 约 60 帧</td></tr>
|
||||
<tr><td>纵向滚动,点击过单元格后(选区常驻)</td><td class="num bad">74.8ms(13 FPS)</td><td class="num bad">99.9ms</td><td class="num bad">166.7ms</td><td class="num">19 / 35</td></tr>
|
||||
<tr><td>横向滚动(fixed-col-shadow 开)</td><td class="num">19.9ms</td><td class="num good">16.7ms</td><td class="num">33.4ms</td><td class="num">22</td></tr>
|
||||
<tr><td>同样 1000 行,virtual <b>off</b>(全量 23000 td,滚动零重渲染)</td><td class="num">28ms</td><td class="num good">16.7ms</td><td class="num">116.6ms</td><td class="num">23</td></tr>
|
||||
<tr><td>100 行(门控自动关闭虚拟)</td><td class="num good">16.7ms(60 FPS)</td><td class="num good">16.7ms</td><td class="num good">16.7ms</td><td class="num good">0</td></tr>
|
||||
<tr><td>裸 div 5000 行(浏览器基线)</td><td class="num good">16.7ms(60 FPS)</td><td class="num good">16.7ms</td><td class="num good">16.8ms</td><td class="num good">0</td></tr>
|
||||
</table>
|
||||
|
||||
<h3>2.2 根因链</h3>
|
||||
<ol class="chain">
|
||||
<li>G3Table 的纵向虚拟门控为「行数 > 100 开启」(<span class="file">g3soft-libs/packages/ui/src/table/G3Table.vue:251</span>),页面默认 1000 行 → 开启。</li>
|
||||
<li>滚轮/滚动条每动一次 → <code>onTableScroll</code>(rAF 节流)→ <code>updateVirtualScrollY</code> 换窗 → <code>triggerRef(virtualScroll)</code> → <b>整个 StkTable 组件重渲染</b>:表头 23 个 th + 可见 31 行 × 23 列 ≈ <b>713 个单元格的 props 重算与 diff</b>,外加 2 行 DOM 进出。</li>
|
||||
<li>CPU profile(滚动 2.5s):<b>67% 时间在浏览器端 <code>(program)</code></b>(样式重算/布局/绘制),Vue JS 仅 ~7%,GC 2.9%,<code>setProperty</code>(Vue 写 style)4%。即贵在每帧 713 格的 style/layout/paint。</li>
|
||||
<li>浏览器端成本被以下 CSS 放大:
|
||||
<ul>
|
||||
<li><code>.g3-table__inner .stk-tbody-main { will-change: transform }</code>(<span class="file">G3Table.vue:1936</span>)——<b>无条件</b>给约 28000px 高的 tbody 挂合成层;库自己只在 transform 滚动模式(<code>.exp-scroll-y</code>)下才加(<span class="file">stk-table-vue/src/StkTable/style.less:392</span>),本页走原生滚动,这层纯属白付(实测去掉仅小幅改善,属次要因素)。</li>
|
||||
<li>4 个冻结列 × 31 行 ≈ 124 个 <code>position: sticky</code> 格(补丁让所有 fixed-cell 恒常 sticky,<span class="file">style/_overrides.scss:460</span>)。</li>
|
||||
<li>29 条 <code>td:has(...)</code> 规则(编辑器/校验/只读态反查)+ 库用背景渐变画格线。</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><b>判定性对照</b>:同样 1000 行显式 <code>virtual=false</code>(一次渲染 23000 个 td)→ 滚动 p50 回到 16.7ms 满帧。证明卡的是「虚拟滚动开启后的每帧重渲染」,而非 DOM 总量。</li>
|
||||
</ol>
|
||||
|
||||
<h3>2.3 为什么点击后滚动更卡(44ms → 75ms/帧)</h3>
|
||||
<ol class="chain">
|
||||
<li><code>editingActive</code> 翻真后,注入 customCell 的列从 10 个(select/checkbox/validate/precision 常驻)涨到 <b>17 个</b>(text/number/date 全部加入,<span class="file">utils/column.ts:121-134</span>),每格每帧走 <code>displayValue</code>:跑业务 <code>validate</code> + <code>checkPrecision</code> + 日期校验 + 数字格式化(<span class="file">utils/editable.ts:369-454</span>)。</li>
|
||||
<li>底座区域选择的 watcher(<span class="file">stk-table-vue/src/StkTable/features/useAreaSelection.ts:319-342</span>)<b>依赖 <code>vs.startIndex/endIndex</code></b> → 滚动换窗即触发 <code>updateSelectionDOM</code>:先 <code>querySelectorAll</code> 清旧选区,再逐行 <code>querySelectorAll('td[data-col-key]')</code> 遍历全部可见格、逐格生成 cellKey 并查 Set。选区在点击后<b>常驻</b>(Esc、点表格外都不清除,实测点击后选区数仍为 1),此后每次滚动都付这笔钱。</li>
|
||||
</ol>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>三、症状二:点击单元格卡顿</h2>
|
||||
|
||||
<h3>3.1 实测 longtask(主线程被阻塞时长)</h3>
|
||||
<table>
|
||||
<tr><th>操作</th><th>longtask 序列</th><th>随后最大帧</th><th>估算阻塞</th></tr>
|
||||
<tr><td>首次点击某可编辑格</td><td class="num">137ms → <b>389ms</b></td><td class="num bad">416.7ms</td><td class="num bad">~400~550ms</td></tr>
|
||||
<tr><td>点另一行同列</td><td class="num">92ms → 136ms</td><td class="num">166.7ms</td><td class="num">~230~300ms</td></tr>
|
||||
<tr><td>行内换到另一列</td><td class="num">130ms → 121ms</td><td class="num">133.3ms</td><td class="num">~250ms</td></tr>
|
||||
<tr><td>点只读列(订舱号)</td><td class="num">97ms → 198ms</td><td class="num">216.6ms</td><td class="num">~300ms</td></tr>
|
||||
</table>
|
||||
<p class="kv">编辑器聚焦本身很快(mousedown 后 ~0.3ms 即聚焦),卡的是点击引发的重渲染/重排链条。</p>
|
||||
|
||||
<h3>3.2 根因链</h3>
|
||||
<ol class="chain">
|
||||
<li>mousedown → 底座选区变化 → <code>handleAreaSelectionChange</code> → <code>activateEditing</code> → <code>editingActive</code> 翻真(<span class="file">G3Table.vue:584-609</span>)。</li>
|
||||
<li><code>displayColumns</code> <b>全量重建</b>:23 个列全部 map 成新对象(<span class="file">G3Table.vue:923-1004</span>)。</li>
|
||||
<li>StkTable <code>props.columns</code> 引用变化 → watch → <code>handleDealColumns</code> + <code>updateMaxRowSpan</code> → nextTick → <code>initVirtualScrollX</code> + <code>updateFixedShadow</code> + <code>updateCustomScrollbar</code>(<span class="file">StkTable.vue:1240-1252</span>)。</li>
|
||||
<li><code>tableHeaders</code> 更换 → <code>cellStyleMap</code> 重建 → <b>全表重渲染</b> + 编辑器挂载 + <code>focus()</code>。CPU profile:主要成本仍在浏览器端(364ms <code>(program)</code>,即全表 style/layout/paint);<code>focus()</code> 本身占 40~57ms。</li>
|
||||
<li>后续点击不重建列(<code>editingActive</code> 已为真),只剩行编辑态切换 + 选区 DOM 更新 + <code>setCurrentRow</code>,故成本降至 ~130~200ms。</li>
|
||||
</ol>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>四、症状三(顺带发现):页面自身的 errorCount 放大编辑提交卡顿</h2>
|
||||
<p><code>views/table-test/index.vue:615-625</code> 的 <code>errorCount</code> 是 computed,依赖整个深层响应式 <code>rows</code>,<b>每次提交(Enter / 失焦 / checkbox)都全量重跑「行数 × 22 列」校验</b>(每格还要跑 <code>validate</code> + <code>checkPrecision</code>)。实测 Enter 提交耗时:</p>
|
||||
<table>
|
||||
<tr><th>数据量</th><th>errorCount 开</th><th>errorCount 关</th><th>差值</th></tr>
|
||||
<tr><td>1000 行</td><td class="num">139ms</td><td class="num">79ms</td><td class="num warn">+60ms</td></tr>
|
||||
<tr><td>5000 行</td><td class="num bad">278ms</td><td class="num">85ms</td><td class="num bad">+193ms</td></tr>
|
||||
</table>
|
||||
<p>按线性外推,选 20000 行时每次提交约多付 <b>~0.8s</b>。这是页面级问题,与组件库无关;工具栏徽章只在打开页面/保存时有意义,不必随每次编辑重算。</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>五、修改方向(按性价比排序,本次未实施)</h2>
|
||||
|
||||
<div class="fix p1">
|
||||
<h4><span class="tag high">P1 · 根子</span> 虚拟滚动开启时避免整表重渲染</h4>
|
||||
<p><b>位置</b>:<code>stk-table-vue</code>(node_modules,属上游库)或 <code>G3Table.vue</code> 的 tbody 渲染组织方式。</p>
|
||||
<p><b>问题</b>:<code>triggerRef(virtualScroll)</code> 一触发,整个 StkTable 单组件重渲染,713 个单元格 props 全量重算与 diff。</p>
|
||||
<p><b>方向</b>:把 tbody 行拆成独立子组件(每行一个渲染作用域),行数据不变时跳过该行 patch;或对可见窗口做更细的 keyed diff 复用。virtual off 满帧已证明 DOM 量不是问题,问题是每帧全量重算。</p>
|
||||
</div>
|
||||
|
||||
<div class="fix p2">
|
||||
<h4><span class="tag mid">P2</span> 去掉无条件的 will-change: transform</h4>
|
||||
<p><b>位置</b>:<code>G3Table.vue:1932-1941</code>(SCSS 块,非 scoped)。</p>
|
||||
<p><b>问题</b>:原生滚动模式下也给 28000px 高的 tbody 常驻合成层;库自己仅在 <code>.exp-scroll-y</code> 下才加(<code>style.less:392-396</code>)。</p>
|
||||
<p><b>方向</b>:与该条件对齐,仅在 transform 滚动模式输出此规则;或直接删除(transform 模式由库自己加)。实测单独去除改善有限(41→38ms),属低成本顺手项。</p>
|
||||
</div>
|
||||
|
||||
<div class="fix p2">
|
||||
<h4><span class="tag mid">P2</span> updateSelectionDOM 不应随滚动窗口全量跑</h4>
|
||||
<p><b>位置</b>:<code>stk-table-vue/src/StkTable/features/useAreaSelection.ts:319-342</code>。</p>
|
||||
<p><b>问题</b>:watcher 依赖 <code>startIndex/endIndex</code>,滚动换窗即对全部可见格做 DOM 遍历 + cellKey 生成;选区常驻导致每次滚动都付。</p>
|
||||
<p><b>方向</b>:选区为空时早退;把「逐格生成 cellKey 查 Set」改为按 <code>selectedCellKeys</code> 直接定位 <code>td[data-col-key]</code> 批量 setAttribute;或只在选区真正变化时重算,滚动换窗时按需增量更新。</p>
|
||||
</div>
|
||||
|
||||
<div class="fix p2">
|
||||
<h4><span class="tag mid">P2</span> 点击时不做全量列重建</h4>
|
||||
<p><b>位置</b>:<code>G3Table.vue:923</code>(<code>displayColumns</code> computed 依赖 <code>editingActive</code>)。</p>
|
||||
<p><b>问题</b>:首次点击 <code>editingActive</code> 翻真 → 23 个列对象全部重建 → 列宽/样式缓存重建 → 全表重排重绘(~400ms)。</p>
|
||||
<p><b>方向</b>:编辑态从「全表列级」下沉为「行级」——只给目标行/目标列注入编辑器(customCell 内部已有 <code>ctx.isEditing(row)</code> 判断,具备按行分发的基礎);或缓存列对象、仅对受影响的列生成新引用,避免 <code>tableHeaders</code>/<code>cellStyleMap</code> 整体失效。</p>
|
||||
</div>
|
||||
|
||||
<div class="fix p1">
|
||||
<h4><span class="tag high">P1 · 零风险</span> errorCount 从 computed 改为按需计算</h4>
|
||||
<p><b>位置</b>:<code>web/src/views/table-test/index.vue:615-625</code>。</p>
|
||||
<p><b>问题</b>:每次提交全量重跑 行数×22 校验,1000 行 +60ms、5000 行 +193ms、20000 行约 +0.8s。</p>
|
||||
<p><b>方向</b>:改为在 <code>saveEdit()</code> 里按需算一次(保存时本就要全表校验,可与 <code>findFirstError</code> 合并成一次遍历);或只统计可见行 + 防抖。徽标语义不变。</p>
|
||||
</div>
|
||||
|
||||
<div class="fix p2">
|
||||
<h4><span class="tag mid">P2</span> 评估每格校验的缓存</h4>
|
||||
<p><b>位置</b>:<code>utils/editable.ts:369-454</code>(<code>displayValue</code> → <code>resolveEditorStatus</code>)。</p>
|
||||
<p><b>问题</b>:滚动时每格每帧重跑 <code>validate</code> + <code>checkPrecision</code> + 日期校验 + 格式化;点击后常驻 customCell 从 10 列涨到 17 列,成本随之上涨。</p>
|
||||
<p><b>方向</b>:校验结果是行数据的派生值,可按 (row, dataIndex, value) 做 WeakMap 缓存,值不变则复用;<code>useNamespace</code> 每格渲染都新建 computed(<code>_composables/useNamespace.ts:41</code>),可下沉为模块级单例。</p>
|
||||
</div>
|
||||
|
||||
<div class="fix p2">
|
||||
<h4><span class="tag low">P3 · 设计取舍</span> 虚拟滚动门控阈值</h4>
|
||||
<p><b>位置</b>:<code>G3Table.vue:251</code>(<code>MIN_VIRTUAL_ROW_COUNT = 100</code>)。</p>
|
||||
<p><b>说明</b>:阈值本身合理(小表示全量渲染更稳),不建议为性能调高——那只是把问题藏到「DOM 全量」里,万行级内存与首渲染会更差。应优先修 P1,让虚拟滚动本身变快。</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>附录:关键代码位置索引</h2>
|
||||
<table>
|
||||
<tr><th>文件</th><th>行</th><th>内容</th></tr>
|
||||
<tr><td><code>g3soft-libs/packages/ui/src/table/G3Table.vue</code></td><td class="num">251</td><td>纵向虚拟门控 <code>MIN_VIRTUAL_ROW_COUNT = 100</code></td></tr>
|
||||
<tr><td></td><td class="num">584</td><td><code>activateEditing</code> / <code>editingActive</code> 翻转</td></tr>
|
||||
<tr><td></td><td class="num">923</td><td><code>displayColumns</code> 全量重建(点击卡顿入口)</td></tr>
|
||||
<tr><td></td><td class="num">1207</td><td><code>handleAreaSelectionChange</code>(点击 → 进编辑链路)</td></tr>
|
||||
<tr><td></td><td class="num">1932</td><td>tbody 无条件 <code>will-change: transform</code></td></tr>
|
||||
<tr><td><code>g3soft-libs/packages/ui/src/table/utils/column.ts</code></td><td class="num">121</td><td>customCell 注入判据(editingActive 后 10 → 17 列)</td></tr>
|
||||
<tr><td><code>g3soft-libs/packages/ui/src/table/utils/editable.ts</code></td><td class="num">369</td><td><code>displayValue</code>:展示态每格跑校验</td></tr>
|
||||
<tr><td><code>g3soft-libs/packages/ui/src/table/style/_overrides.scss</code></td><td class="num">460</td><td>fixed-cell 恒常 sticky 补丁</td></tr>
|
||||
<tr><td><code>stk-table-vue/src/StkTable/StkTable.vue</code></td><td class="num">1240</td><td>columns 变化 → 全量重算链</td></tr>
|
||||
<tr><td><code>stk-table-vue/src/StkTable/StkTable.vue</code></td><td class="num">1927</td><td><code>onTableScroll</code>(rAF 节流的滚动入口)</td></tr>
|
||||
<tr><td><code>stk-table-vue/src/StkTable/features/useAreaSelection.ts</code></td><td class="num">319</td><td>选区 watcher 依赖 startIndex/endIndex</td></tr>
|
||||
<tr><td><code>stk-table-vue/src/StkTable/features/useAreaSelection.ts</code></td><td class="num">217</td><td><code>updateSelectionDOM</code> 全可见格遍历</td></tr>
|
||||
<tr><td><code>web/src/views/table-test/index.vue</code></td><td class="num">615</td><td><code>errorCount</code> computed(页面级提交卡顿)</td></tr>
|
||||
</table>
|
||||
</section>
|
||||
|
||||
<footer>排查基于 2026-10-16 dev server 实测;探针页面与脚本已清理,未改动任何仓库文件。</footer>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in new issue
Block a user