perf(frontend): replace blank Suspense fallbacks with Spin, switch to matchMedia hook, add virtual table scrolling (#6187)

- routes.tsx, LazyMount.tsx: replace Suspense fallback={null} with Spin
  loader so page transitions and lazy modals never show blank content
- useMediaQuery.ts: switch from resize event to matchMedia change event,
  eliminating state updates on every pixel drag; export MOBILE_BREAKPOINT_PX
- SubPage.tsx: drop duplicate inline isMobile logic (7 lines), use shared
  useMediaQuery(576) (2 lines)
- ClientsPage, InboundList, HostList, NodeList: add virtual + scroll.y to
  Table for viewport-only DOM rendering of large datasets
This commit is contained in:
PathGao
2026-08-15 22:44:08 +08:00
committed by GitHub
parent 0f14ce7551
commit 8e7fb144ee
8 changed files with 42 additions and 20 deletions
+2 -1
View File
@@ -1350,7 +1350,8 @@ export default function ClientsPage() {
rowSelection={rowSelection}
pagination={tablePagination}
size="small"
scroll={{ x: 1200 }}
scroll={{ x: 1200, y: 'calc(100vh - 380px)' }}
virtual
onChange={onTableChange}
locale={{
emptyText: (
+2 -1
View File
@@ -231,7 +231,8 @@ export default function HostList(props: HostListProps) {
columns={columns}
dataSource={sorted}
pagination={false}
scroll={{ x: 'max-content' }}
scroll={{ x: 'max-content', y: 'calc(100vh - 280px)' }}
virtual
rowSelection={{
selectedRowKeys: selectedGroupIds,
onChange: (keys) => onSelectionChange(keys as string[]),
@@ -286,7 +286,8 @@ export default function InboundList({
onChange: (keys: Key[]) => setSelectedRowKeys(keys as number[]),
}}
pagination={paginationFor(visibleInbounds)}
scroll={{ x: tableScrollX }}
scroll={{ x: tableScrollX, y: 'calc(100vh - 320px)' }}
virtual
style={{ marginTop: 10 }}
size="small"
locale={{
+2 -1
View File
@@ -648,7 +648,8 @@ export default function NodeList({
columns={columns}
pagination={false}
loading={loading}
scroll={{ x: 'max-content' }}
scroll={{ x: 'max-content', y: 'calc(100vh - 320px)' }}
virtual
size="middle"
rowKey="key"
rowSelection={dataSource.length > 1 ? {
+2 -8
View File
@@ -38,6 +38,7 @@ import { LinkTags, parseLinkParts } from '@/lib/xray/link-label';
import ConfigBlock from '@/components/clients/ConfigBlock';
import { setMessageInstance } from '@/utils/messageBus';
import { pauseAnimationsUntilLeave, useTheme } from '@/hooks/useTheme';
import { useMediaQuery } from '@/hooks/useMediaQuery';
import SubUsageSummary from './SubUsageSummary';
import './SubPage.css';
@@ -84,16 +85,9 @@ export default function SubPage() {
const { isDark, isUltra, toggleTheme, toggleUltra, antdThemeConfig } = useTheme();
const [messageApi, messageContextHolder] = message.useMessage();
useEffect(() => { setMessageInstance(messageApi); }, [messageApi]);
const [isMobile, setIsMobile] = useState<boolean>(() => window.innerWidth < 576);
const { isMobile } = useMediaQuery(576);
const [lang, setLang] = useState<string>(() => LanguageManager.getLanguage());
useEffect(() => {
const onResize = () => setIsMobile(window.innerWidth < 576);
window.addEventListener('resize', onResize);
return () => window.removeEventListener('resize', onResize);
}, []);
const onLangChange = useCallback((next: string) => {
setLang(next);
LanguageManager.setLanguage(next);