调试技巧 #

一、调试概述 #

1.1 调试工具 #

工具 用途
DevTools 前端调试
println! Rust 日志
log crate 结构化日志
VS Code 断点调试
Chrome DevTools WebView 调试

1.2 调试流程 #

text
┌─────────────────────────────────────────────────────────────┐
│                      调试流程                                │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  问题发现 ──► 问题定位 ──► 问题分析 ──► 问题修复 ──► 验证    │
│      │           │           │           │           │      │
│      ▼           ▼           ▼           ▼           ▼      │
│   日志/报错   断点调试    代码分析    修改代码    测试验证   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

二、前端调试 #

2.1 打开 DevTools #

typescript
import { getCurrentWindow } from '@tauri-apps/api/window';

const window = getCurrentWindow();

// 打开开发者工具
await window.openDevTools();

// 关闭开发者工具
await window.closeDevTools();

// 检查是否打开
const isOpen = await window.isDevToolsOpen();

2.2 快捷键 #

text
Windows/Linux: Ctrl + Shift + I
macOS: Cmd + Option + I

2.3 配置自动打开 #

json
// tauri.conf.json
{
    "build": {
        "devtools": true
    }
}

2.4 Console 调试 #

typescript
// 基本日志
console.log('普通日志');
console.info('信息日志');
console.warn('警告日志');
console.error('错误日志');

// 表格输出
console.table([{ name: 'Alice', age: 25 }, { name: 'Bob', age: 30 }]);

// 分组输出
console.group('用户信息');
console.log('姓名: Alice');
console.log('年龄: 25');
console.groupEnd();

// 计时
console.time('操作');
await heavyOperation();
console.timeEnd('操作');

// 堆栈跟踪
console.trace('跟踪调用');

2.5 断点调试 #

typescript
// 使用 debugger 语句
function process(data: unknown) {
    debugger; // 执行会在此暂停
    return transform(data);
}

三、后端调试 #

3.1 println! 调试 #

rust
#[tauri::command]
fn my_command(param: String) -> String {
    println!("Received param: {}", param);
    
    let result = process(&param);
    
    println!("Result: {}", result);
    
    result
}

3.2 使用 log crate #

toml
# Cargo.toml
[dependencies]
log = "0.4"
env_logger = "0.10"
rust
use log::{info, debug, error, warn};

fn main() {
    env_logger::init();
    
    info!("Application started");
    debug!("Debug information");
    warn!("Warning message");
    error!("Error occurred");
}

#[tauri::command]
fn logged_operation() -> Result<(), String> {
    info!("Starting operation");
    
    match perform_operation() {
        Ok(_) => {
            info!("Operation completed successfully");
            Ok(())
        }
        Err(e) => {
            error!("Operation failed: {}", e);
            Err(e.to_string())
        }
    }
}

3.3 运行时日志级别 #

bash
# 设置日志级别
RUST_LOG=debug pnpm tauri dev

# 特定模块日志
RUST_LOG=my_app=debug,tauri=info pnpm tauri dev

3.4 VS Code 调试 #

json
// .vscode/launch.json
{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "lldb",
            "request": "launch",
            "name": "Debug Tauri App",
            "cargo": {
                "args": [
                    "build",
                    "--manifest-path=./src-tauri/Cargo.toml",
                    "--no-default-features"
                ]
            },
            "preLaunchTask": "ui:dev",
            "executable": "${workspaceFolder}/src-tauri/target/debug/my-app"
        }
    ]
}
json
// .vscode/tasks.json
{
    "version": "2.0.0",
    "tasks": [
        {
            "label": "ui:dev",
            "type": "shell",
            "command": "pnpm",
            "args": ["dev"],
            "isBackground": true,
            "problemMatcher": {
                "owner": "custom",
                "pattern": {
                    "regexp": "^$"
                },
                "background": {
                    "activeOnStart": true,
                    "beginsPattern": "VITE",
                    "endsPattern": "ready in"
                }
            }
        }
    ]
}

四、网络调试 #

4.1 HTTP 请求日志 #

typescript
// 封装 fetch 添加日志
async function loggedFetch(url: string, options?: RequestInit) {
    console.log(`[Request] ${options?.method || 'GET'} ${url}`);
    console.log('[Request Body]', options?.body);
    
    const start = performance.now();
    const response = await fetch(url, options);
    const end = performance.now();
    
    console.log(`[Response] ${response.status} (${(end - start).toFixed(2)}ms)`);
    
    const data = await response.clone().json();
    console.log('[Response Data]', data);
    
    return response;
}

4.2 IPC 调用日志 #

typescript
// 封装 invoke 添加日志
import { invoke as tauriInvoke } from '@tauri-apps/api/core';

