perf: 优化旧版浏览器页面恢复与资源加载
- 页面恢复时延后版本检查并补齐根背景,减少白闪与首帧卡顿 - 面向 Chrome 90 收敛公共分包,按需加载资源弹窗和知识库面板 - 压缩工作流图标并补充延迟加载与可见性生命周期测试
This commit is contained in:
@@ -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();
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user