From 449597f9418be635296da1f864e63364bafd8ab9 Mon Sep 17 00:00:00 2001 From: pompurin404 Date: Sun, 1 Sep 2024 16:00:31 +0800 Subject: [PATCH] optimized rule page performance --- changelog.md | 8 +--- src/main/core/factory.ts | 13 +++-- src/main/core/manager.ts | 1 + src/main/core/mihomoApi.ts | 7 ++- .../src/components/sider/proxy-card.tsx | 5 +- .../src/components/sider/rule-card.tsx | 5 +- src/renderer/src/hooks/use-groups.tsx | 36 ++++++++++++++ src/renderer/src/hooks/use-rules.tsx | 47 +++++++++++++++++++ src/renderer/src/main.tsx | 8 +++- src/renderer/src/pages/proxies.tsx | 6 +-- src/renderer/src/pages/rules.tsx | 5 +- 11 files changed, 115 insertions(+), 26 deletions(-) create mode 100644 src/renderer/src/hooks/use-groups.tsx create mode 100644 src/renderer/src/hooks/use-rules.tsx diff --git a/changelog.md b/changelog.md index c2c0e86..b2469d6 100644 --- a/changelog.md +++ b/changelog.md @@ -2,10 +2,6 @@ - 1.2.x YAML覆写语法有所变动,请更新后参考文档进行修改 -### New Features +### Performance Improvements -- 支持使用外部编辑器打开文件 -- 支持新建订阅/覆写 -- 添加窗口置顶按钮 -- 允许禁用系统标题栏 -- 重写连接页面 +- 优化规则/代理页面性能 diff --git a/src/main/core/factory.ts b/src/main/core/factory.ts index 36519d5..5f1a22d 100644 --- a/src/main/core/factory.ts +++ b/src/main/core/factory.ts @@ -8,17 +8,22 @@ import { } from '../config' import { mihomoWorkConfigPath, overridePath } from '../utils/dirs' import yaml from 'yaml' -import { readFile, writeFile } from 'fs/promises' +import { writeFile } from 'fs/promises' import { deepMerge } from '../utils/merge' import vm from 'vm' import { writeFileSync } from 'fs' +let runtimeConfigStr: string +let runtimeConfig: IMihomoConfig + export async function generateProfile(): Promise { const { current } = await getProfileConfig() const currentProfile = await overrideProfile(current, await getProfile(current)) const controledMihomoConfig = await getControledMihomoConfig() const profile = deepMerge(currentProfile, controledMihomoConfig) - await writeFile(mihomoWorkConfigPath(), yaml.stringify(profile)) + runtimeConfig = profile + runtimeConfigStr = yaml.stringify(profile) + await writeFile(mihomoWorkConfigPath(), runtimeConfigStr) } async function overrideProfile( @@ -87,9 +92,9 @@ function runOverrideScript( } export async function getRuntimeConfigStr(): Promise { - return await readFile(mihomoWorkConfigPath(), 'utf8') + return runtimeConfigStr } export async function getRuntimeConfig(): Promise { - return yaml.parse(await getRuntimeConfigStr()) + return runtimeConfig } diff --git a/src/main/core/manager.ts b/src/main/core/manager.ts index 3beedf7..afb5faa 100644 --- a/src/main/core/manager.ts +++ b/src/main/core/manager.ts @@ -60,6 +60,7 @@ export async function startCore(): Promise { } if (data.toString().includes('RESTful API listening at')) { await startMihomoTraffic() + mainWindow?.webContents.send('coreRestart') retry = 10 resolve() } diff --git a/src/main/core/mihomoApi.ts b/src/main/core/mihomoApi.ts index f8ec32d..ef965de 100644 --- a/src/main/core/mihomoApi.ts +++ b/src/main/core/mihomoApi.ts @@ -71,8 +71,11 @@ export const mihomoRules = async (): Promise => { export const mihomoProxies = async (): Promise => { const instance = await getAxios() - - return await instance.get('/proxies') + const proxies = (await instance.get('/proxies')) as IMihomoProxies + if (!proxies.proxies['GLOBAL']) { + throw new Error('GLOBAL proxy not found') + } + return proxies } export const mihomoGroups = async (): Promise => { diff --git a/src/renderer/src/components/sider/proxy-card.tsx b/src/renderer/src/components/sider/proxy-card.tsx index 3357ada..b92bfca 100644 --- a/src/renderer/src/components/sider/proxy-card.tsx +++ b/src/renderer/src/components/sider/proxy-card.tsx @@ -3,14 +3,13 @@ import { useSortable } from '@dnd-kit/sortable' import { CSS } from '@dnd-kit/utilities' import { LuGroup } from 'react-icons/lu' import { useLocation, useNavigate } from 'react-router-dom' -import useSWR from 'swr' -import { mihomoGroups } from '@renderer/utils/ipc' +import { useGroups } from '@renderer/hooks/use-groups' const ProxyCard: React.FC = () => { const navigate = useNavigate() const location = useLocation() const match = location.pathname.includes('/proxies') - const { data: groups = [] } = useSWR('mihomoGroups', mihomoGroups) + const { groups = [] } = useGroups() const { attributes, listeners, diff --git a/src/renderer/src/components/sider/rule-card.tsx b/src/renderer/src/components/sider/rule-card.tsx index 4079b80..b875cdd 100644 --- a/src/renderer/src/components/sider/rule-card.tsx +++ b/src/renderer/src/components/sider/rule-card.tsx @@ -1,16 +1,15 @@ import { Button, Card, CardBody, CardFooter, Chip } from '@nextui-org/react' -import { mihomoRules } from '@renderer/utils/ipc' import { MdOutlineAltRoute } from 'react-icons/md' import { useLocation, useNavigate } from 'react-router-dom' import { useSortable } from '@dnd-kit/sortable' import { CSS } from '@dnd-kit/utilities' -import useSWR from 'swr' +import { useRules } from '@renderer/hooks/use-rules' const RuleCard: React.FC = () => { const navigate = useNavigate() const location = useLocation() const match = location.pathname.includes('/rules') - const { data: rules } = useSWR('mihomoRules', mihomoRules) + const { rules } = useRules() const { attributes, listeners, diff --git a/src/renderer/src/hooks/use-groups.tsx b/src/renderer/src/hooks/use-groups.tsx new file mode 100644 index 0000000..90990c0 --- /dev/null +++ b/src/renderer/src/hooks/use-groups.tsx @@ -0,0 +1,36 @@ +import React, { createContext, useContext, ReactNode } from 'react' +import useSWR from 'swr' +import { mihomoGroups } from '@renderer/utils/ipc' + +interface GroupsContextType { + groups: IMihomoMixedGroup[] | undefined + mutate: () => void +} + +const GroupsContext = createContext(undefined) + +export const GroupsProvider: React.FC<{ children: ReactNode }> = ({ children }) => { + const { data: groups, mutate } = useSWR('mihomoGroups', mihomoGroups, { + errorRetryInterval: 200, + errorRetryCount: 10 + }) + + React.useEffect(() => { + window.electron.ipcRenderer.on('coreRestart', () => { + mutate() + }) + return (): void => { + window.electron.ipcRenderer.removeAllListeners('coreRestart') + } + }, []) + + return {children} +} + +export const useGroups = (): GroupsContextType => { + const context = useContext(GroupsContext) + if (context === undefined) { + throw new Error('useGroups must be used within an GroupsProvider') + } + return context +} diff --git a/src/renderer/src/hooks/use-rules.tsx b/src/renderer/src/hooks/use-rules.tsx new file mode 100644 index 0000000..208791e --- /dev/null +++ b/src/renderer/src/hooks/use-rules.tsx @@ -0,0 +1,47 @@ +import React, { createContext, useContext, ReactNode } from 'react' +import useSWR from 'swr' +import { mihomoRules } from '@renderer/utils/ipc' + +interface RulesContextType { + rules: IMihomoRulesInfo | undefined + mutate: () => void +} + +const RulesContext = createContext(undefined) +let emptyRetry = 10 + +export const RulesProvider: React.FC<{ children: ReactNode }> = ({ children }) => { + const { data: rules, mutate } = useSWR('mihomoRules', mihomoRules, { + errorRetryInterval: 200, + errorRetryCount: 10, + onSuccess: (data) => { + if (data.rules.length === 0 && emptyRetry) { + emptyRetry-- + setTimeout(() => { + mutate() + }, 200) + } else { + emptyRetry = 10 + } + } + }) + + React.useEffect(() => { + window.electron.ipcRenderer.on('coreRestart', () => { + mutate() + }) + return (): void => { + window.electron.ipcRenderer.removeAllListeners('coreRestart') + } + }, []) + + return {children} +} + +export const useRules = (): RulesContextType => { + const context = useContext(RulesContext) + if (context === undefined) { + throw new Error('useRules must be used within an RulesProvider') + } + return context +} diff --git a/src/renderer/src/main.tsx b/src/renderer/src/main.tsx index a34291e..0d67dd6 100644 --- a/src/renderer/src/main.tsx +++ b/src/renderer/src/main.tsx @@ -12,6 +12,8 @@ import { AppConfigProvider } from './hooks/use-app-config' import { ControledMihomoConfigProvider } from './hooks/use-controled-mihomo-config' import { OverrideConfigProvider } from './hooks/use-override-config' import { ProfileConfigProvider } from './hooks/use-profile-config' +import { RulesProvider } from './hooks/use-rules' +import { GroupsProvider } from './hooks/use-groups' init().then(() => { document.addEventListener('keydown', (e) => { @@ -53,7 +55,11 @@ init().then(() => { - + + + + + diff --git a/src/renderer/src/pages/proxies.tsx b/src/renderer/src/pages/proxies.tsx index dd54fbf..756c52c 100644 --- a/src/renderer/src/pages/proxies.tsx +++ b/src/renderer/src/pages/proxies.tsx @@ -5,7 +5,6 @@ import { mihomoChangeProxy, mihomoCloseAllConnections, mihomoGroupDelay, - mihomoGroups, mihomoProxyDelay } from '@renderer/utils/ipc' import { CgDetailsLess, CgDetailsMore } from 'react-icons/cg' @@ -14,14 +13,14 @@ import { TbCircleLetterD } from 'react-icons/tb' import { FaLocationCrosshairs } from 'react-icons/fa6' import { RxLetterCaseCapitalize } from 'react-icons/rx' import { useEffect, useMemo, useRef, useState } from 'react' -import useSWR from 'swr' import { GroupedVirtuoso, GroupedVirtuosoHandle } from 'react-virtuoso' import ProxyItem from '@renderer/components/proxies/proxy-item' import { IoIosArrowBack } from 'react-icons/io' import { MdOutlineSpeed } from 'react-icons/md' +import { useGroups } from '@renderer/hooks/use-groups' const Proxies: React.FC = () => { - const { data: groups = [], mutate } = useSWR('mihomoGroups', mihomoGroups) + const { groups = [], mutate } = useGroups() const { appConfig, patchAppConfig } = useAppConfig() const { proxyDisplayMode = 'simple', @@ -190,7 +189,6 @@ const Proxies: React.FC = () => { canvas.height = img.height ctx?.drawImage(img, 0, 0) const data = canvas.toDataURL('image/png') - console.log('set') localStorage.setItem(groups[index].icon, data) } img.src = groups[index].icon diff --git a/src/renderer/src/pages/rules.tsx b/src/renderer/src/pages/rules.tsx index 6a7824a..2c56193 100644 --- a/src/renderer/src/pages/rules.tsx +++ b/src/renderer/src/pages/rules.tsx @@ -3,11 +3,10 @@ import RuleItem from '@renderer/components/rules/rule-item' import { Virtuoso } from 'react-virtuoso' import { useMemo, useState } from 'react' import { Divider, Input } from '@nextui-org/react' -import useSWR from 'swr' -import { mihomoRules } from '@renderer/utils/ipc' +import { useRules } from '@renderer/hooks/use-rules' const Rules: React.FC = () => { - const { data: rules } = useSWR('mihomoRules', mihomoRules) + const { rules } = useRules() const [filter, setFilter] = useState('') const filteredRules = useMemo(() => {