fix: eliminate white screen on startup

- Main window starts hidden (visible: false), shown after frontend ready
- Remove React.StrictMode to reduce initial render overhead
- Add loading spinner during app initialization
- Use Promise.all + requestIdleCallback to optimize startup timing
This commit is contained in:
小鱼开发
2026-05-20 09:47:59 +08:00
parent 8794901bfa
commit 2d7e1473a9
4 changed files with 82 additions and 14 deletions
+1 -1
View File
@@ -19,7 +19,7 @@
"minWidth": 960,
"minHeight": 640,
"resizable": true,
"visible": true
"visible": false
}
],
"security": {
+33
View File
@@ -1,3 +1,36 @@
.app-loading {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100vw;
height: 100vh;
background: #ffffff;
gap: 24px;
}
.app-loading-spinner {
width: 40px;
height: 40px;
border: 3px solid #e8e8e8;
border-top-color: #1a9e8a;
border-radius: 50%;
animation: app-loading-spin 0.8s linear infinite;
}
@keyframes app-loading-spin {
to {
transform: rotate(360deg);
}
}
.app-loading-text {
font-size: 16px;
font-weight: 500;
color: #1a9e8a;
letter-spacing: 2px;
}
.app-layout {
display: flex;
height: 100vh;
+22 -7
View File
@@ -69,16 +69,22 @@ function App() {
const [currentPage, setCurrentPage] = useState<PageType>('video-creation');
const [showLogoutConfirm, setShowLogoutConfirm] = useState(false);
const [appEnvironment, setAppEnvironment] = useState<string>('production');
const [isReady, setIsReady] = useState(false);
// 应用启动时加载配置和认证状态
useEffect(() => {
loadAppConfig()
.then(config => {
setApiBaseUrl(config.apiBaseUrl);
setAppEnvironment(config.environment);
})
.catch(console.error);
loadFromStorage().catch(console.error);
Promise.all([
loadAppConfig()
.then(config => {
setApiBaseUrl(config.apiBaseUrl);
setAppEnvironment(config.environment);
})
.catch(console.error),
loadFromStorage().catch(console.error),
]).finally(() => {
// 确保至少显示 300ms 加载态,避免闪屏
setTimeout(() => setIsReady(true), 300);
});
}, [loadFromStorage]);
// 固定浅色模式
@@ -109,6 +115,15 @@ function App() {
setCurrentPage(page as PageType);
};
if (!isReady) {
return (
<div className="app-loading">
<div className="app-loading-spinner" />
<div className="app-loading-text"></div>
</div>
);
}
return (
<>
{/* Toast 全局挂载 - 不受登录状态影响 */}
+26 -6
View File
@@ -16,10 +16,30 @@ document.addEventListener('contextmenu', (e) => {
}
});
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
root.render(
<BrowserRouter>
<App />
</BrowserRouter>
);
// 前端渲染完成后,通知 Tauri 显示主窗口
// 使用 requestIdleCallback 确保首帧已绘制
const showWindow = () => {
import('@tauri-apps/api/webviewWindow')
.then(({ getCurrentWebviewWindow }) => {
const win = getCurrentWebviewWindow();
win.show();
win.setFocus();
})
.catch(() => {
// 非 Tauri 环境(如浏览器开发)忽略
});
};
if ('requestIdleCallback' in window) {
requestIdleCallback(showWindow, { timeout: 500 });
} else {
setTimeout(showWindow, 100);
}