窗口创建 #

一、窗口概述 #

1.1 窗口架构 #

Tauri 应用可以包含多个窗口,每个窗口都是独立的 WebView 实例。

text
┌─────────────────────────────────────────────────────────────┐
│                      Tauri 应用                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐        │
│  │  主窗口     │  │  设置窗口   │  │  关于窗口   │        │
│  │   main      │  │  settings   │  │   about     │        │
│  │             │  │             │  │             │        │
│  │  WebView    │  │  WebView    │  │  WebView    │        │
│  └─────────────┘  └─────────────┘  └─────────────┘        │
│                                                             │
└─────────────────────────────────────────────────────────────┘

1.2 窗口类型 #

类型 说明 使用场景
主窗口 应用启动时创建 主要界面
子窗口 动态创建 设置、详情页
模态窗口 阻塞父窗口 确认对话框
无边框窗口 无标题栏 自定义标题栏

二、配置创建窗口 #

2.1 在配置文件中定义 #

json
// tauri.conf.json
{
    "app": {
        "windows": [
            {
                "title": "My App",
                "label": "main",
                "width": 800,
                "height": 600,
                "resizable": true,
                "fullscreen": false,
                "center": true
            },
            {
                "title": "Settings",
                "label": "settings",
                "width": 500,
                "height": 400,
                "resizable": false,
                "visible": false
            }
        ]
    }
}

2.2 窗口配置选项 #

json
{
    "label": "main",
    "title": "Window Title",
    "url": "index.html",
    "width": 800,
    "height": 600,
    "minWidth": 400,
    "minHeight": 300,
    "maxWidth": 1200,
    "maxHeight": 900,
    "x": 100,
    "y": 100,
    "center": true,
    "resizable": true,
    "movable": true,
    "minimizable": true,
    "maximizable": true,
    "closable": true,
    "alwaysOnTop": false,
    "fullscreen": false,
    "fullscreenable": true,
    "decorations": true,
    "transparent": false,
    "visible": true,
    "skipTaskbar": false,
    "theme": "system",
    "titleBarStyle": "visible",
    "hiddenTitle": false,
    "acceptFirstMouse": false,
    "tabbingIdentifier": null
}

三、动态创建窗口 #

3.1 后端创建窗口 #

rust
use tauri::WebviewWindowBuilder;

#[tauri::command]
async fn create_settings_window(app: tauri::AppHandle) -> Result<(), String> {
    let _window = WebviewWindowBuilder::new(
        &app,
        "settings",
        tauri::WebviewUrl::App("settings.html".into())
    )
    .title("Settings")
    .inner_size(500.0, 400.0)
    .resizable(false)
    .build()
    .map_err(|e| e.to_string())?;
    
    Ok(())
}

3.2 前端创建窗口 #

typescript
import { WebviewWindow } from '@tauri-apps/api/webviewWindow';

async function createSettingsWindow() {
    const webview = new WebviewWindow('settings', {
        url: 'settings.html',
        title: 'Settings',
        width: 500,
        height: 400,
        resizable: false,
    });

    // 监听窗口创建完成
    webview.once('tauri://created', () => {
        console.log('Settings window created');
    });

    // 监听创建错误
    webview.once('tauri://error', (e) => {
        console.error('Failed to create window:', e);
    });
}

3.3 带预加载脚本的窗口 #

rust
use tauri::WebviewWindowBuilder;

