当前位置: 首页 > news >正文

【Tauri2】014——简单使用listen和emit

前言

【Tauri2】013——前端Window Event与创建Window-CSDN博客https://blog.csdn.net/qq_63401240/article/details/146981362?spm=1001.2014.3001.5502【Tauri2】012——on_window_event函数-CSDN博客https://blog.csdn.net/qq_63401240/article/details/146909801?spm=1001.2014.3001.5502

前面简单地介绍了window中的事件,分别在前后端使用事件,但是这些事件都是Tauri中的,实际上也可以自定义事件,试试

发送事件emit并在前端监听事件listen

可以全局监听事件,但是笔者想写写通信函数。因此,把事件写在通信函数中。

要想发送事件,需要这个emit方法,而这个emit在AppHandle中,准确地说应该是在AppHandle实现的trait Emittter中

AppHandle in tauri - Rusthttps://docs.rs/tauri/latest/tauri/struct.AppHandle.html#impl-Emitter%3CR%3E-for-AppHandle%3CR%3E看看函数

  fn emit<S: Serialize + Clone>(&self, event: &str, payload: S) -> Result<()> {
    let event = EventName::new(event)?;
    let payload = EmitPayload::Serialize(&payload);
    self.manager().emit(event, payload)
  }

第一个参数 &self ,实例的引用,结构体实现trait的方法,一般第一个参数都是包含self,不必细说

第二个参数 event: &str,字符串切片,很明显,事件的名字

第三个参数payload: S,S是泛型,泛型约束是Serialize + Clone,简单地说,序列化和克隆

在函数体,实现也比较简单,初始化名字和负载。

从函数定义,可以发现,payload需要实现Serialize + Clone

实现Serialize + Clone

很简单,看看依赖

[dependencies]
tauri = { version = "2.4.1", features = ["image-png", "unstable", "devtools"] }
tauri-plugin-opener = "2"
serde = { version = "1", features = ["derive"] }
serde_json = "1"

这个serde 就是实现的关键。

在实现之前,先对项目进行修改一下

其中mod.rs的代码如下

pub mod event;
pub mod window;

在event.rs中实现。代码如下

use serde::Serialize;

#[derive(Serialize, Clone)]
struct User{
    id: i32,
    name: String,
    age: i32,
}

首先,这个derive是属性,用于触发派生宏(proc_macro_derive)的执行,它可以附加到结构体、枚举等类型上,告诉编译器为这些类型生成特定的代码,

Derive - The Rust Referencehttps://doc.rust-lang.org/reference/attributes/derive.html可以通过cargo expand查看一些宏展开之后的代码

【Tauri2】007——Tauri2和cargo expand-CSDN博客https://blog.csdn.net/qq_63401240/article/details/146632055?spm=1001.2014.3001.5502在src-tauri目录,运行如下命令

cargo expand ::command::event

结果如下

pub mod event {
    use serde::Serialize;
    struct User {
        id: i32,
        name: String,
        age: i32,
    }
    #[doc(hidden)]
    #[allow(
        non_upper_case_globals,
        unused_attributes,
        unused_qualifications,
        clippy::absolute_paths,
    )]
    const _: () = {
        #[allow(unused_extern_crates, clippy::useless_attribute)]
        extern crate serde as _serde;
        #[automatically_derived]
        impl _serde::Serialize for User {
            fn serialize<__S>(
                &self,
                __serializer: __S,
            ) -> _serde::__private::Result<__S::Ok, __S::Error>
            where
                __S: _serde::Serializer,
            {
                let mut __serde_state = _serde::Serializer::serialize_struct(
                    __serializer,
                    "User",
                    false as usize + 1 + 1 + 1,
                )?;
                _serde::ser::SerializeStruct::serialize_field(
                    &mut __serde_state,
                    "id",
                    &self.id,
                )?;
                _serde::ser::SerializeStruct::serialize_field(
                    &mut __serde_state,
                    "name",
                    &self.name,
                )?;
                _serde::ser::SerializeStruct::serialize_field(
                    &mut __serde_state,
                    "age",
                    &self.age,
                )?;
                _serde::ser::SerializeStruct::end(__serde_state)
            }
        }
    };
    #[automatically_derived]
    impl ::core::clone::Clone for User {
        #[inline]
        fn clone(&self) -> User {
            User {
                id: ::core::clone::Clone::clone(&self.id),
                name: ::core::clone::Clone::clone(&self.name),
                age: ::core::clone::Clone::clone(&self.age),
            }
        }
    }
}

这段代码还是很复杂的,笔者就简单说一说。

1、#[allow]

allow是允许的意思,所以意思很明显

non_upper_case_globals:允许非大写的常量名(如 _serde)。

unused_attributes/unused_qualifications:忽略未使用的属性或限定符。

clippy::absolute_paths:允许使用绝对路径(如 ::core::clone)。

2、#[automatically_derived]

标记此实现是自动生成的,避免编译器警告

3、实现Serialize 这个trait和其中的serialize方法

调用 serialize_struct开始序列化结构体,使用serialize_field 依次序列化每个字段,最终调用end完成序列化。

4、实现Clone这个trait和其中的clone方法

总之,对于payload,需要实现这两个trait。

创建通信函数,准备发送事件

在event.rs中,关键代码如下

