Tauri 2.0 系统托盘实战:从零构建后台常驻应用

tauri版本2的系统托盘 通过上述两种方式,你可以在 Tauri 2.0.0-rc 应用程序中实现系统托盘功能。第一种方式适用于简单的托盘图标显示,而第二种方式提供了更多的自定义和交互可能性。根据你的应用需求选择合适的实现方参考资料。 阅读详情

1. 为什么你的桌面应用需要一个“后台常驻”模式?

很多刚接触桌面应用开发的朋友,尤其是从Web前端转过来的,可能会觉得:应用窗口一关,程序不就退出了吗?这其实是我们最熟悉的“Web思维”。但在真实的桌面场景里,用户的需求往往更复杂。想象一下你正在用的音乐播放器、聊天软件或者下载工具,你点击窗口右上角的“X”,是希望它彻底退出,还是希望它安静地缩到系统托盘(就是屏幕右下角或右上角那一小块区域),继续在后台为你播放音乐、接收消息或下载文件呢?

绝大多数情况下,用户期望的是后者。这就是“后台常驻”模式的核心价值:提供无缝的用户体验,让应用在不干扰用户当前工作的前提下,持续提供服务。Tauri 2.0 作为一个优秀的跨平台桌面应用框架,其系统托盘(System Tray)功能,正是实现这一体验的“瑞士军刀”。

我刚开始做桌面应用时,也踩过不少坑。比如用户反馈说“你们的软件怎么一点关闭就没了,我的任务还没完成呢”,或者“找不到重启应用的地方,只能去任务管理器里强杀”。这些问题,本质上都是因为应用的生命周期管理没有贴合用户的真实使用习惯。系统托盘,就是一个绝佳的“中间态”。它让应用从用户的视野中“消失”(隐藏主窗口),但又没有从系统中“消失”(托盘图标常驻),用户可以随时通过点击图标或菜单,让应用“满血复活”。

所以,在Tauri 2.0里折腾系统托盘,绝不是为了炫技,而是为了解决一个非常实际的产品问题:如何优雅地管理应用的“显”与“隐”,实现真正的“点击关闭即最小化到托盘”。接下来,我就手把手带你从零开始,构建一个这样的应用,过程中遇到的坑和解决方案,我都会毫无保留地分享给你。

2. 项目初始化与核心依赖配置

万事开头难,但配置对了开头就成功了一半。我们先从创建一个干净的Tauri 2.0项目开始。这里我假设你已经安装了Node.js、Rust和Tauri CLI。如果还没装,去官方文档走一遍安装流程,几分钟的事儿。

打开终端,创建一个新项目:

npm create tauri-app@latest my-tray-app

创建过程中,前端框架选你熟悉的就行,Vue、React、Svelte、Solid都可以,甚至纯HTML/CSS/JS也没问题,Tauri在这方面很包容。我这里为了演示方便,就选Vanilla(纯原生JS)。项目创建好后,用你喜欢的编辑器打开。

接下来是至关重要的一步:配置 Cargo.toml 文件。 这个文件位于 src-tauri 目录下,是Rust端的依赖清单。我们需要在这里声明启用系统托盘和图片处理功能。

打开 src-tauri/Cargo.toml,找到 [dependencies] 部分,修改 tauri 的依赖项:

