From fbe663994cb038cde8cc51a2a96427fef9c87975 Mon Sep 17 00:00:00 2001 From: yuanyxh <15766118362@139.com> Date: Sun, 19 May 2024 15:09:54 +0800 Subject: [PATCH] feat: add load resource error handle --- src/App.tsx | 92 ++++++++++++++++++--------------- src/router/components/Error.tsx | 17 +++++- src/utils/index.ts | 1 + src/utils/online.ts | 49 ++++++++++++++++++ 4 files changed, 116 insertions(+), 43 deletions(-) create mode 100644 src/utils/online.ts diff --git a/src/App.tsx b/src/App.tsx index f910038..654db1e 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -16,7 +16,7 @@ import type { AppState } from '@/store'; import { useAppStore } from '@/store'; import { - addGlobalListener, + assetsLoadHandle, getStorage, globalEvent, hasLocalStorage, @@ -92,49 +92,52 @@ const App: React.FC = (props) => { } } - function update() { - function handleReEnter() { - serviceWorkerRef.current?.skipWaiting(); - notificationApi.destroy(); - } - - const btn = ( - - ); - - notificationApi.info({ - message: '有新内容', - description: '网站有更新,请点击确认以获取最新内容。', - placement: 'bottomRight', - style: { padding: '14px 16px', width: 300 }, - btn, - duration: null - }); - - if (!frontDesk && enableNotification) { - notify({ - icon: '/favicon.ico', - title: '有新内容', - data: '网站内容有更新,您可以前往查看更新。' + { + const update = () => { + function handleReEnter() { + serviceWorkerRef.current?.skipWaiting(); + notificationApi.destroy(); + } + + const btn = ( + + ); + + notificationApi.info({ + message: '有新内容', + description: '网站有更新,请点击确认以获取最新内容。', + placement: 'bottomRight', + style: { padding: '14px 16px', width: 300 }, + btn, + duration: null }); + + if (!frontDesk && enableNotification) { + notify({ + icon: '/favicon.ico', + title: '有新内容', + data: '网站内容有更新,您可以前往查看更新。' + }); + } + }; + serviceWorkerRef.current = new ServiceWorkerManager({ update }); + if (import.meta.env.PROD && enableServiceWorkerCache) { + serviceWorkerRef.current.registerServiceWorker(); + } else { + serviceWorkerRef.current.unregisterServiceWorker(); } } - serviceWorkerRef.current = new ServiceWorkerManager({ update }); - if (import.meta.env.PROD && enableServiceWorkerCache) { - serviceWorkerRef.current.registerServiceWorker(); - } else { - serviceWorkerRef.current.unregisterServiceWorker(); - } - const removePageShowListener = addGlobalListener('pageshow', () => { - setFrontDesk(true); - }); - const removePageHideListener = addGlobalListener('pagehide', () => { - setFrontDesk(false); - }); darkModeQuery.addEventListener('change', listenerColorSchemeChange); + const listenerVisibilityChange = () => { + setFrontDesk(!window.document.hidden); + }; + window.document.addEventListener( + 'visibilitychange', + listenerVisibilityChange + ); const cancelGlobalUserTipsEventListener = globalEvent.on( 'user_tips', @@ -149,12 +152,19 @@ const App: React.FC = (props) => { } ); + const cancelListenerReLoad = assetsLoadHandle.reLoadByOnline( + () => !enableServiceWorkerCache + ); + return () => { - removePageShowListener(); - removePageHideListener(); + cancelListenerReLoad(); cancelGlobalUserTipsEventListener(); cancelGlobalUserAlertEventListener(); darkModeQuery.removeEventListener('change', listenerColorSchemeChange); + window.document.removeEventListener( + 'visibilitychange', + listenerVisibilityChange + ); }; }, []); diff --git a/src/router/components/Error.tsx b/src/router/components/Error.tsx index 719a8df..6e4ecfd 100644 --- a/src/router/components/Error.tsx +++ b/src/router/components/Error.tsx @@ -1,6 +1,14 @@ +import { useEffect } from 'react'; + import { Button, Typography } from 'antd'; -import { copy, serializeError, success, UnknownError } from '@/utils'; +import { + assetsLoadHandle, + copy, + serializeError, + success, + UnknownError +} from '@/utils'; import { Icon } from '@/components'; @@ -13,10 +21,15 @@ interface IErrorProps { } const ErrorCom: React.FC = (props) => { - const { errorInfo: { error } = { hasError: false } } = props; + const { errorInfo: { hasError, error } = { hasError: true } } = props; const history = useHistory(); + useEffect(() => { + // When rendering errors, we need to know that sending the error to the resource load processor + hasError && assetsLoadHandle.emitRenderError(); + }, []); + const handleReload = () => { history.go(0); }; diff --git a/src/utils/index.ts b/src/utils/index.ts index 9031231..ffce58c 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -5,6 +5,7 @@ export { default as globalEvent } from './globalEventEmitter'; export * from './globalEventEmitter'; export * from './localStorageTools'; export * from './notification'; +export { default as assetsLoadHandle } from './online.ts'; export * from './permissions'; export { default as ServiceWorkerManager } from './serviceWorker'; export * from './serviceWorker'; diff --git a/src/utils/online.ts b/src/utils/online.ts new file mode 100644 index 0000000..319ede1 --- /dev/null +++ b/src/utils/online.ts @@ -0,0 +1,49 @@ +import EventEmitter from './event'; +import { warning } from './globalEventEmitter'; +import { addGlobalListener } from './windowEvent'; + +import { sleep } from '.'; + +const LOAD_ERROR_KEY = 'load_error'; + +class AssetsLoadHandle { + event = new EventEmitter(); + + private cb = () => false; + + private listener = () => { + if (!window.navigator.onLine) { + const _listener = () => { + cancelOnlineListener(); + + if (this.cb()) { + warning('即将在三秒后重载页面。'); + sleep(3000, () => window.location.reload()); + } + }; + + const cancelOnlineListener = addGlobalListener('online', _listener); + } + }; + + emitRenderError() { + this.event.emit(LOAD_ERROR_KEY); + } + + reLoadByOnline(cb: () => boolean) { + this.cb = cb; + + const cancelGlobalErrorListener = addGlobalListener('error', this.listener); + const cancelRenderErrorListener = this.event.on( + LOAD_ERROR_KEY, + this.listener + ); + + return () => { + cancelGlobalErrorListener(); + cancelRenderErrorListener(); + }; + } +} + +export default new AssetsLoadHandle();