mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-11-05 07:02:09 +08:00
perf: narrow app data subscriptions
This commit is contained in:
@@ -4,7 +4,12 @@ import { useMemo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import { useClash } from '@/hooks/use-clash'
|
||||
import { useAppData } from '@/providers/app-data-context'
|
||||
import {
|
||||
useClashConfigData,
|
||||
useRulesData,
|
||||
useSystemData,
|
||||
useUptimeData,
|
||||
} from '@/providers/app-data-context'
|
||||
|
||||
import { EnhancedCard } from './enhanced-card'
|
||||
|
||||
@@ -19,7 +24,10 @@ const formatUptime = (uptimeMs: number) => {
|
||||
export const ClashInfoCard = () => {
|
||||
const { t } = useTranslation()
|
||||
const { version: clashVersion } = useClash()
|
||||
const { clashConfig, rules, uptime, systemProxyAddress } = useAppData()
|
||||
const { clashConfig } = useClashConfigData()
|
||||
const { rules } = useRulesData()
|
||||
const { uptime } = useUptimeData()
|
||||
const { systemProxyAddress } = useSystemData()
|
||||
|
||||
// 使用useMemo缓存格式化后的uptime,避免频繁计算
|
||||
const formattedUptime = useMemo(() => formatUptime(uptime), [uptime])
|
||||
|
||||
@@ -10,7 +10,11 @@ import { useTranslation } from 'react-i18next'
|
||||
import { closeAllConnections } from 'tauri-plugin-mihomo-api'
|
||||
|
||||
import { useVerge } from '@/hooks/use-verge'
|
||||
import { useAppData } from '@/providers/app-data-context'
|
||||
import {
|
||||
useAppRefreshers,
|
||||
useClashConfigData,
|
||||
useCoreDataStatus,
|
||||
} from '@/providers/app-data-context'
|
||||
import { patchClashMode } from '@/services/cmds'
|
||||
import type { TranslationKey } from '@/types/generated/i18n-keys'
|
||||
|
||||
@@ -41,7 +45,9 @@ const MODE_META: Record<
|
||||
export const ClashModeCard = () => {
|
||||
const { t } = useTranslation()
|
||||
const { verge } = useVerge()
|
||||
const { clashConfig, isCoreDataPending, refreshClashConfig } = useAppData()
|
||||
const { clashConfig } = useClashConfigData()
|
||||
const { isCoreDataPending } = useCoreDataStatus()
|
||||
const { refreshClashConfig } = useAppRefreshers()
|
||||
|
||||
// 支持的模式列表
|
||||
const modeList = CLASH_MODES
|
||||
|
||||
@@ -37,7 +37,13 @@ import { EnhancedCard } from '@/components/home/enhanced-card'
|
||||
import { useProfiles } from '@/hooks/use-profiles'
|
||||
import { useProxySelection } from '@/hooks/use-proxy-selection'
|
||||
import { useVerge } from '@/hooks/use-verge'
|
||||
import { useAppData } from '@/providers/app-data-context'
|
||||
import {
|
||||
useAppRefreshers,
|
||||
useClashConfigData,
|
||||
useCoreDataStatus,
|
||||
useProxiesData,
|
||||
useRulesData,
|
||||
} from '@/providers/app-data-context'
|
||||
import delayManager from '@/services/delay'
|
||||
import { debugLog } from '@/utils/debug'
|
||||
|
||||
@@ -105,8 +111,11 @@ export const CurrentProxyCard = () => {
|
||||
const { t } = useTranslation()
|
||||
const navigate = useNavigate()
|
||||
const theme = useTheme()
|
||||
const { proxies, clashConfig, isCoreDataPending, refreshProxy, rules } =
|
||||
useAppData()
|
||||
const { proxies } = useProxiesData()
|
||||
const { clashConfig } = useClashConfigData()
|
||||
const { rules } = useRulesData()
|
||||
const { refreshProxy } = useAppRefreshers()
|
||||
const { isCoreDataPending } = useCoreDataStatus()
|
||||
const { verge } = useVerge()
|
||||
const { current: currentProfile } = useProfiles()
|
||||
const autoDelayEnabled = verge?.enable_auto_delay_detection ?? false
|
||||
|
||||
@@ -24,7 +24,7 @@ import { useCallback, useMemo, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useNavigate } from 'react-router'
|
||||
|
||||
import { useAppData } from '@/providers/app-data-context'
|
||||
import { useAppRefreshers } from '@/providers/app-data-context'
|
||||
import { openWebUrl, updateProfile } from '@/services/cmds'
|
||||
import { showNotice } from '@/services/notice-service'
|
||||
import parseTraffic from '@/utils/parse-traffic'
|
||||
@@ -281,7 +281,7 @@ export const HomeProfileCard = ({
|
||||
}: HomeProfileCardProps) => {
|
||||
const { t } = useTranslation()
|
||||
const navigate = useNavigate()
|
||||
const { refreshAll } = useAppData()
|
||||
const { refreshAll } = useAppRefreshers()
|
||||
|
||||
// 更新当前订阅
|
||||
const [updating, setUpdating] = useState(false)
|
||||
|
||||
@@ -22,7 +22,7 @@ import { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { updateProxyProvider } from 'tauri-plugin-mihomo-api'
|
||||
|
||||
import { useAppData } from '@/providers/app-data-context'
|
||||
import { useAppRefreshers, useProxiesData } from '@/providers/app-data-context'
|
||||
import { showNotice } from '@/services/notice-service'
|
||||
import parseTraffic from '@/utils/parse-traffic'
|
||||
|
||||
@@ -48,7 +48,8 @@ const parseExpire = (expire?: number) => {
|
||||
export const ProviderButton = () => {
|
||||
const { t } = useTranslation()
|
||||
const [open, setOpen] = useState(false)
|
||||
const { proxyProviders, refreshProxy, refreshProxyProviders } = useAppData()
|
||||
const { proxyProviders } = useProxiesData()
|
||||
const { refreshProxy, refreshProxyProviders } = useAppRefreshers()
|
||||
const [updating, setUpdating] = useState<Record<string, boolean>>({})
|
||||
|
||||
// 检查是否有提供者
|
||||
|
||||
@@ -40,7 +40,7 @@ import {
|
||||
selectNodeForGroup,
|
||||
} from 'tauri-plugin-mihomo-api'
|
||||
|
||||
import { useAppData } from '@/providers/app-data-context'
|
||||
import { useAppRefreshers, useProxiesData } from '@/providers/app-data-context'
|
||||
import { updateProxyChainConfigInRuntime } from '@/services/cmds'
|
||||
import { debugLog } from '@/utils/debug'
|
||||
|
||||
@@ -251,7 +251,8 @@ export const ProxyChain = ({
|
||||
}: ProxyChainProps) => {
|
||||
const theme = useTheme()
|
||||
const { t } = useTranslation()
|
||||
const { proxies, refreshProxy } = useAppData()
|
||||
const { proxies } = useProxiesData()
|
||||
const { refreshProxy } = useAppRefreshers()
|
||||
const [isConnecting, setIsConnecting] = useState(false)
|
||||
const markUnsavedChanges = useCallback(() => {
|
||||
onMarkUnsavedChanges?.()
|
||||
|
||||
@@ -19,7 +19,7 @@ import { delayGroup, healthcheckProxyProvider } from 'tauri-plugin-mihomo-api'
|
||||
import { BaseEmpty } from '@/components/base'
|
||||
import { useProxySelection } from '@/hooks/use-proxy-selection'
|
||||
import { useVerge } from '@/hooks/use-verge'
|
||||
import { useAppData } from '@/providers/app-data-context'
|
||||
import { useProxiesData } from '@/providers/app-data-context'
|
||||
import { calcuProxies, updateProxyChainConfigInRuntime } from '@/services/cmds'
|
||||
import delayManager from '@/services/delay'
|
||||
import { debugLog } from '@/utils/debug'
|
||||
@@ -57,7 +57,7 @@ export const ProxyGroups = (props: Props) => {
|
||||
const { t } = useTranslation()
|
||||
const { mode, isChainMode = false, chainConfigData } = props
|
||||
|
||||
// Drive 3s polling on the shared TQ cache; data is read via useAppData() below
|
||||
// Drive 3s polling on the shared TQ cache; data is read via granular context below
|
||||
useQuery({
|
||||
queryKey: ['getProxies'],
|
||||
queryFn: calcuProxies,
|
||||
@@ -95,7 +95,7 @@ export const ProxyGroups = (props: Props) => {
|
||||
}>({ open: false, message: '' })
|
||||
|
||||
const { verge } = useVerge()
|
||||
const { proxies: proxiesData } = useAppData()
|
||||
const { proxies: proxiesData } = useProxiesData()
|
||||
const groups = proxiesData?.groups
|
||||
const availableGroups = useMemo(() => {
|
||||
if (!groups) return []
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useEffect, useMemo, useRef } from 'react'
|
||||
|
||||
import { useRuntimeConfig } from '@/hooks/use-clash'
|
||||
import { useVerge } from '@/hooks/use-verge'
|
||||
import { useAppData } from '@/providers/app-data-context'
|
||||
import { useAppRefreshers, useProxiesData } from '@/providers/app-data-context'
|
||||
import delayManager from '@/services/delay'
|
||||
import { debugLog } from '@/utils/debug'
|
||||
|
||||
@@ -108,7 +108,8 @@ export const useRenderList = (
|
||||
selectedGroup?: string | null,
|
||||
) => {
|
||||
// 使用全局数据提供者
|
||||
const { proxies: proxiesData, refreshProxy } = useAppData()
|
||||
const { proxies: proxiesData } = useProxiesData()
|
||||
const { refreshProxy } = useAppRefreshers()
|
||||
const { verge } = useVerge()
|
||||
const { width } = useWindowWidth()
|
||||
const [headStates, setHeadState] = useHeadStateNew()
|
||||
|
||||
@@ -21,7 +21,7 @@ import { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { updateRuleProvider } from 'tauri-plugin-mihomo-api'
|
||||
|
||||
import { useAppData } from '@/providers/app-data-context'
|
||||
import { useAppRefreshers, useRulesData } from '@/providers/app-data-context'
|
||||
import { showNotice } from '@/services/notice-service'
|
||||
|
||||
// 辅助组件 - 类型框
|
||||
@@ -40,7 +40,8 @@ const TypeBox = styled(Box)<{ component?: React.ElementType }>(({ theme }) => ({
|
||||
export const ProviderButton = () => {
|
||||
const { t } = useTranslation()
|
||||
const [open, setOpen] = useState(false)
|
||||
const { ruleProviders, refreshRules, refreshRuleProviders } = useAppData()
|
||||
const { ruleProviders } = useRulesData()
|
||||
const { refreshRules, refreshRuleProviders } = useAppRefreshers()
|
||||
const [updating, setUpdating] = useState<Record<string, boolean>>({})
|
||||
|
||||
// 检查是否有提供者
|
||||
|
||||
@@ -34,7 +34,7 @@ import {
|
||||
import { EditorViewer } from '@/components/profile/editor-viewer'
|
||||
import { useSystemProxyState } from '@/hooks/use-system-proxy-state'
|
||||
import { useVerge } from '@/hooks/use-verge'
|
||||
import { useAppData } from '@/providers/app-data-context'
|
||||
import { useClashConfigData, useSystemData } from '@/providers/app-data-context'
|
||||
import {
|
||||
getAutotemProxy,
|
||||
getNetworkInterfacesInfo,
|
||||
@@ -108,7 +108,7 @@ export const SysproxyViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
const { verge, patchVerge, mutateVerge } = useVerge()
|
||||
const [hostOptions, setHostOptions] = useState<string[]>([])
|
||||
|
||||
const { clashConfig } = useAppData()
|
||||
const { clashConfig } = useClashConfigData()
|
||||
const { indicator: isProxyReallyEnabled, invalidateProxyState } =
|
||||
useSystemProxyState()
|
||||
|
||||
@@ -179,7 +179,7 @@ export const SysproxyViewer = forwardRef<DialogRef>((props, ref) => {
|
||||
updateProxy()
|
||||
}, [clashConfig?.mixedPort, enabled, value.pac, invalidateProxyState])
|
||||
|
||||
const { systemProxyAddress } = useAppData()
|
||||
const { systemProxyAddress } = useSystemData()
|
||||
|
||||
// 为当前状态计算系统代理地址
|
||||
const getSystemProxyAddress = useMemo(() => {
|
||||
|
||||
@@ -16,7 +16,7 @@ import { useTranslation } from 'react-i18next'
|
||||
|
||||
import { BaseDialog } from '@/components/base'
|
||||
import { useClash } from '@/hooks/use-clash'
|
||||
import { useAppData } from '@/providers/app-data-context'
|
||||
import { useProxiesData } from '@/providers/app-data-context'
|
||||
import { isPortInUse } from '@/services/cmds'
|
||||
import { showNotice } from '@/services/notice-service'
|
||||
import {
|
||||
@@ -92,7 +92,7 @@ export const TunnelsViewer = forwardRef<TunnelsViewerRef>((_, ref) => {
|
||||
})
|
||||
}, [draftTunnels])
|
||||
|
||||
const { proxies } = useAppData()
|
||||
const { proxies } = useProxiesData()
|
||||
|
||||
const proxyGroups = useMemo<IProxyGroupItem[]>(() => {
|
||||
return proxies?.groups ?? []
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import { useMemo } from 'react'
|
||||
|
||||
import { useAppData } from '@/providers/app-data-context'
|
||||
import {
|
||||
useAppRefreshers,
|
||||
useClashConfigData,
|
||||
useProxiesData,
|
||||
} from '@/providers/app-data-context'
|
||||
|
||||
// 定义代理组类型
|
||||
interface ProxyGroup {
|
||||
@@ -11,7 +15,9 @@ interface ProxyGroup {
|
||||
// 获取当前代理节点信息的自定义Hook
|
||||
export const useCurrentProxy = () => {
|
||||
// 从AppDataProvider获取数据
|
||||
const { proxies, clashConfig, refreshProxy } = useAppData()
|
||||
const { proxies } = useProxiesData()
|
||||
const { clashConfig } = useClashConfigData()
|
||||
const { refreshProxy } = useAppRefreshers()
|
||||
|
||||
// 获取当前模式
|
||||
const currentMode = clashConfig?.mode?.toLowerCase() || 'rule'
|
||||
|
||||
@@ -3,14 +3,15 @@ import { useRef } from 'react'
|
||||
import { closeAllConnections } from 'tauri-plugin-mihomo-api'
|
||||
|
||||
import { useVerge } from '@/hooks/use-verge'
|
||||
import { useAppData } from '@/providers/app-data-context'
|
||||
import { useClashConfigData, useSystemData } from '@/providers/app-data-context'
|
||||
import { getAutotemProxy } from '@/services/cmds'
|
||||
import { queryClient } from '@/services/query-client'
|
||||
|
||||
// 系统代理状态检测统一逻辑
|
||||
export const useSystemProxyState = () => {
|
||||
const { verge, mutateVerge, patchVerge } = useVerge()
|
||||
const { sysproxy, clashConfig } = useAppData()
|
||||
const { sysproxy } = useSystemData()
|
||||
const { clashConfig } = useClashConfigData()
|
||||
const { data: autoproxy } = useQuery({
|
||||
queryKey: ['getAutotemProxy'],
|
||||
queryFn: getAutotemProxy,
|
||||
|
||||
@@ -9,7 +9,10 @@ import { BasePage } from '@/components/base'
|
||||
import { ProviderButton } from '@/components/proxy/provider-button'
|
||||
import { ProxyGroups } from '@/components/proxy/proxy-groups'
|
||||
import { useVerge } from '@/hooks/use-verge'
|
||||
import { useAppData } from '@/providers/app-data-context'
|
||||
import {
|
||||
useAppRefreshers,
|
||||
useClashConfigData,
|
||||
} from '@/providers/app-data-context'
|
||||
import {
|
||||
getRuntimeProxyChainConfig,
|
||||
patchClashMode,
|
||||
@@ -41,7 +44,8 @@ const ProxyPage = () => {
|
||||
null as string | null,
|
||||
)
|
||||
|
||||
const { clashConfig, refreshClashConfig } = useAppData()
|
||||
const { clashConfig } = useClashConfigData()
|
||||
const { refreshClashConfig } = useAppRefreshers()
|
||||
|
||||
const updateChainConfigData = useCallback((value: string | null) => {
|
||||
dispatchChainConfigData(value)
|
||||
|
||||
+3
-2
@@ -13,11 +13,12 @@ import { ScrollTopButton } from '@/components/layout/scroll-top-button'
|
||||
import { ProviderButton } from '@/components/rule/provider-button'
|
||||
import RuleItem from '@/components/rule/rule-item'
|
||||
import { useVisibility } from '@/hooks/use-visibility'
|
||||
import { useAppData } from '@/providers/app-data-context'
|
||||
import { useAppRefreshers, useRulesData } from '@/providers/app-data-context'
|
||||
|
||||
const RulesPage = () => {
|
||||
const { t } = useTranslation()
|
||||
const { rules = [], refreshRules, refreshRuleProviders } = useAppData()
|
||||
const { rules = [] } = useRulesData()
|
||||
const { refreshRules, refreshRuleProviders } = useAppRefreshers()
|
||||
const [match, setMatch] = useState(() => (_: string) => true)
|
||||
const virtuosoRef = useRef<VirtualListHandle>(null)
|
||||
const [showScrollTop, setShowScrollTop] = useState(false)
|
||||
|
||||
@@ -65,6 +65,10 @@ export interface UptimeContextType {
|
||||
uptime: number
|
||||
}
|
||||
|
||||
export interface CoreDataStatusContextType {
|
||||
isCoreDataPending: boolean
|
||||
}
|
||||
|
||||
export interface RefreshersContextType {
|
||||
refreshProxy: () => Promise<any>
|
||||
refreshClashConfig: () => Promise<any>
|
||||
@@ -82,23 +86,63 @@ export const ClashConfigContext = createContext<ClashConfigContextType | null>(
|
||||
)
|
||||
export const SystemContext = createContext<SystemContextType | null>(null)
|
||||
export const UptimeContext = createContext<UptimeContextType | null>(null)
|
||||
export const CoreDataStatusContext =
|
||||
createContext<CoreDataStatusContextType | null>(null)
|
||||
export const RefreshersContext = createContext<RefreshersContextType | null>(
|
||||
null,
|
||||
)
|
||||
|
||||
const useCtx = <T>(ctx: Context<T | null>): T => {
|
||||
const useCtx = <T>(ctx: Context<T | null>, hookName: string): T => {
|
||||
const v = use(ctx)
|
||||
if (!v) throw new Error('useAppData must be used within AppDataProvider')
|
||||
if (!v) throw new Error(`${hookName} must be used within AppDataProvider`)
|
||||
return v
|
||||
}
|
||||
|
||||
export const useProxiesData = () => {
|
||||
const { proxies, proxyProviders, isProxiesPending } = useCtx(
|
||||
ProxiesContext,
|
||||
'useProxiesData',
|
||||
)
|
||||
|
||||
return {
|
||||
proxies,
|
||||
proxyProviders: proxyProviders as Record<string, ProxyProvider>,
|
||||
isProxiesPending,
|
||||
}
|
||||
}
|
||||
|
||||
export const useRulesData = () => {
|
||||
const { rules, ruleProviders } = useCtx(RulesContext, 'useRulesData')
|
||||
|
||||
return {
|
||||
rules,
|
||||
ruleProviders: ruleProviders as Record<string, RuleProvider>,
|
||||
}
|
||||
}
|
||||
|
||||
export const useClashConfigData = (): ClashConfigContextType =>
|
||||
useCtx(ClashConfigContext, 'useClashConfigData')
|
||||
|
||||
export const useSystemData = (): SystemContextType =>
|
||||
useCtx(SystemContext, 'useSystemData')
|
||||
|
||||
export const useUptimeData = (): UptimeContextType =>
|
||||
useCtx(UptimeContext, 'useUptimeData')
|
||||
|
||||
export const useAppRefreshers = (): RefreshersContextType =>
|
||||
useCtx(RefreshersContext, 'useAppRefreshers')
|
||||
|
||||
export const useCoreDataStatus = (): CoreDataStatusContextType =>
|
||||
useCtx(CoreDataStatusContext, 'useCoreDataStatus')
|
||||
|
||||
export const useAppData = (): AppDataContextType => {
|
||||
const { proxies, proxyProviders, isProxiesPending } = useCtx(ProxiesContext)
|
||||
const { rules, ruleProviders } = useCtx(RulesContext)
|
||||
const { clashConfig, isClashConfigPending } = useCtx(ClashConfigContext)
|
||||
const { sysproxy, runningMode, systemProxyAddress } = useCtx(SystemContext)
|
||||
const { uptime } = useCtx(UptimeContext)
|
||||
const refreshers = useCtx(RefreshersContext)
|
||||
const { proxies, proxyProviders } = useProxiesData()
|
||||
const { rules, ruleProviders } = useRulesData()
|
||||
const { clashConfig } = useClashConfigData()
|
||||
const { sysproxy, runningMode, systemProxyAddress } = useSystemData()
|
||||
const { uptime } = useUptimeData()
|
||||
const { isCoreDataPending } = useCoreDataStatus()
|
||||
const refreshers = useAppRefreshers()
|
||||
|
||||
return {
|
||||
proxies,
|
||||
@@ -110,7 +154,7 @@ export const useAppData = (): AppDataContextType => {
|
||||
proxyProviders: proxyProviders as Record<string, ProxyProvider>,
|
||||
ruleProviders: ruleProviders as Record<string, RuleProvider>,
|
||||
systemProxyAddress,
|
||||
isCoreDataPending: isProxiesPending || isClashConfigPending,
|
||||
isCoreDataPending,
|
||||
...refreshers,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
|
||||
import {
|
||||
ClashConfigContext,
|
||||
CoreDataStatusContext,
|
||||
ProxiesContext,
|
||||
RefreshersContext,
|
||||
RulesContext,
|
||||
@@ -265,6 +266,11 @@ export const AppDataProvider = ({
|
||||
|
||||
const uptimeValue = useMemo(() => ({ uptime: uptimeData || 0 }), [uptimeData])
|
||||
|
||||
const coreDataStatusValue = useMemo(
|
||||
() => ({ isCoreDataPending: isProxiesPending || isClashConfigPending }),
|
||||
[isProxiesPending, isClashConfigPending],
|
||||
)
|
||||
|
||||
const refreshersValue = useMemo(
|
||||
() => ({
|
||||
refreshProxy,
|
||||
@@ -292,9 +298,11 @@ export const AppDataProvider = ({
|
||||
<ClashConfigContext value={clashConfigValue}>
|
||||
<SystemContext value={systemValue}>
|
||||
<UptimeContext value={uptimeValue}>
|
||||
<RefreshersContext value={refreshersValue}>
|
||||
{children}
|
||||
</RefreshersContext>
|
||||
<CoreDataStatusContext value={coreDataStatusValue}>
|
||||
<RefreshersContext value={refreshersValue}>
|
||||
{children}
|
||||
</RefreshersContext>
|
||||
</CoreDataStatusContext>
|
||||
</UptimeContext>
|
||||
</SystemContext>
|
||||
</ClashConfigContext>
|
||||
|
||||
Reference in New Issue
Block a user