From dd505e4d584df4cd03b9844331f2349c9c21b7d7 Mon Sep 17 00:00:00 2001 From: wonfen Date: Sun, 15 Jun 2025 19:35:12 +0800 Subject: [PATCH] perf: optimize layout initialization and enhance backend notification mechanism --- src-tauri/src/feat/window.rs | 84 +++--------- src-tauri/src/module/lightweight.rs | 44 +++--- src-tauri/src/utils/resolve.rs | 107 +++++++++------ src/pages/_layout.tsx | 199 ++++++++++++++++++---------- 4 files changed, 236 insertions(+), 198 deletions(-) diff --git a/src-tauri/src/feat/window.rs b/src-tauri/src/feat/window.rs index cd3d95a2..40f3c4ca 100644 --- a/src-tauri/src/feat/window.rs +++ b/src-tauri/src/feat/window.rs @@ -188,78 +188,30 @@ async fn clean_async() -> bool { } pub fn clean() -> bool { - use tokio::time::{timeout, Duration}; + use crate::process::AsyncHandler; - // 使用异步处理 - let rt = tokio::runtime::Runtime::new().unwrap(); - let cleanup_result = rt.block_on(async { + let (tx, rx) = std::sync::mpsc::channel(); + + AsyncHandler::spawn(move || async move { log::info!(target: "app", "开始执行清理操作..."); - // 1. 处理TUN模式 - 并行执行,减少等待时间 - let tun_task = async { - if Config::verge().data().enable_tun_mode.unwrap_or(false) { - let disable_tun = serde_json::json!({ - "tun": { - "enable": false - } - }); - timeout( - Duration::from_secs(2), - MihomoManager::global().patch_configs(disable_tun), - ) - .await - .is_ok() - } else { - true - } - }; + // 使用已有的异步清理函数 + let cleanup_result = clean_async().await; - // 2. 系统代理重置 - let proxy_task = async { - timeout( - Duration::from_secs(2), - sysopt::Sysopt::global().reset_sysproxy(), - ) - .await - .is_ok() - }; - - // 3. 核心服务停止 - let core_task = async { - timeout(Duration::from_secs(2), CoreManager::global().stop_core()) - .await - .is_ok() - }; - - // 4. DNS恢复(仅macOS) - #[cfg(target_os = "macos")] - let dns_task = async { - timeout(Duration::from_millis(800), resolve::restore_public_dns()) - .await - .is_ok() - }; - - // 并行执行所有清理任务,提高效率 - let (tun_success, proxy_success, core_success) = - tokio::join!(tun_task, proxy_task, core_task); - - #[cfg(target_os = "macos")] - let dns_success = dns_task.await; - #[cfg(not(target_os = "macos"))] - let dns_success = true; - - let all_success = tun_success && proxy_success && core_success && dns_success; - - log::info!( - target: "app", - "清理操作完成 - TUN: {}, 代理: {}, 核心: {}, DNS: {}, 总体: {}", - tun_success, proxy_success, core_success, dns_success, all_success - ); - - all_success + // 发送结果 + let _ = tx.send(cleanup_result); }); - cleanup_result + match rx.recv_timeout(std::time::Duration::from_secs(8)) { + Ok(result) => { + log::info!(target: "app", "清理操作完成,结果: {}", result); + result + } + Err(_) => { + log::warn!(target: "app", "清理操作超时,返回成功状态避免阻塞"); + true + } + } } #[cfg(target_os = "macos")] diff --git a/src-tauri/src/module/lightweight.rs b/src-tauri/src/module/lightweight.rs index cf8b33c5..529d006b 100644 --- a/src-tauri/src/module/lightweight.rs +++ b/src-tauri/src/module/lightweight.rs @@ -172,19 +172,20 @@ fn cancel_window_close_listener() { fn setup_light_weight_timer() -> Result<()> { Timer::global().init()?; - - let mut timer_map = Timer::global().timer_map.write(); - let delay_timer = Timer::global().delay_timer.write(); - let mut timer_count = Timer::global().timer_count.lock(); - - let task_id = *timer_count; - *timer_count += 1; - let once_by_minutes = Config::verge() .latest() .auto_light_weight_minutes .unwrap_or(10); + // 获取task_id + let task_id = { + let mut timer_count = Timer::global().timer_count.lock(); + let id = *timer_count; + *timer_count += 1; + id + }; + + // 创建任务 let task = TaskBuilder::default() .set_task_id(task_id) .set_maximum_parallel_runnable_num(1) @@ -195,17 +196,24 @@ fn setup_light_weight_timer() -> Result<()> { }) .context("failed to create timer task")?; - delay_timer - .add_task(task) - .context("failed to add timer task")?; + // 添加任务到定时器 + { + let delay_timer = Timer::global().delay_timer.write(); + delay_timer + .add_task(task) + .context("failed to add timer task")?; + } - let timer_task = crate::core::timer::TimerTask { - task_id, - interval_minutes: once_by_minutes, - last_run: chrono::Local::now().timestamp(), - }; - - timer_map.insert(LIGHT_WEIGHT_TASK_UID.to_string(), timer_task); + // 更新任务映射 + { + let mut timer_map = Timer::global().timer_map.write(); + let timer_task = crate::core::timer::TimerTask { + task_id, + interval_minutes: once_by_minutes, + last_run: chrono::Local::now().timestamp(), + }; + timer_map.insert(LIGHT_WEIGHT_TASK_UID.to_string(), timer_task); + } logging!( info, diff --git a/src-tauri/src/utils/resolve.rs b/src-tauri/src/utils/resolve.rs index 3932a85f..5af3b56a 100644 --- a/src-tauri/src/utils/resolve.rs +++ b/src-tauri/src/utils/resolve.rs @@ -344,49 +344,72 @@ pub fn create_window(is_show: bool) -> bool { .fullscreen(false) .inner_size(DEFAULT_WIDTH as f64, DEFAULT_HEIGHT as f64) .min_inner_size(520.0, 520.0) - .visible(true) // 立即显示窗口,避免用户等待 - .initialization_script(r#" - // 添加非侵入式的加载指示器 - document.addEventListener('DOMContentLoaded', function() { - // 只有在React应用还没有挂载时才显示加载指示器 - if (!document.getElementById('root') || !document.getElementById('root').hasChildNodes()) { - // 创建加载指示器,但不覆盖整个body - const loadingDiv = document.createElement('div'); - loadingDiv.id = 'initial-loading-overlay'; - loadingDiv.innerHTML = ` -
-
-
-
-
正在加载 Clash Verge...
-
- - `; - document.body.appendChild(loadingDiv); - - // 设置定时器,如果React应用在5秒内没有挂载,移除加载指示器 - setTimeout(() => { - const overlay = document.getElementById('initial-loading-overlay'); - if (overlay) { - overlay.remove(); - } - }, 5000); + .visible(true) // 立即显示窗口,避免用户等待 + .initialization_script( + r#" + console.log('[Tauri] 窗口初始化脚本开始执行'); + + function createLoadingOverlay() { + + if (document.getElementById('initial-loading-overlay')) { + console.log('[Tauri] 加载指示器已存在'); + return; } - }); - "#) + + console.log('[Tauri] 创建加载指示器'); + const loadingDiv = document.createElement('div'); + loadingDiv.id = 'initial-loading-overlay'; + loadingDiv.innerHTML = ` +
+
+
+
+
Loading Clash Verge...
+
+ + `; + + if (document.body) { + document.body.appendChild(loadingDiv); + } else { + document.addEventListener('DOMContentLoaded', () => { + if (document.body && !document.getElementById('initial-loading-overlay')) { + document.body.appendChild(loadingDiv); + } + }); + } + } + + createLoadingOverlay(); + + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', createLoadingOverlay); + } else { + createLoadingOverlay(); + } + + console.log('[Tauri] 窗口初始化脚本执行完成'); + "#, + ) .build() { Ok(newly_created_window) => { diff --git a/src/pages/_layout.tsx b/src/pages/_layout.tsx index 57f0730b..eca75fa1 100644 --- a/src/pages/_layout.tsx +++ b/src/pages/_layout.tsx @@ -237,49 +237,120 @@ const Layout = () => { console.log("[Layout] 开始执行初始化代码"); initRef.current = true; - const notifyUiStage = async (stage: string) => { + let isInitialized = false; + let initializationAttempts = 0; + const maxAttempts = 3; + + const notifyBackend = async (action: string, stage?: string) => { try { - console.log(`[Layout] UI加载阶段: ${stage}`); - await invoke("update_ui_stage", { stage }); + if (stage) { + console.log(`[Layout] 通知后端 ${action}: ${stage}`); + await invoke("update_ui_stage", { stage }); + } else { + console.log(`[Layout] 通知后端 ${action}`); + await invoke("notify_ui_ready"); + } } catch (err) { - console.error(`[Layout] 通知UI加载阶段(${stage})失败:`, err); + console.error(`[Layout] 通知失败 ${action}:`, err); } }; - const notifyUiCoreReady = async () => { - try { - console.log("[Layout] 核心组件已加载,通知后端"); - await invoke("update_ui_stage", { stage: "DomReady" }); - } catch (err) { - console.error("[Layout] 通知核心组件加载完成失败:", err); + const removeLoadingOverlay = () => { + const initialOverlay = document.getElementById("initial-loading-overlay"); + if (initialOverlay) { + console.log("[Layout] 移除加载指示器"); + initialOverlay.style.opacity = "0"; + setTimeout(() => { + try { + initialOverlay.remove(); + } catch (e) { + console.log("[Layout] 加载指示器已被移除"); + } + }, 300); } }; - const notifyUiResourcesLoaded = async () => { + const performInitialization = async () => { + if (isInitialized) { + console.log("[Layout] 已经初始化过,跳过"); + return; + } + + initializationAttempts++; + console.log(`[Layout] 开始第 ${initializationAttempts} 次初始化尝试`); + try { - console.log("[Layout] 所有资源已加载,通知后端"); - await invoke("update_ui_stage", { stage: "ResourcesLoaded" }); - } catch (err) { - console.error("[Layout] 通知资源加载完成失败:", err); + removeLoadingOverlay(); + + await notifyBackend("加载阶段", "Loading"); + + await new Promise((resolve) => { + const checkReactMount = () => { + const rootElement = document.getElementById("root"); + if (rootElement && rootElement.children.length > 0) { + console.log("[Layout] React组件已挂载"); + resolve(); + } else { + setTimeout(checkReactMount, 50); + } + }; + + checkReactMount(); + + setTimeout(() => { + console.log("[Layout] React组件挂载检查超时,继续执行"); + resolve(); + }, 2000); + }); + + await notifyBackend("DOM就绪", "DomReady"); + + await new Promise((resolve) => { + requestAnimationFrame(() => resolve()); + }); + + await notifyBackend("资源加载完成", "ResourcesLoaded"); + + await notifyBackend("UI就绪"); + + isInitialized = true; + console.log(`[Layout] 第 ${initializationAttempts} 次初始化完成`); + } catch (error) { + console.error( + `[Layout] 第 ${initializationAttempts} 次初始化失败:`, + error, + ); + + if (initializationAttempts < maxAttempts) { + console.log( + `[Layout] 将在500ms后进行第 ${initializationAttempts + 1} 次重试`, + ); + setTimeout(performInitialization, 500); + } else { + console.error("[Layout] 所有初始化尝试都失败,执行紧急初始化"); + + removeLoadingOverlay(); + try { + await notifyBackend("UI就绪"); + isInitialized = true; + } catch (e) { + console.error("[Layout] 紧急初始化也失败:", e); + } + } } }; - const notifyUiReady = async () => { - try { - console.log("[Layout] UI完全准备就绪,通知后端"); - await invoke("notify_ui_ready"); - } catch (err) { - console.error("[Layout] 通知UI准备就绪失败:", err); - } - }; + let hasEventTriggered = false; - // 监听后端发送的启动完成事件 - const listenStartupCompleted = async () => { + const setupEventListener = async () => { try { console.log("[Layout] 开始监听启动完成事件"); const unlisten = await listen("verge://startup-completed", () => { - console.log("[Layout] 收到启动完成事件,开始通知UI就绪"); - notifyUiReady(); + if (!hasEventTriggered) { + console.log("[Layout] 收到启动完成事件,开始初始化"); + hasEventTriggered = true; + performInitialization(); + } }); return unlisten; } catch (err) { @@ -288,61 +359,45 @@ const Layout = () => { } }; - // 简化的UI初始化流程 - const initializeUI = async () => { + const checkImmediateInitialization = async () => { try { - const initialOverlay = document.getElementById( - "initial-loading-overlay", - ); - if (initialOverlay) { - initialOverlay.style.opacity = "0"; - setTimeout(() => initialOverlay.remove(), 200); - } - - await notifyUiStage("Loading"); - - await new Promise((resolve) => setTimeout(resolve, 100)); - - console.log("[Layout] 通知后端:DomReady"); - await notifyUiCoreReady(); - - await new Promise((resolve) => { - requestAnimationFrame(() => { - requestAnimationFrame(resolve); - }); - }); - - console.log("[Layout] 通知后端:ResourcesLoaded"); - await notifyUiResourcesLoaded(); - - await new Promise((resolve) => setTimeout(resolve, 100)); - - await notifyUiReady(); - } catch (error) { - try { - await notifyUiReady(); - } catch (e) { - console.error("[Layout] 通知UI就绪失败:", e); + console.log("[Layout] 检查后端是否已就绪"); + await invoke("update_ui_stage", { stage: "Loading" }); + + if (!hasEventTriggered && !isInitialized) { + console.log("[Layout] 后端已就绪,立即开始初始化"); + hasEventTriggered = true; + performInitialization(); } + } catch (err) { + console.log("[Layout] 后端尚未就绪,等待启动完成事件"); } }; - setTimeout(initializeUI, 50); + const backupInitialization = setTimeout(() => { + if (!hasEventTriggered && !isInitialized) { + console.warn("[Layout] 备用初始化触发:1.5秒内未开始初始化"); + hasEventTriggered = true; + performInitialization(); + } + }, 1500); - const emergencyTimeout = setTimeout(() => { - const emergencyNotify = async () => { - try { - await invoke("notify_ui_ready"); - } catch (error) {} - }; - emergencyNotify(); + const emergencyInitialization = setTimeout(() => { + if (!isInitialized) { + console.error("[Layout] 紧急初始化触发:5秒内未完成初始化"); + removeLoadingOverlay(); + notifyBackend("UI就绪").catch(() => {}); + isInitialized = true; + } }, 5000); - // 启动监听器 - const unlistenPromise = listenStartupCompleted(); + const unlistenPromise = setupEventListener(); + + setTimeout(checkImmediateInitialization, 100); return () => { - clearTimeout(emergencyTimeout); + clearTimeout(backupInitialization); + clearTimeout(emergencyInitialization); unlistenPromise.then((unlisten) => unlisten()); }; }, []);