#[command]
pub fn send_event(app: AppHandle, event_name: &str) {
    app.emit(
        event_name,
        User {
            id: 1,
            name: "John".to_string(),
            age: 30,
        },
    )
    .unwrap()
}

注册通信函数。

前端监听

在App.tsx中,设置按钮并绑定按钮的点击事件

    async function listenSignal(){
        await invoke("send_event",{
            event_name:"hello",
        });
    }

在Events目录下,监听

export default function useWindowEvent() {
    let window = getCurrentWindow();
    window.listen("hello", (event) => {
        console.log(event.payload);
    })

}

运行项目

点击触发信号,然后

报错了。报错如下

Uncaught (in promise) invalid args `eventName` for command `send_event`:
 command send_event missing required key eventName

 居然需要eventName,还需要转化命名方式camelCase,因此,修改代码

    async function listenSignal(){
        await invoke("send_event",{
            eventName:"hello",
        });
    }

结果如下

居然还需要改参数名字,笔者还不知道

使用cargo expand 看看

 cargo expand > expanded.rs

输出到文件中,太多了,powershell展示不全

关键代码如下

match ::tauri::ipc::CommandArg::from_command(::tauri::ipc::CommandItem {
                            plugin: ::core::option::Option::None,
                            name: "send_event",
                            key: "eventName",
                            message: &__tauri_message__,
                            acl: &__tauri_acl__,

还真是eventName

总之,交互成功

前端发送事件emit并在后端监听事件listen

看看函数签名

  fn listen<F>(&self, event: impl Into<String>, handler: F) -> EventId
      where
        F: Fn(Event) + Send + 'static

第一个参数event ,接受任何能 转换为Striing的类型。

第一个参数handler,类型是泛型F,F的约束是Fn(Event) + Send + 'static。

简单地说。第一个参数一般传&str,第一个参数是闭包或者函数。

通信函数监听

代码如下

#[command]
pub fn listen_event(app:AppHandle, event_name: &str) {
    app.listen(event_name, |event| {
        let user = event.payload();
        println!("user: {:?}", user);
    });
    
}

但是这个payload返回的是字符串切片&str,笔者希望得到一个User

  pub fn payload(&self) -> &str {
    &self.data
  }

这就需要另一个依赖serde_json。

serde_json - Rusthttps://docs.rs/serde_json/latest/serde_json/代码如下

#[command]
pub fn listen_event(app:AppHandle, event_name: &str) {
    app.listen(event_name, |event| {
        let user:User =serde_json::from_str(event.payload()).unwrap() ;
        println!("user: {:?}", user);
    });
    
}

要想把&str变成User,需要反序列化,就需要实现Deserialize这个trait

因为还需要打印User,还需要实现Debug这个trait

因此,User的修改如下

#[derive(Serialize, Clone, Deserialize, Debug)]
struct User {
    id: i32,
    name: String,
    age: i32,
}

前端发送信号

关键代码

    let window = getCurrentWindow();    
    async function triggerSignal(){
        window.emit("world",{
            id:1,
            name:"tom",
            age:18,
        })
        await invoke("listen_event",{
            eventName:"world",
        });
    }

运行,结果如下

其他方法及总结

实际上,除了emit和listen,还有emitTo(emit_to)、once之类的

event | Taurihttps://v2.tauri.app/reference/javascript/api/namespaceevent/#emitto还有取消监听unlisten之类的方法。以后慢慢使用。

当然,也不一定要在通信函数中,也可以在setup或者其他地方。

http://www.dtcms.com/a/111901.html

相关文章:

  • DuckDB系列教程:如何分析Parquet文件
  • Linux中的调试器gdb与冯·诺伊曼体系
  • 使用MCP方案与Claude实现虚幻引擎自动化游戏开发
  • [2008][note]腔内级联拉曼发射的,二极管泵浦多频调Q laser——
  • 【LLM】使用MySQL MCP Server让大模型轻松操作本地数据库
  • JSON-lib考古现场:在2025年打开赛博古董店的奇妙冒险
  • 如何分析 jstat 统计来定位 GC?
  • Navicat17详细安装教程(附最新版本安装包和补丁)2025最详细图文教程安装手册
  • 运算放大器(五)电压比较器
  • WSL使用经验
  • 从代码学习深度学习 - GRU PyTorch版
  • 基于大模型与动态接口调用的智能系统(知识库实现)
  • 动态规划似包非包系列一>组合总和IIV
  • leetcode117 填充每个节点的下一个右侧节点指针2
  • ctfshow VIP题目限免 phps源码泄露
  • LMK04828使用指南-01-简介与引脚功能描述
  • vm虚拟机虚拟出网卡并ping通外网
  • Linux驱动开发练习案例
  • 三、Jenkinsfile 的使用
  • 数字人代言人如何提升品牌信任度?
  • [C/C++]文件输入输出
  • 【YOLO系列(V5-V12)通用数据集-电梯内电动车检测数据集】
  • Temu物流成本或上涨?南非海关140项减免取消倒计时
  • 明清两朝全方位对比
  • 计算机视觉算法实战——基于YOLOv8的汽车试验场积水路段识别系统
  • SpringMVC+Spring+MyBatis知识点
  • Buildroot与Yocto介绍比对
  • 【MySQL】常用SQL--持续更新ing
  • Linux make与makefile 项目自动化构建工具
  • 26考研——排序(8)