[dependencies]
tauri = { version = "2", features = ["tray-icon", "image-png"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"

重点看 features = ["tray-icon", "image-png"] 这一行。

  • tray-icon:这个feature必须开启,它提供了创建和管理系统托盘图标的核心API。没有它,后面所有关于托盘的操作都无法进行。
  • image-png:这个feature是为了支持 Image API,让我们能加载PNG格式的图标文件。你可能会问,我图标用SVG不行吗?在Tauri里,系统托盘图标对跨平台兼容性要求很高,PNG是最稳妥、支持最广的格式。开启这个feature后,我们才能在代码里安全地通过文件路径或二进制数据加载图标,否则在生产环境很容易遇到图标加载失败的坑,这个我们后面会详细说。

保存这个文件后,Tauri的构建工具会自动下载和编译这些开启了新特性的依赖。第一次可能会慢一点,喝杯咖啡等等就好。

3. 前端(JavaScript)实现:从创建托盘到交互闭环

大部分逻辑我们会放在前端(JavaScript/TypeScript)来实现,这样对于前端开发者来说更友好,迭代和调试也更方便。我们一步一步来,把每个环节都吃透。

3.1 创建第一个托盘图标

首先,我们在前端代码里找一个合适的地方初始化托盘。通常我会在应用主组件挂载后,或者在一个专门的模块里处理。新建一个文件叫 tray.js(或者 tray.ts)。

第一步,导入必要的模块并定义托盘配置:

// tray.js
import { TrayIcon } from '@tauri-apps/api/tray';
import { getCurrentWindow } from '@tauri-apps/api/window';
import { Menu } from '@tauri-apps/api/menu';
import { exit } from '@tauri-apps/plugin-process';
// 注意图标的导入方式,后面会详细解释
import iconPath from '../assets/icon_32x32.png';

// 定义托盘的基础配置对象
const trayOptions = {
  // 图标路径(开发环境可用,生产环境会出问题,我们先这么写,后面再优化)
  icon: iconPath,
  // 鼠标悬停在图标上时的提示文字
  tooltip: '我的后台应用',
  // 非常重要!设为false,禁用左键点击直接弹出菜单。
  // 我们希望左键点击是显示/隐藏窗口,而不是弹出菜单。
  menuOnLeftClick: false,
  // 托盘菜单,我们先留空,后面再创建
  menu: undefined,
  // 托盘图标的事件处理函数,所有交互逻辑的核心
  action: (event) => {
    handleTrayEvent(event);
  }
};

这里有几个关键点:

  1. menuOnLeftClick: false:这个配置项新手很容易忽略。如果设为 true(默认值),用户左键点击托盘图标时,会直接弹出托盘菜单。但这不符合大多数用户习惯(比如微信、QQ,左键点击是显示/隐藏主窗口)。所以我们设为 false,把左键点击事件留给我们自己来处理。
  2. action 函数:这是整个托盘交互的“总开关”。图标的点击、双击、鼠标移入移出等所有事件,都会触发这个函数,并通过 event 参数告诉我们具体是什么事件。

3.2 精细化处理托盘事件

光有图标不行,我们得让它能“干活”。action 函数里的 event 对象包含了丰富的信息。我们来完善事件处理:

async function handleTrayEvent(event) {
  switch (event.type) {
    case 'Click':
      // 注意:Click事件会触发两次:鼠标按下(Down)和松开(Up)
      // 我们通常只处理一次,避免重复动作
      if (event.button === 'Left' && event.buttonState === 'Up') {
        console.log('左键单击托盘图标');
        await toggleMainWindow();
      }
      // 你也可以处理右键点击,通常用来显示菜单
      // if (event.button === 'Right' && event.buttonState === 'Up') {
      //   trayOptions.menu?.popup();
      // }
      break;
    case 'DoubleClick':
      console.log('双击托盘图标');
      // 可以定义双击行为,比如直接显示窗口并聚焦
      break;
    case 'Enter':
      // 鼠标移入托盘图标区域,可以更新提示信息等
      break;
    case 'Leave':
      // 鼠标移出
      break;
    default:
      console.log(`未处理的事件类型: ${event.type}`);
  }
}

这里我重点说一下 Click 事件。你可能在测试时会发现,明明只点了一下,日志却打印了两次。这是因为一次完整的点击包含了 buttonState: 'Down'(按下)和 buttonState: 'Up'(松开)两个阶段。我们通常只需要处理一次,比如在 'Up' 时触发动作,这样更符合用户的直觉。我个人的习惯是处理 'Up',因为这意味着用户已经完成了点击动作。

3.3 窗口的显示、隐藏与状态管理

这是实现“后台常驻”的核心逻辑。我们想要的效果是:用户点击窗口关闭按钮时,窗口不是销毁,而是隐藏;点击托盘图标时,如果窗口隐藏了就显示并聚焦,如果已经显示了就尝试聚焦。

首先,我们需要改造窗口的关闭按钮。默认情况下,标题栏的关闭按钮会直接销毁窗口。我们需要在 src-tauri/tauri.conf.json 中配置,使用自定义的标题栏,或者拦截关闭事件。更常见的做法是直接隐藏窗口。

我们先写一个管理窗口状态的函数:

async function toggleMainWindow() {
  const mainWindow = getCurrentWindow(); // 获取当前主窗口实例

  // 检查窗口当前是否可见
  const isVisible = await mainWindow.isVisible();

  if (!isVisible) {
    // 窗口不可见,则显示它
    await mainWindow.show();
    await mainWindow.setFocus(); // 并尝试获取焦点
  } else {
    // 窗口可见,检查是否最小化了
    const isMinimized = await mainWindow.isMinimized();
    if (isMinimized) {
      // 如果是最小化状态,先取消最小化
      await mainWindow.unminimize();
    }
    // 无论之前状态如何,都尝试让窗口聚焦到前台
    // 注意:在Windows上,如果窗口是“最小化”状态,setFocus可能不会将其恢复到前台。
    // 这就是为什么上面要先判断和取消最小化。
    await mainWindow.setFocus();
  }
}

这个函数已经能处理大部分情况了。但这里有个大坑,是我实测中发现的:在 JavaScript API 中,如果窗口处于最小化(minimized)状态,直接调用 setFocus() 是无法将窗口带到前台的!你必须先调用 unminimize() 取消最小化,再调用 setFocus() 才行。而在 Rust 端 API 中,set_focus() 会自动处理最小化状态。这个平台/API差异一定要注意,否则你会遇到“点击托盘图标没反应”的诡异问题。

接下来,我们需要修改窗口关闭按钮的行为。假设你用了自定义的标题栏,HTML结构里有一个关闭按钮:

<button id="close-btn">×</button>

那么它的点击事件应该这样处理:

document.getElementById('close-btn').addEventListener('click', async () => {
  // 不是关闭,而是隐藏窗口
  const mainWindow = getCurrentWindow();
  await mainWindow.hide();
  // 可选:给用户一个提示,比如托盘图标闪烁一下,或者发送一个桌面通知
  console.log('应用已隐藏到系统托盘');
});

这样,用户点击关闭按钮后,窗口“消失”了,但进程还在,托盘图标依然存在。

3.4 构建功能完整的托盘菜单

只有左键点击显示/隐藏窗口还不够专业。一个完整的托盘应用,右键点击图标应该弹出一个功能菜单,比如“显示窗口”、“退出”等。

我们来创建一个菜单:

async function createTrayMenu() {
  const menu = await Menu.new({
    items: [
      {
        id: 'show',
        text: '显示主窗口',
        action: async () => {
          await toggleMainWindow();
        }
      },
      {
        id: 'separator-1', // 分隔符的id可以任意
        text: '-', // 文本是‘-’表示这是一个分隔符
      },
      {
        id: 'quit',
        text: '退出',
        action: async () => {
          // 退出应用
          await exit(0); // 0 表示正常退出
        }
      }
    ]
  });
  return menu;
}

创建好菜单后,我们需要把它赋值给之前定义的 trayOptions.menu,并且在创建托盘实例时使用它。同时,我们也可以利用右键点击事件来弹出这个菜单。修改一下 handleTrayEvent 函数:

async function handleTrayEvent(event) {
  switch (event.type) {
    case 'Click':
      if (event.button === 'Left' && event.buttonState === 'Up') {
        await toggleMainWindow();
      } else if (event.button === 'Right' && event.buttonState === 'Up') {
        // 右键点击,弹出菜单
        // 我们需要能访问到创建好的托盘实例,这里假设我们将其保存在一个变量中
        if (window.trayInstance) {
          window.trayInstance.menu?.popup();
        }
      }
      break;
    // ... 其他事件处理
  }
}

3.5 解决生产环境图标丢失的“幽灵”问题

这是前端方案里最经典的一个坑。在开发时,你用 icon: “icons/32x32.png” 这种相对路径一切正常。但一旦打包成生产环境的安装包,图标就神秘消失了。为什么呢?因为打包过程会重构目录结构,你写的那个相对路径在打包后根本指向了一个不存在的文件。

Tauri的 icon 配置非常灵活,除了字符串路径,它还接受 Uint8Array、ArrayBuffer、number[] 或 Image 对象。这给了我们解决方案:在前端,以模块方式导入图片资源,并将其转换为二进制数据。

具体操作如下:

  1. 移动图标文件:把你准备好的托盘图标(比如 32x32.png)从 src-tauri 目录,放到前端的静态资源目录,比如 src/assets/ 下。
  2. 修改图标引入方式:
// 在前端模块中导入图片
import iconUrl from '../assets/icon_32x32.png';
  1. 编写一个转换函数,在运行时获取这张图片的二进制数据:
async function loadIconAsUint8Array(imageUrl) {
  try {
    // 使用fetch获取图片资源
    const response = await fetch(imageUrl);
    if (!response.ok) {
      throw new Error(`图标加载失败,状态码: ${response.status}`);
    }
    const imageBlob = await response.blob();
    const arrayBuffer = await imageBlob.arrayBuffer();
    // 转换为Tauri API需要的Uint8Array格式
    return new Uint8Array(arrayBuffer);
  } catch (error) {
    console.error('加载托盘图标失败:', error);
    // 可以在这里提供一个备用的内置图标或空白图标,避免应用崩溃
    return new Uint8Array(0);
  }
}
  1. 在创建托盘时动态设置图标: 我们不能在定义 trayOptions 时直接赋值 icon,因为那时异步操作还没完成。正确的做法是在创建托盘的函数里赋值:
export async function setupSystemTray() {
  // 1. 创建菜单
  const trayMenu = await createTrayMenu();
  trayOptions.menu = trayMenu;

  // 2. 加载图标二进制数据
  const iconData = await loadIconAsUint8Array(iconUrl);
  trayOptions.icon = iconData;

  // 3. 创建托盘实例
  const tray = await TrayIcon.new(trayOptions);

  // 4. 将实例保存到全局变量,方便其他地方(如事件处理)访问
  window.trayInstance = tray;

  console.log('系统托盘初始化成功!');
}

最后,在你的应用初始化代码(比如 main.js)中调用 setupSystemTray() 函数。这样,无论开发还是生产环境,图标都能正确加载了。

3.6 权限配置:绕不开的“安全门”

Tauri 2.0 遵循最小权限原则,非常安全,但也意味着很多API默认是不可用的。当你调用 window.hide()、window.setFocus() 等方法时,可能会在控制台看到权限错误。

你需要显式地在Tauri的权限配置文件中声明这些能力。打开 src-tauri/capabilities/default.json,在 permissions 数组里添加以下权限:

{
  "identifier": "default",
  "description": "默认权限",
  "permissions": [
    "core:window:allow-hide",
    "core:window:allow-show",
    "core:window:allow-set-focus",
    "core:window:allow-unminimize",
    "core:window:allow-is-visible",
    "core:window:allow-is-minimized",
    "core:window:allow-close",
    "core:tray:allow-new",
    "core:tray:allow-set-icon",
    "core:tray:allow-set-menu",
    "core:tray:allow-set-tooltip",
    "core:menu:allow-new",
    "core:menu:allow-popup"
  ]
}

这些权限名基本都能望文生义,对应着我们之前用到的各个API。配置好后,重新运行应用,权限错误就应该消失了。

4. Rust后端实现:更底层、更高效的选择

虽然前端方案已经足够强大和方便,但Tauri也允许你在Rust后端处理所有逻辑。这对于追求极致性能、或者希望将核心逻辑与UI彻底分离的开发者来说,是更好的选择。两种方式没有绝对的好坏,只有适合与否。

4.1 Rust端创建托盘的基本结构

在Rust端,我们通常在 src-tauri/src/main.rs(或 lib.rs,取决于项目结构)的 setup 钩子中初始化托盘。代码结构会显得更集中和底层。

首先,确保 Cargo.toml 的依赖已按第二部分配置好。然后在你的主运行函数中:

// 引入必要的模块
use tauri::{
    tray::{TrayIconBuilder, MouseButton, MouseButtonState, TrayIconEvent},
    menu::{Menu, MenuItem},
    Manager,
};

#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
    tauri::Builder::default()
        // ... 其他插件注册
        .setup(|app| {
            // ************ 托盘初始化代码写在这里 ************
            // 1. 创建菜单项
            let show_item = MenuItem::with_id(app, "show", "显示窗口", true, None::<&str>)?;
            let quit_item = MenuItem::with_id(app, "quit", "退出", true, None::<&str>)?;

            // 2. 组装菜单
            let tray_menu = Menu::with_items(app, &[&show_item, &quit_item])?;

            // 3. 构建托盘图标
            let _tray = TrayIconBuilder::new()
                .icon(app.default_window_icon().unwrap().clone()) // 使用应用默认图标
                .tooltip("我的Rust托盘应用".into())
                .menu(&tray_menu)
                .menu_on_left_click(false) // 同样禁用左键菜单
                .on_tray_icon_event(|tray, event| {
                    // 处理托盘图标事件(鼠标点击等)
                    match event {
                        TrayIconEvent::Click {
                            button: MouseButton::Left,
                            button_state: MouseButtonState::Up,
                            ..
                        } => {
                            // 左键单击,显示/聚焦窗口
                            if let Some(window) = tray.app_handle().get_webview_window("main") {
                                let _ = window.show();
                                let _ = window.set_focus();
                            }
                        }
                        _ => {}
                    }
                })
                .on_menu_event(|app_handle, event| {
                    // 处理菜单项点击事件
                    match event.id.as_ref() {
                        "show" => {
                            if let Some(window) = app_handle.get_webview_window("main") {
                                let _ = window.show();
                                let _ = window.set_focus();
                            }
                        }
                        "quit" => {
                            app_handle.exit(0);
                        }
                        _ => {}
                    }
                })
                .build(app)?;
            // ************ 托盘初始化代码结束 ************
            Ok(())
        })
        .run(tauri::generate_context!())
        .expect("启动应用失败");
}

这段代码做了以下几件事:

  1. 创建菜单:使用 MenuItem::with_id 创建菜单项,然后用 Menu::with_items 组装。
  2. 构建托盘:TrayIconBuilder 提供了链式调用的配置方式,非常清晰。
  3. 事件监听:
    • .on_tray_icon_event:监听图标的鼠标事件(点击、双击等)。这里我们匹配左键单击松开事件。
    • .on_menu_event:监听菜单项的点击事件。
  4. 窗口操作:在事件处理器中,通过 app_handle.get_webview_window(“main”) 获取到主窗口的句柄,然后调用 show() 和 set_focus() 方法。注意:在Rust端,set_focus() 的行为更一致,通常不需要像JS端那样先检查最小化状态。

4.2 前端与Rust方案的对比与选型建议

两种方案都能实现相同的功能,但各有优劣。我整理了一个对比表格,帮你快速决策:

特性维度JavaScript/前端方案Rust/后端方案
上手难度低。对于前端开发者,语法熟悉,调试方便(用浏览器开发者工具)。中高。需要基本的Rust知识,编译和错误排查对新手有一定门槛。
开发效率高。热重载(HMR)支持好,修改代码能立刻看到效果。中。修改Rust代码后需要重新编译,速度相对慢。
性能与控制力中。经过WebView桥接,稍有开销,但对托盘这种低频操作足够。高。直接调用系统API,无额外开销,对系统事件的响应更直接。
代码组织灵活。可以将托盘逻辑放在前端任何位置,与UI逻辑结合紧密。集中。所有托盘逻辑集中在setup中,与前端UI解耦,更清晰。
生产环境图标需要额外处理。需将图标转为二进制数据,步骤稍繁琐。相对简单。可使用应用内置资源或指定固定路径,打包时易处理。
多窗口/复杂交互方便。前端状态管理库(Vuex, Pinia, Redux)可直接用于托盘逻辑。较复杂。需要通过Tauri的命令(Command)或事件(Event)系统与前端通信。

我的个人建议是:

  • 如果你是独立开发者或小团队,主要技术栈是前端,强烈建议从 JavaScript方案 开始。它能让你快速搭建出可用的功能,遇到问题也更容易搜索到解决方案(前端社区更庞大)。先把产品核心功能跑起来更重要。
  • 如果你在构建一个对性能、稳定性要求极高的生产级应用,或者团队中有Rust开发者,可以考虑 Rust方案。它将核心逻辑放在更稳定的后端,减少了前端代码崩溃导致托盘功能失效的风险,长期维护性可能更好。
  • 折中方案:你也可以混合使用。例如,用Rust创建托盘和菜单骨架,但将菜单点击事件通过Tauri的事件系统发送到前端,由前端来处理具体的业务逻辑(比如显示某个设置页面)。这既利用了Rust的稳定性,又保持了前端逻辑的灵活性。

5. 实战进阶:让托盘体验更“原生”

基础功能实现后,我们可以追求更极致的用户体验,让应用看起来更像一个“原生”的桌面程序。

1. 托盘图标状态反馈 不要让图标只是一个静态图片。当应用有状态更新时,可以改变图标。例如,一个下载应用可以在下载时显示一个“活动”状态的图标,完成时恢复常态。

// 假设你有两个图标:icon_normal 和 icon_active
import iconNormal from '../assets/icon_normal.png';
import iconActive from '../assets/icon_active.png';

async function setTrayIcon(iconName) {
  const iconUrl = iconName === 'active' ? iconActive : iconNormal;
  const iconData = await loadIconAsUint8Array(iconUrl);
  if (window.trayInstance) {
    await window.trayInstance.setIcon(iconData);
  }
}
// 在下载开始时调用 setTrayIcon('active'),结束时调用 setTrayIcon('normal')

2. 托盘菜单动态更新 菜单内容不是一成不变的。比如一个音乐播放器,可以根据播放状态切换“播放/暂停”菜单项的文本。

async function updatePlayPauseMenuItem(isPlaying) {
  if (window.trayMenuInstance) { // 假设你保存了菜单实例
    const item = await window.trayMenuInstance.get('play-pause');
    if (item) {
      await item.setText(isPlaying ? '暂停' : '播放');
    }
  }
}

3. 处理应用启动与托盘创建时机 确保托盘图标在应用启动后立即创建。通常,在Tauri的 app_handle 就绪后(前端在 onMount 或 app.ts 中)立即调用你的 setupSystemTray() 函数。同时,要做好错误处理,比如图标加载失败时使用一个备用方案,避免应用因托盘创建失败而无法启动。

4. 跨平台差异的细微处理 虽然Tauri尽力抹平了差异,但macOS、Windows和Linux的托盘行为仍有细微差别。例如,在macOS上,点击窗口的关闭按钮(红点),应用默认不会退出,而是常驻在菜单栏(与托盘类似)。这时你可能需要调整逻辑,与系统行为保持一致。多在实际系统上测试是唯一的方法。

走到这一步,你已经拥有了一个功能完整、行为专业的后台常驻桌面应用。它可以从托盘图标快速唤出,也能安静地在后台工作。这个过程里最关键的,不是记住每一行代码,而是理解“后台常驻”这个模式如何通过系统托盘这个载体,以符合用户直觉的方式来实现。剩下的,就是根据你具体的应用场景,去填充托盘菜单里的功能,比如“新建任务”、“打开日志”、“检查更新”等等。

Tauri 2.0 系统托盘实战:从JavaScript到Rust,手把手教你打造后台常驻应用(附完整代码) 本文详细介绍了Tauri 2.0系统托盘的开发实践,涵盖JavaScript和Rust两种实现方案。通过基础配置、窗口状态管理、托盘菜单实现等核心内容,帮助开发者快速掌握后台常驻应用的开发技巧,并提供生产环境中的优化建议和跨平台兼容性处理方案。 阅读详情

相关推荐

Tauri 2.0系统托盘进阶:动态菜单与事件交互实战

本文深入探讨了Tauri 2.0系统托盘的进阶功能,重点讲解了如何实现动态菜单与复杂的事件交互。通过实战代码示例,详细展示了如何利用Rust代码创建可响应应用状态变化的智能托盘菜单,并处理左键、双击等多种交互事件,从而将静态托盘图标升级为功能强大的桌面应用助手。

weixin_29021291的博客 579

Tauri 系统托盘图标

你的项目/src-tauri/src/main.rs。

豪先生的博客 1167

Tauri教程-实战篇-第三节 定义系统托盘、日志

本章节我们将学习如何为我们的应用添加系统托盘功能和服务日志。

追逐自己的目标,感受生活的乐趣。Elcker 1892

Tauri教程-进阶篇-第七节 系统托盘

本章节我们将了解到如何在Tauri项目中创建系统托盘。使得我们可以通过提供对常用操作的快速访问来增强用户体验。对于没有接受过C端开发的人员来说,托盘这个概念可能有些模糊,所谓托盘就是在我们系统右下角展示的应用图标,你可以通过它来快速的显示/隐藏应用或者对应用进行一些快捷操作。

追逐自己的目标,感受生活的乐趣。Elcker 1819

使用Tauri + Leptos开发带系统托盘桌面应用

初学Rust编程,尝试使用Tauri + Leptos开发带系统托盘的桌面小程序。Win10操作系统,使用VS Code及rust analyzer插件搭建的开发环境。

weixin_44274609的博客 2040

Tauri 2.0.0-rc系统托盘实战:5分钟搞定基础配置与Rust动态菜单

本文详细介绍了Tauri 2.0框架中系统托盘功能的开发实践,从基础配置到Rust动态菜单实现。通过具体代码示例,展示了如何创建静态托盘、实现动态交互以及处理跨平台兼容性问题,帮助开发者快速掌握Tauri系统托盘开发技巧。

weixin_28025327的博客 419

Tauri 应用篇 - 系统托盘

在 Linux 上,则需要安装软件包对其进行支持,从官方文档了解 Linux 设置[2]。是一个布尔值,用于确定图像是否表示 macOS 上的模板图像[1](为。方法,用于返回系统托盘的句柄,可以用来更新托盘图标和上下文菜单项。注意:只创建菜单项,而不添加事件处理,点击菜单依然会无响应。注意:点击托盘图标无响应,是因为并未对其添加菜单和事件。- 使用给定的标题和菜单项创建一个新的子菜单。注意:虽然应用菜单和托盘菜单都是菜单,但除。- 原生菜单项,目前仅支持菜单分割符。- 将自定义菜单项添加到系统托盘菜单。

豪先生的博客 2492

Tauri 2.0.0-rc系统托盘实战:从配置到交互菜单的完整指南

本文提供了Tauri 2.0系统托盘功能的完整实战指南,涵盖从基础配置到动态交互菜单的构建。详细解析了通过Rust代码创建托盘图标、响应事件、管理复杂菜单结构以及处理多平台差异等核心技巧,帮助开发者为其桌面应用实现专业级的后台驻留与快速访问功能。

weixin_29021757的博客 243

tauri2.x+vue3实践篇|封装多窗口|tauri2.0自定义托盘闪烁消息提示+右键菜单

基于tauri2.x跨平台框架整合vite5搭建桌面端多窗口应用、自定义系统托盘图标闪烁提示、托盘右键菜单。

码农ICoding 2976

Tauri 2.0 系统托盘避坑指南:解决JS API窗口聚焦失效与生产环境图标丢失

本文深入解析Tauri 2.0系统托盘功能的实现原理,针对开发者常见的JS API窗口聚焦失效和生产环境图标丢失问题,提供实战验证的解决方案。通过详细的配置指南和代码示例,帮助开发者高效避坑,提升跨平台桌面应用的稳定性和用户体验。

weixin_28713947的博客 393

Tauri 实战:从零构建一个轻量级桌面应用

本文详细介绍了如何使用Tauri框架从零构建一个轻量级桌面应用。通过对比Electron,Tauri凭借其更小的体积、更快的启动速度和更低的内存占用脱颖而出。文章涵盖了环境配置、项目初始化、前后端通信、文件系统操作、系统托盘集成等核心功能开发,并提供了性能优化、安全实践和调试技巧,帮助开发者高效构建跨平台桌面应用。

weixin_33681778的博客 478

Lemonade 构建选项完全指南:Linux 系统托盘与 Web/Tauri 桌面应用的可定制构建

本文以 Lemonade 仓库的构建系统文档 [src/cpp/BUILD_OPTIONS.md](https://link.gitcode.com/i/d1c2c922612f7370d220fd60b617013f) 为核心骨架,系统讲解 `REQUIRE_LINUX_TRAY`、`BUILD_WEB_APP`、`BUILD_TAURI_APP` 三个 CMake 构建选项的作用与组合用法。读

gitblog_07277的博客 514

基于Tauri+React构建跨平台桌面便签应用:从设计到实现

桌面应用开发是现代软件开发中的重要领域,它涉及将软件功能直接部署到用户的操作系统环境中,提供更高效、更集成的使用体验。其技术原理通常基于跨平台框架或原生SDK,通过封装系统API来实现窗口管理、文件操作和硬件交互等功能。在技术价值上,优秀的桌面应用能够填补轻量级工具与重型软件之间的空白,满足用户对即时性、轻量化和可管理性的综合需求。应用场景广泛,涵盖个人知识管理、快速笔记记录、临时信息收纳等日常办公与学习场景。本文聚焦于桌面便签工具的开发实践,深入探讨了如何利用Tauri框架结合React技术栈,实现一个类

weixin_34068198的博客 439

OpenFang Desktop 桌面应用深度解析:基于 Tauri 2.0 的 Agent OS 原生封装与嵌入式服务架构

OpenFang Desktop 是 OpenFang Agent OS 的原生桌面封装层,基于 Tauri 2.0 将整个 Agent 操作系统打包为单一可安装应用,通过嵌入式 axum API 服务器与内核在后台线程运行、WebView 渲染 WebUI 的方式,把无头(headless)部署的全部能力搬进了原生窗口。本文以 [docs/desktop.md](https://link.git

gitblog_00616的博客 767

1006. 换个格式输出整数 (15)-PAT乙级真题

1006. 换个格式输出整数 (15)让我们用字母B来表示“百”、字母S表示“十”,用“12…n”来表示个位数字n(&lt;10),换个格式来输出任一个不超过3位的正整数。例如234应该被输出为BBSSS1234,因为它有2个“百”、3个“十”、以及个位的4。输入格式:每个测试输入包含1个测试用例,给出正整数n(&lt;1000)。输出格式:每个测试用例的输出占一行,用规定的格式输出n。输入样例1...

柳婼 の blog 2243

KUKA .RSI 软件包

KUKA RSI软件包通过XML文件使得机器人与外部设备(传感器通讯)通讯成为了可能,但仍存在诸多限制,该模块最多允许9台设备进行连接,有限的IO口,匮乏的功能,并且需要投入大量精力琢磨相关说明文档。为此开发人员针对KUKA实时通讯,做了一些开源的接口使得上位机与机械臂的通讯更为简单。

huhu7777的博客 3295

Tauri 2.0系统托盘开发避坑指南:从菜单交互到WASM通信的完整实现

本文详细解析了在Tauri 2.0框架下开发系统托盘功能的完整流程与实战技巧。内容涵盖从项目初始化、跨平台图标处理、动态菜单构建,到Rust后端与Leptos前端WASM模块之间高效通信的实现方案,并重点分享了事件处理、状态管理以及常见平台兼容性问题的避坑指南,帮助开发者构建稳定、响应迅速的桌面应用。

z5a6b的博客 512

VisionPro动态坐标系构建:CogFixtureTool与CogFixtureNPointToNPointTool实战解析

本文深入解析了VisionPro中动态坐标系构建的核心技术,重点对比了CogFixtureTool与CogFixtureNPointToNPointTool的原理与应用。通过实战案例,阐述了如何利用坐标跟随(Fixturing)技术解决工业视觉检测中因零件位置偏移导致的测量不准问题,实现从“固定”到“随动”的思维跃迁,从而构建稳定、精准的检测程序。

weixin_29207439的博客 565
上一篇: Android 16KB Page Size 适配避坑指南:以CMake项目集成第三方预编译so库为例
下一篇: 腾讯地图Android SDK从零到一:手把手教你5分钟跑通Demo(附避坑指南)
code8
博客等级 码龄17年 65粉丝 1720原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值