原来换到没缓存的一条要先卸掉代码区、转圈、再整个挂回来,CPU 降速 6 倍下每次卡 60–100ms;走缓存的只换内容不卡。现在这一条加载完顺手把上下两条也拉回来,同一条 只发一次请求(预取还在路上时按到它就接着等),按住 ↓ 划过去的那些行不预取。 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
e511d82f3b
commit
95dc2f24e6
3 files changed
+52
-4
No files matched your search
@@ -23,6 +23,8 @@ import UserName from 'shared/components/UserName.vue'
|
||||
*/
|
||||
const props = defineProps<{
|
||||
row: SubmissionListItem
|
||||
/** 按 ↑ ↓ 会落到的上一条、下一条:这条加载完顺手把它们也拉回来,换条时不用等网络 */
|
||||
neighbors?: SubmissionListItem[]
|
||||
/** 老师那套:重新判题、逐个测试点、复制两项收进「⋯」 */
|
||||
teacher: boolean
|
||||
/** 在比赛的提交列表里(不能重判,题目链接走比赛) */
|
||||
@@ -61,7 +63,42 @@ const detail = ref<Submission | null>(null)
|
||||
const loading = ref(false)
|
||||
const failed = ref(false)
|
||||
|
||||
const judging = computed(() => props.row.result === SubmissionStatus.pending || props.row.result === SubmissionStatus.judging)
|
||||
function isJudging(row: SubmissionListItem) {
|
||||
return row.result === SubmissionStatus.pending || row.result === SubmissionStatus.judging
|
||||
}
|
||||
const judging = computed(() => isJudging(props.row))
|
||||
|
||||
/**
|
||||
* 同一条只发一次请求:预取的还在路上时按到了它,接着等那一个,不再发第二个
|
||||
*/
|
||||
const inflight = new Map<string, Promise<Submission>>()
|
||||
function fetchDetail(id: string) {
|
||||
let request = inflight.get(id)
|
||||
if (!request) {
|
||||
request = getSubmission(id).finally(() => inflight.delete(id))
|
||||
inflight.set(id, request)
|
||||
}
|
||||
return request
|
||||
}
|
||||
|
||||
/**
|
||||
* 预取上一条、下一条。只在这一条落定之后才做(按住 ↓ 一路划过去的那些行不预取),
|
||||
* 进缓存的口径和 load() 一样:还在判的不要,列表和详情的结果对不上也不要。
|
||||
*
|
||||
* 没预取的时候,换到没看过的一条要先把代码区卸掉、转圈、再整个重新挂上 ——
|
||||
* 2026-11 实测(生产构建、CPU 降速 6 倍)每次卡 60–100ms;走缓存的只换内容,不卡。
|
||||
*/
|
||||
function prefetch() {
|
||||
for (const row of props.neighbors ?? []) {
|
||||
if (!row.showLink || isJudging(row) || cache.get(row.id)?.result === row.result) continue
|
||||
fetchDetail(row.id).then(
|
||||
(res) => {
|
||||
if (res.result === row.result) cache.set(row.id, res)
|
||||
},
|
||||
() => {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// 每次加载一个序号:同一条先按「判题中」拉了一次、判完又拉一次时,晚回来的旧请求不能盖掉新的
|
||||
let loadSeq = 0
|
||||
@@ -78,15 +115,17 @@ async function load() {
|
||||
const hit = cache.get(row.id)
|
||||
if (hit && hit.result === row.result) {
|
||||
detail.value = hit
|
||||
prefetch()
|
||||
return
|
||||
}
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await getSubmission(row.id)
|
||||
const res = await fetchDetail(row.id)
|
||||
if (seq !== loadSeq) return
|
||||
// 列表那边还在判、详情这边已经判完了(或反过来)都不缓存,等两边对上
|
||||
if (!judging.value && res.result === props.row.result) cache.set(row.id, res)
|
||||
detail.value = res
|
||||
prefetch()
|
||||
} catch {
|
||||
if (seq === loadSeq) failed.value = true
|
||||
} finally {
|
||||
|
||||
@@ -35,6 +35,13 @@ export function useRowCursor(options: {
|
||||
const index = navigable.value.findIndex((row) => row.id === selectedId.value)
|
||||
return index === -1 ? null : { index: index + 1, count: navigable.value.length }
|
||||
})
|
||||
/** 按 ↑ ↓ 会落到的上一条、下一条(本页之内),右栏拿去预取 */
|
||||
const neighbors = computed(() => {
|
||||
const list = navigable.value
|
||||
const index = list.findIndex((row) => row.id === selectedId.value)
|
||||
if (index === -1) return []
|
||||
return [list[index + 1], list[index - 1]].filter((row): row is Row => row !== undefined)
|
||||
})
|
||||
|
||||
// 翻页是异步的,先记下「落地后停在哪一头」,新一页回来再选
|
||||
let pendingJump: 'first' | 'last' | null = null
|
||||
@@ -125,5 +132,5 @@ export function useRowCursor(options: {
|
||||
move(e.key === 'ArrowUp' ? -1 : 1)
|
||||
})
|
||||
|
||||
return { selectedId, selectedRow, position, maxPage, select, move, settleSelection, forgetJump }
|
||||
return { selectedId, selectedRow, position, neighbors, maxPage, select, move, settleSelection, forgetJump }
|
||||
}
|
||||
@@ -263,7 +263,7 @@ watch(
|
||||
|
||||
// ==================== 选中与翻阅 ====================
|
||||
|
||||
const { selectedId, selectedRow, position, maxPage, select, move, settleSelection, forgetJump } = useRowCursor({
|
||||
const { selectedId, selectedRow, position, neighbors, maxPage, select, move, settleSelection, forgetJump } = useRowCursor({
|
||||
rows,
|
||||
total,
|
||||
query,
|
||||
@@ -858,6 +858,7 @@ function dayBreak(index: number) {
|
||||
<SubmissionPane
|
||||
v-else
|
||||
:row="selectedRow as SubmissionListItem"
|
||||
:neighbors="neighbors as SubmissionListItem[]"
|
||||
:teacher="teacher"
|
||||
:class-name="query.className"
|
||||
:contest="inContest"
|
||||
@@ -893,6 +894,7 @@ function dayBreak(index: number) {
|
||||
<SubmissionPane
|
||||
v-else
|
||||
:row="selectedRow as SubmissionListItem"
|
||||
:neighbors="neighbors as SubmissionListItem[]"
|
||||
:teacher="teacher"
|
||||
:class-name="query.className"
|
||||
:contest="inContest"
|
||||
|
||||
Reference in new issue
Block a user