perf: 优化旧版浏览器页面恢复与资源加载

- 页面恢复时延后版本检查并补齐根背景,减少白闪与首帧卡顿

- 面向 Chrome 90 收敛公共分包,按需加载资源弹窗和知识库面板

- 压缩工作流图标并补充延迟加载与可见性生命周期测试
This commit is contained in:
2026-07-31 16:11:14 +08:00
parent 5c29ca9407
commit 12080ceedb
15 changed files with 505 additions and 68 deletions

View File

@@ -16,7 +16,6 @@
html {
@apply text-foreground bg-background font-sans text-[100%];
background-color: hsl(var(--surface-canvas));
font-variation-settings: normal;
line-height: 1.15;
text-size-adjust: 100%;
@@ -34,6 +33,8 @@
html {
@apply size-full;
background-color: hsl(var(--surface-canvas));
/* scrollbar-gutter: stable; */
}

View File

@@ -0,0 +1,76 @@
import { flushPromises, mount } from '@vue/test-utils';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import CheckUpdates from './check-updates.vue';
const { modalOpen } = vi.hoisted(() => ({
modalOpen: vi.fn(),
}));
vi.mock('@easyflow/locales', () => ({
$t: (key: string) => key,
}));
vi.mock('@easyflow-core/popup-ui', () => ({
useEasyFlowModal: () => [
{
name: 'UpdateNoticeModalStub',
template: '<div><slot /></div>',
},
{ open: modalOpen },
],
}));
describe('check updates visibility lifecycle', () => {
let hidden = false;
beforeEach(() => {
hidden = false;
modalOpen.mockClear();
vi.useFakeTimers();
vi.spyOn(document, 'hidden', 'get').mockImplementation(() => hidden);
vi.stubGlobal('location', { hostname: 'customer.example' });
vi.stubGlobal(
'fetch',
vi.fn().mockResolvedValue({
headers: {
get: (name: string) => (name === 'etag' ? 'version-1' : null),
},
}),
);
});
afterEach(() => {
vi.useRealTimers();
vi.restoreAllMocks();
vi.unstubAllGlobals();
});
it('restarts polling without fetching immediately when the page becomes visible', async () => {
const wrapper = mount(CheckUpdates);
const fetchMock = vi.mocked(fetch);
hidden = true;
document.dispatchEvent(new Event('visibilitychange'));
hidden = false;
document.dispatchEvent(new Event('visibilitychange'));
expect(fetchMock).not.toHaveBeenCalled();
await vi.advanceTimersByTimeAsync(59_999);
expect(fetchMock).not.toHaveBeenCalled();
await vi.advanceTimersByTimeAsync(1);
await flushPromises();
expect(fetchMock).toHaveBeenCalledTimes(1);
expect(fetchMock).toHaveBeenCalledWith(
'/',
expect.objectContaining({ method: 'HEAD' }),
);
expect(modalOpen).not.toHaveBeenCalled();
wrapper.unmount();
});
});

View File

@@ -20,7 +20,6 @@ const props = withDefaults(defineProps<Props>(), {
});
let isCheckingUpdates = false;
let resumeCheckFrame: number | undefined;
let versionCheckController: AbortController | null = null;
const VERSION_CHECK_TIMEOUT_MS = 10_000;
const currentVersionTag = ref('');
@@ -121,36 +120,12 @@ function start() {
);
}
function cancelResumeCheck() {
if (resumeCheckFrame === undefined) {
return;
}
cancelAnimationFrame(resumeCheckFrame);
resumeCheckFrame = undefined;
}
function scheduleResumeCheck() {
cancelResumeCheck();
// 页面恢复后先让浏览器完成两帧绘制,再发起版本检查请求。
resumeCheckFrame = requestAnimationFrame(() => {
resumeCheckFrame = requestAnimationFrame(() => {
resumeCheckFrame = undefined;
if (document.hidden) return;
runUpdateCheck().finally(() => {
if (!document.hidden) {
start();
}
});
});
});
}
function handleVisibilitychange() {
if (document.hidden) {
cancelResumeCheck();
stop();
} else {
scheduleResumeCheck();
// 恢复窗口时只重启轮询,避免与旧浏览器的首帧重绘争抢资源。
start();
}
}
@@ -169,7 +144,6 @@ onMounted(() => {
});
onUnmounted(() => {
cancelResumeCheck();
stop();
document.removeEventListener('visibilitychange', handleVisibilitychange);
});