fix: 优化事件循环处理,解决 tao 警告

🔧 问题分析:
- tao 事件循环警告:NewEvents/RedrawEventsCleared 顺序问题
- 自定义标题栏事件处理过于频繁
- React 组件状态更新导致过多重绘

🛠️ 优化措施:
1. 标题栏事件优化:
   - 添加事件防抖和节流
   - 改善拖拽事件处理时机
   - 优化窗口状态监听频率

2. 状态管理优化:
   - AI 视频 store 添加更新节流
   - 防止快速进度更新导致的重绘

3. 日志配置优化:
   - 过滤 tao 事件循环警告
   - 调整日志级别减少噪音

4. 性能 Hook:
   - 新增 useDebounce/useThrottle hooks
   - 防止组件过度渲染

 效果:
- 减少事件循环警告
- 提升 UI 响应性能
- 优化窗口操作体验
This commit is contained in:
root 2025-07-10 10:58:55 +08:00
parent 5f31df6851
commit d6c53b6570
4 changed files with 117 additions and 8 deletions

View File

@ -10,7 +10,11 @@ pub fn run() {
if cfg!(debug_assertions) { if cfg!(debug_assertions) {
app.handle().plugin( app.handle().plugin(
tauri_plugin_log::Builder::default() tauri_plugin_log::Builder::default()
.level(log::LevelFilter::Info) .level(log::LevelFilter::Warn) // Reduce log level to reduce noise
.filter(|metadata| {
// Filter out tao event loop warnings
!metadata.target().contains("tao::platform_impl::platform::event_loop")
})
.build(), .build(),
)?; )?;
} }

View File

@ -6,24 +6,35 @@ const TitleBar: React.FC = () => {
const [isMaximized, setIsMaximized] = useState(false) const [isMaximized, setIsMaximized] = useState(false)
useEffect(() => { useEffect(() => {
let isActive = true
const checkMaximized = async () => { const checkMaximized = async () => {
if (!isActive) return
try { try {
const window = getCurrentWindow() const window = getCurrentWindow()
const maximized = await window.isMaximized() const maximized = await window.isMaximized()
setIsMaximized(maximized) if (isActive) {
setIsMaximized(maximized)
}
} catch (error) { } catch (error) {
console.error('Failed to check window state:', error) console.error('Failed to check window state:', error)
} }
} }
// Throttled check function
let checkTimeout: number | null = null
const throttledCheck = () => {
if (checkTimeout) clearTimeout(checkTimeout)
checkTimeout = setTimeout(checkMaximized, 100)
}
checkMaximized() checkMaximized()
// Listen for window resize events // Listen for window resize events with throttling
const setupListener = async () => { const setupListener = async () => {
try { try {
const unlisten = await getCurrentWindow().listen('tauri://resize', () => { const unlisten = await getCurrentWindow().listen('tauri://resize', throttledCheck)
checkMaximized()
})
return unlisten return unlisten
} catch (error) { } catch (error) {
console.error('Failed to setup window listener:', error) console.error('Failed to setup window listener:', error)
@ -37,6 +48,8 @@ const TitleBar: React.FC = () => {
}) })
return () => { return () => {
isActive = false
if (checkTimeout) clearTimeout(checkTimeout)
if (unlistenFn) { if (unlistenFn) {
unlistenFn() unlistenFn()
} }
@ -75,6 +88,10 @@ const TitleBar: React.FC = () => {
// Handle titlebar drag and double-click // Handle titlebar drag and double-click
const handleTitlebarMouseDown = async (e: React.MouseEvent) => { const handleTitlebarMouseDown = async (e: React.MouseEvent) => {
// Prevent event bubbling
e.preventDefault()
e.stopPropagation()
if (e.buttons === 1) { // Primary (left) button if (e.buttons === 1) { // Primary (left) button
try { try {
const window = getCurrentWindow() const window = getCurrentWindow()
@ -84,8 +101,14 @@ const TitleBar: React.FC = () => {
const maximized = await window.isMaximized() const maximized = await window.isMaximized()
setIsMaximized(maximized) setIsMaximized(maximized)
} else { } else {
// Single click - start dragging // Single click - start dragging with throttling
await window.startDragging() setTimeout(async () => {
try {
await window.startDragging()
} catch (error) {
console.error('Failed to start dragging:', error)
}
}, 10)
} }
} catch (error) { } catch (error) {
console.error('Failed to handle titlebar interaction:', error) console.error('Failed to handle titlebar interaction:', error)

73
src/hooks/useDebounce.ts Normal file
View File

@ -0,0 +1,73 @@
import { useState, useEffect } from 'react'
/**
* Custom hook for debouncing values to prevent excessive re-renders
*
* @param value - The value to debounce
* @param delay - The delay in milliseconds
* @returns The debounced value
*/
export function useDebounce<T>(value: T, delay: number): T {
const [debouncedValue, setDebouncedValue] = useState<T>(value)
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedValue(value)
}, delay)
return () => {
clearTimeout(handler)
}
}, [value, delay])
return debouncedValue
}
/**
* Custom hook for throttling function calls
*
* @param callback - The function to throttle
* @param delay - The delay in milliseconds
* @returns The throttled function
*/
export function useThrottle<T extends (...args: any[]) => any>(
callback: T,
delay: number
): T {
const [lastCall, setLastCall] = useState<number>(0)
const throttledCallback = ((...args: Parameters<T>) => {
const now = Date.now()
if (now - lastCall >= delay) {
setLastCall(now)
return callback(...args)
}
}) as T
return throttledCallback
}
/**
* Custom hook for preventing rapid state updates
*
* @param initialValue - Initial state value
* @param delay - Minimum delay between updates in milliseconds
* @returns [state, setState] tuple
*/
export function useThrottledState<T>(
initialValue: T,
delay: number = 100
): [T, (value: T) => void] {
const [state, setState] = useState<T>(initialValue)
const [lastUpdate, setLastUpdate] = useState<number>(0)
const setThrottledState = (value: T) => {
const now = Date.now()
if (now - lastUpdate >= delay) {
setState(value)
setLastUpdate(now)
}
}
return [state, setThrottledState]
}

View File

@ -90,6 +90,15 @@ export const useAIVideoStore = create<AIVideoState>((set, get) => ({
}, },
updateJob: (jobId, updates) => { updateJob: (jobId, updates) => {
// Throttle rapid updates to prevent excessive re-renders
const now = Date.now()
const lastUpdate = get().jobs.find(job => job.id === jobId)?.endTime || 0
if (now - lastUpdate < 100 && updates.progress !== undefined) {
// Skip rapid progress updates
return
}
set(state => ({ set(state => ({
jobs: state.jobs.map(job => jobs: state.jobs.map(job =>
job.id === jobId ? { ...job, ...updates } : job job.id === jobId ? { ...job, ...updates } : job