调试技巧 #
一、调试概述 #
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(¶m);
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