#[tauri::command]
async fn create_window_with_preload(app: tauri::AppHandle) -> Result<(), String> {
    let _window = WebviewWindowBuilder::new(
        &app,
        "custom",
        tauri::WebviewUrl::App("custom.html".into())
    )
    .title("Custom Window")
    .inner_size(600.0, 400.0)
    .initialization_script(r#"
        console.log('Window initialized');
        window.customData = { initialized: true };
    "#)
    .build()
    .map_err(|e| e.to_string())?;
    
    Ok(())
}

四、窗口属性设置 #

4.1 设置标题 #

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

const window = getCurrentWindow();

// 设置标题
await window.setTitle('New Title');

4.2 设置大小 #

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

const window = getCurrentWindow();

// 设置大小
await window.setSize(new LogicalSize(800, 600));

// 设置最小大小
await window.setMinSize(new LogicalSize(400, 300));

// 设置最大大小
await window.setMaxSize(new LogicalSize(1200, 900));

4.3 设置位置 #

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

const window = getCurrentWindow();

// 设置位置
await window.setPosition(new LogicalPosition(100, 100));

// 居中窗口
await window.center();

4.4 设置状态 #

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

const window = getCurrentWindow();

// 最小化
await window.minimize();

// 最大化
await window.maximize();

// 取消最大化
await window.unmaximize();

// 全屏
await window.setFullscreen(true);

// 置顶
await window.setAlwaysOnTop(true);

// 显示/隐藏
await window.show();
await window.hide();

// 聚焦
await window.setFocus();

五、窗口样式 #

5.1 无边框窗口 #

typescript
import { WebviewWindow } from '@tauri-apps/api/webviewWindow';

const window = new WebviewWindow('frameless', {
    url: 'frameless.html',
    title: 'Frameless Window',
    width: 800,
    height: 600,
    decorations: false,
    transparent: true,
});

5.2 透明窗口 #

typescript
const window = new WebviewWindow('transparent', {
    url: 'transparent.html',
    title: 'Transparent Window',
    width: 400,
    height: 300,
    transparent: true,
    decorations: false,
});
css
/* 透明窗口样式 */
body {
    background: transparent;
}

/* 半透明效果 */
.container {
    background: rgba(255, 255, 255, 0.8);
    backdrop-filter: blur(10px);
}

5.3 自定义标题栏 #

typescript
const window = new WebviewWindow('custom-titlebar', {
    url: 'custom-titlebar.html',
    title: 'Custom Titlebar',
    width: 800,
    height: 600,
    decorations: false,
    data: {
        // macOS 特有配置
        titleBarStyle: 'overlay',
        hiddenTitle: true,
        trafficLightPosition: { x: 10, y: 10 },
    }
});
html
<!-- 自定义标题栏 HTML -->
<div class="titlebar" data-tauri-drag-region>
    <span class="title">My App</span>
    <div class="window-controls">
        <button id="minimize">─</button>
        <button id="maximize">□</button>
        <button id="close">×</button>
    </div>
</div>
typescript
// 窗口控制按钮
import { getCurrentWindow } from '@tauri-apps/api/window';

const window = getCurrentWindow();

document.getElementById('minimize')?.addEventListener('click', () => {
    window.minimize();
});

document.getElementById('maximize')?.addEventListener('click', () => {
    window.toggleMaximize();
});

document.getElementById('close')?.addEventListener('click', () => {
    window.close();
});

六、窗口生命周期 #

6.1 监听窗口事件 #

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

const window = getCurrentWindow();

// 窗口关闭请求
await window.onCloseRequested(async (event) => {
    const confirmed = await confirm('确定要关闭吗?');
    if (!confirmed) {
        event.preventDefault();
    }
});

// 窗口获得焦点
await window.onFocusChanged(({ payload: focused }) => {
    console.log('Focus changed:', focused);
});

// 窗口大小改变
await window.onResized(({ payload: size }) => {
    console.log('Resized:', size);
});

// 窗口移动
await window.onMoved(({ payload: position }) => {
    console.log('Moved:', position);
});

// 窗口最大化状态改变
await window.onMaximized(({ payload: maximized }) => {
    console.log('Maximized:', maximized);
});

// 窗口最小化状态改变
await window.onMinimized(({ payload: minimized }) => {
    console.log('Minimized:', minimized);
});

6.2 后端窗口事件 #

rust
use tauri::Manager;

tauri::Builder::default()
    .setup(|app| {
        let window = app.get_webview_window("main").unwrap();
        
        // 监听窗口关闭
        window.on_window_event(move |event| {
            match event {
                tauri::WindowEvent::CloseRequested { .. } => {
                    println!("Window close requested");
                }
                tauri::WindowEvent::Destroyed => {
                    println!("Window destroyed");
                }
                tauri::WindowEvent::Focused(focused) => {
                    println!("Window focused: {}", focused);
                }
                _ => {}
            }
        });
        
        Ok(())
    })
    .run(tauri::generate_context!())
    .expect("error while running tauri application");

七、窗口数据传递 #

7.1 创建时传递数据 #

typescript
import { WebviewWindow } from '@tauri-apps/api/webviewWindow';

const window = new WebviewWindow('detail', {
    url: 'detail.html',
    title: 'Detail',
    width: 600,
    height: 400,
});

// 发送数据到新窗口
window.once('tauri://created', async () => {
    await window.emit('init-data', {
        id: 123,
        name: 'Item Name',
    });
});
typescript
// 在新窗口中接收数据
import { listen } from '@tauri-apps/api/event';

await listen('init-data', (event) => {
    const data = event.payload;
    console.log('Received init data:', data);
});

7.2 通过 URL 参数传递 #

typescript
const params = new URLSearchParams({
    id: '123',
    name: 'Item Name',
});

const window = new WebviewWindow('detail', {
    url: `detail.html?${params.toString()}`,
    title: 'Detail',
    width: 600,
    height: 400,
});
typescript
// 在新窗口中解析参数
const params = new URLSearchParams(window.location.search);
const id = params.get('id');
const name = params.get('name');

八、窗口状态 #

8.1 获取窗口状态 #

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

const window = getCurrentWindow();

// 获取标题
const title = await window.title();

// 获取大小
const size = await window.innerSize();
console.log('Size:', size.width, size.height);

// 获取位置
const position = await window.outerPosition();
console.log('Position:', position.x, position.y);

// 获取状态
const isMaximized = await window.isMaximized();
const isMinimized = await window.isMinimized();
const isFullscreen = await window.isFullscreen();
const isFocused = await window.isFocused();
const isVisible = await window.isVisible();
const isDecorated = await window.isDecorated();
const isResizable = await window.isResizable();

8.2 监控窗口状态 #

typescript
import { useEffect, useState } from 'react';
import { getCurrentWindow } from '@tauri-apps/api/window';

export function useWindowState() {
    const window = getCurrentWindow();
    const [isMaximized, setIsMaximized] = useState(false);

    useEffect(() => {
        window.isMaximized().then(setIsMaximized);

        const unlisten = window.onMaximized(({ payload }) => {
            setIsMaximized(payload);
        });

        return () => {
            unlisten.then((fn) => fn());
        };
    }, []);

    return isMaximized;
}

九、最佳实践 #

9.1 窗口管理器 #

typescript
// WindowManager.ts
import { WebviewWindow } from '@tauri-apps/api/webviewWindow';

class WindowManager {
    private windows: Map<string, WebviewWindow> = new Map();

    async create(label: string, options: WindowOptions) {
        if (this.windows.has(label)) {
            const existing = this.windows.get(label)!;
            await existing.setFocus();
            return existing;
        }

        const window = new WebviewWindow(label, options);
        this.windows.set(label, window);

        window.once('tauri://destroyed', () => {
            this.windows.delete(label);
        });

        return window;
    }

    async close(label: string) {
        const window = this.windows.get(label);
        if (window) {
            await window.close();
            this.windows.delete(label);
        }
    }

    async closeAll() {
        for (const [label] of this.windows) {
            await this.close(label);
        }
    }
}

export const windowManager = new WindowManager();

9.2 单例窗口 #

typescript
async function openSettingsWindow() {
    const existing = await WebviewWindow.getByLabel('settings');
    
    if (existing) {
        await existing.setFocus();
        return existing;
    }

    return new WebviewWindow('settings', {
        url: 'settings.html',
        title: 'Settings',
        width: 500,
        height: 400,
    });
}

十、调试技巧 #

10.1 打开开发者工具 #

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

const window = getCurrentWindow();

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

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

// 判断是否打开
const isOpen = await window.isDevToolsOpen();

10.2 窗口信息日志 #

typescript
async function logWindowInfo() {
    const window = getCurrentWindow();
    
    console.log('Window Info:', {
        label: window.label,
        title: await window.title(),
        size: await window.innerSize(),
        position: await window.outerPosition(),
        isMaximized: await window.isMaximized(),
        isMinimized: await window.isMinimized(),
        isFullscreen: await window.isFullscreen(),
    });
}

十一、总结 #

11.1 核心要点 #

要点 说明
配置创建 在 tauri.conf.json 中定义
动态创建 使用 WebviewWindowBuilder
窗口属性 通过 API 设置和获取
生命周期 监听各种窗口事件
数据传递 通过事件或 URL 参数

11.2 下一步 #

现在你已经掌握了窗口创建,接下来让我们学习 窗口配置,深入了解窗口的各种配置选项!

最后更新:2026-03-28