mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-04 08:27:46 +08:00
perf(frontend): 收敛导航预取并去重首屏请求
- 导航预取仅保留 pointerdown 触发,移除 mouseenter/focus,避免鼠标划过误触发 - 后台预取只做组件懒加载,不再预取各页业务数据,减少首屏资源争抢 - 版本状态检查增加 sessionStorage 缓存(正常 20 分钟 / 错误 5 分钟 TTL) - fetchModules、必读公告拉取增加请求去重,避免并发重复请求 - 更新检查改用可清理的定时器,组件卸载时清理 - UsageRecordsTable 搜索防抖改为自定义实现,卸载时取消挂起 emit 并补充测试
This commit is contained in:
@@ -1057,8 +1057,8 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import { useDebounceFn, useLocalStorage } from '@vueuse/core'
|
||||
import { ref, computed, onBeforeUnmount, watch } from 'vue'
|
||||
import { useLocalStorage } from '@vueuse/core'
|
||||
import {
|
||||
TableCard,
|
||||
Badge,
|
||||
@@ -1355,10 +1355,26 @@ const timeRangeModel = computed({
|
||||
})
|
||||
|
||||
// 通用搜索(输入防抖)
|
||||
const SEARCH_EMIT_DEBOUNCE_MS = 300
|
||||
const localSearch = ref(props.filterSearch)
|
||||
const emitSearchDebounced = useDebounceFn((value: string) => {
|
||||
emit('update:filterSearch', value)
|
||||
}, 300)
|
||||
let searchEmitTimer: ReturnType<typeof setTimeout> | null = null
|
||||
|
||||
function cancelPendingSearchEmit() {
|
||||
if (searchEmitTimer !== null) {
|
||||
clearTimeout(searchEmitTimer)
|
||||
searchEmitTimer = null
|
||||
}
|
||||
}
|
||||
|
||||
function scheduleSearchEmit(value: string) {
|
||||
cancelPendingSearchEmit()
|
||||
searchEmitTimer = setTimeout(() => {
|
||||
searchEmitTimer = null
|
||||
if (value !== props.filterSearch) {
|
||||
emit('update:filterSearch', value)
|
||||
}
|
||||
}, SEARCH_EMIT_DEBOUNCE_MS)
|
||||
}
|
||||
|
||||
function getDisplayStatus(record: UsageRecord) {
|
||||
return resolveDisplayRequestStatus(record)
|
||||
@@ -1422,12 +1438,18 @@ function formatRecordProviderSegment(record: UsageRecord): string {
|
||||
|
||||
watch(() => props.filterSearch, (value) => {
|
||||
if (value !== localSearch.value) {
|
||||
cancelPendingSearchEmit()
|
||||
localSearch.value = value
|
||||
}
|
||||
})
|
||||
|
||||
watch(localSearch, (value) => {
|
||||
emitSearchDebounced(value)
|
||||
if (value === props.filterSearch) return
|
||||
scheduleSearchEmit(value)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
cancelPendingSearchEmit()
|
||||
})
|
||||
|
||||
// 使用复用的行点击逻辑
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import { createApp, defineComponent, h, type App } from 'vue'
|
||||
import { createApp, defineComponent, h, nextTick, type App } from 'vue'
|
||||
import UsageRecordsTable from '../UsageRecordsTable.vue'
|
||||
import type { UsageRecord } from '../../types'
|
||||
|
||||
@@ -167,6 +167,7 @@ afterEach(() => {
|
||||
app.unmount()
|
||||
root.remove()
|
||||
}
|
||||
vi.useRealTimers()
|
||||
})
|
||||
|
||||
describe('UsageRecordsTable', () => {
|
||||
@@ -314,6 +315,31 @@ describe('UsageRecordsTable', () => {
|
||||
expect(onUpdateHideUnknownRecords).toHaveBeenCalledWith(true)
|
||||
})
|
||||
|
||||
it('debounces usage search updates', async () => {
|
||||
vi.useFakeTimers()
|
||||
const onUpdateFilterSearch = vi.fn()
|
||||
const root = mountUsageRecordsTable([buildRecord()], {
|
||||
'onUpdate:filterSearch': onUpdateFilterSearch,
|
||||
})
|
||||
const input = root.querySelector<HTMLInputElement>('#usage-records-search')
|
||||
expect(input).not.toBeNull()
|
||||
|
||||
input!.value = 'a'
|
||||
input!.dispatchEvent(new Event('input'))
|
||||
input!.value = 'ab'
|
||||
input!.dispatchEvent(new Event('input'))
|
||||
input!.value = 'abc'
|
||||
input!.dispatchEvent(new Event('input'))
|
||||
await nextTick()
|
||||
|
||||
await vi.advanceTimersByTimeAsync(299)
|
||||
expect(onUpdateFilterSearch).not.toHaveBeenCalled()
|
||||
|
||||
await vi.advanceTimersByTimeAsync(1)
|
||||
expect(onUpdateFilterSearch).toHaveBeenCalledTimes(1)
|
||||
expect(onUpdateFilterSearch).toHaveBeenCalledWith('abc')
|
||||
})
|
||||
|
||||
it('shows retry and fallback markers together when both flags are set', () => {
|
||||
const root = mountUsageRecordsTable([buildRecord({
|
||||
has_fallback: true,
|
||||
|
||||
Reference in New Issue
Block a user