async function loggedInvoke<T>(cmd: string, args?: Record<string, unknown>): Promise<T> {
    console.log(`[IPC] Invoking: ${cmd}`, args);
    
    const start = performance.now();
    try {
        const result = await tauriInvoke<T>(cmd, args);
        const end = performance.now();
        
        console.log(`[IPC] Result: ${cmd} (${(end - start).toFixed(2)}ms)`, result);
        return result;
    } catch (error) {
        console.error(`[IPC] Error: ${cmd}`, error);
        throw error;
    }
}

export const invoke = loggedInvoke;

五、错误处理 #

5.1 全局错误捕获 #

typescript
// 捕获未处理的 Promise 拒绝
window.addEventListener('unhandledrejection', (event) => {
    console.error('Unhandled rejection:', event.reason);
    event.preventDefault();
});

// 捕获全局错误
window.addEventListener('error', (event) => {
    console.error('Global error:', event.error);
});

5.2 React 错误边界 #

tsx
import { Component, ReactNode } from 'react';

interface Props {
    children: ReactNode;
}

interface State {
    hasError: boolean;
    error: Error | null;
}

export class ErrorBoundary extends Component<Props, State> {
    constructor(props: Props) {
        super(props);
        this.state = { hasError: false, error: null };
    }

    static getDerivedStateFromError(error: Error) {
        return { hasError: true, error };
    }

    componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
        console.error('Error caught by boundary:', error, errorInfo);
    }

    render() {
        if (this.state.hasError) {
            return (
                <div>
                    <h2>Something went wrong</h2>
                    <p>{this.state.error?.message}</p>
                </div>
            );
        }

        return this.props.children;
    }
}

5.3 Rust 错误处理 #

rust
use thiserror::Error;

#[derive(Debug, Error)]
pub enum AppError {
    #[error("IO error: {0}")]
    Io(#[from] std::io::Error),
    
    #[error("Serialization error: {0}")]
    Serialization(#[from] serde_json::Error),
    
    #[error("Command failed: {0}")]
    CommandFailed(String),
}

impl From<AppError> for String {
    fn from(error: AppError) -> String {
        error.to_string()
    }
}

六、常见问题排查 #

6.1 白屏问题 #

typescript
// 检查控制台错误
console.error('Check for errors');

// 检查资源加载
window.addEventListener('error', (e) => {
    console.error('Resource failed to load:', e.target);
}, true);

// 检查路由
console.log('Current URL:', window.location.href);

6.2 IPC 调用失败 #

rust
// 检查命令是否注册
.invoke_handler(tauri::generate_handler![
    my_command,  // 确保命令已注册
])
typescript
// 检查参数类型
await invoke('my_command', {
    param: 'value'  // 确保参数名称匹配
});

6.3 窗口问题 #

typescript
import { getCurrentWindow } from '@tauri-apps/api/window';

const window = getCurrentWindow();

// 检查窗口状态
console.log('Window label:', window.label);
console.log('Is visible:', await window.isVisible());
console.log('Is focused:', await window.isFocused());

6.4 性能问题 #

typescript
// 使用 Performance API
const start = performance.now();
// ... 操作
const end = performance.now();
console.log(`Time: ${end - start}ms`);

// 使用 memory API
console.log('Memory usage:', (performance as any).memory);

七、调试工具 #

7.1 Tauri DevTools #

bash
# 安装 Tauri DevTools
cargo install tauri-cli
tauri dev --devtools

7.2 React DevTools #

bash
# 安装 React DevTools
pnpm add -D @types/react-devtools

7.3 Redux DevTools #

typescript
import { configureStore } from '@reduxjs/toolkit';

const store = configureStore({
    reducer: rootReducer,
    devTools: process.env.NODE_ENV !== 'production',
});

八、最佳实践 #

8.1 日志规范 #

typescript
// 使用统一的日志前缀
const LOG_PREFIX = '[MyApp]';

function log(message: string, ...args: unknown[]) {
    console.log(`${LOG_PREFIX} ${message}`, ...args);
}

function error(message: string, ...args: unknown[]) {
    console.error(`${LOG_PREFIX} ${message}`, ...args);
}

8.2 调试配置 #

typescript
// 根据环境启用调试
const DEBUG = import.meta.env.DEV;

function debug(...args: unknown[]) {
    if (DEBUG) {
        console.log('[Debug]', ...args);
    }
}

8.3 错误报告 #

typescript
async function reportError(error: Error, context?: Record<string, unknown>) {
    if (import.meta.env.PROD) {
        // 发送到错误追踪服务
        await fetch('/api/errors', {
            method: 'POST',
            body: JSON.stringify({
                message: error.message,
                stack: error.stack,
                context,
            }),
        });
    } else {
        console.error('Error:', error, context);
    }
}

九、总结 #

9.1 核心要点 #

要点 说明
前端调试 DevTools、console
后端调试 println!、log crate
VS Code 断点调试
错误处理 全局捕获、错误边界
问题排查 系统化排查流程

9.2 下一步 #

现在你已经掌握了调试技巧,接下来让我们学习 完整项目实战,通过实际项目巩固所学知识!

最后更新:2026-03-28