コンテンツにスキップ
Tauri

Rust からフロントエンドを呼び出す

この文書には、Rust コードからアプリケーションのフロントエンドと通信する方法についての手引が書かれています。 フロントエンドから Rust コードと通信する方法については、次節の「フロントエンドから Rust を呼び出す」を参照してください。

Tauri アプリケーションの Rust 側では、「Tauri イベント・システム」を利用したり、「チャネル」を使用したり、あるいは JavaScript コードを直接検証することで、フロントエンドを呼び出すことができます。

Tauri には、Rust とフロントエンド間の双方向通信が行なえるシンプルな「イベント・システム」が付属しています。

このイベント・システムは、少量のデータをストリーミングする必要がある場合や、複数コンシューマー/複数プロデューサーのパターン(たとえば、プッシュ通知システムなど)を実装する必要があるといった状況を考慮して設計されています。

低遅延(レイテンシー)あるいは高スループットの状況向けの設計ではありません。ストリーミング・データ向けに最適化された実装については、チャンネル の項を参照してください。

「Tauri コマンド」と「Tauri イベント」の大きな違いは、イベントには強力な型のサポートがなく、イベント・ペイロード(データ本体)が常に JSON 文字列であるために大きなメッセージには適さず、イベント・データとチャンネルを細かく制御するための セキュリティ・レベル システムがサポートされていないことです。

構造体 AppHandle および WebviewWindow の型(タイプ)は、イベント・システム・トレイトの Listener および Emitter を実装します。

イベントは、「グローバル」(すべての「リスナー」に配信される)または「Webview 固有」(指定されたラベルに一致する Webview にのみ配信される)のどちらかになります。

グローバル・イベントをトリガー(開始)するには、Emitter#emit 関数を使用します:

src-tauri/src/lib.rs
use tauri::{AppHandle, Emitter};
#[tauri::command]
fn download(app: AppHandle, url: String) {
app.emit("download-started", &url).unwrap();
for progress in [1, 15, 50, 80, 100] {
app.emit("download-progress", progress).unwrap();
}
app.emit("download-finished", &url).unwrap();
}

特定の Webview により指定されたリスナーに対してイベントをトリガーするには、Emitter#emit_to 関数を使用します:

src-tauri/src/lib.rs
use tauri::{AppHandle, Emitter};
#[tauri::command]
fn login(app: AppHandle, user: String, password: String) {
let authenticated = user == "tauri-apps" && password == "tauri";
let result = if authenticated { "loggedIn" } else { "invalidCredentials" };
app.emit_to("login", "login-result", result).unwrap();
}

Emitter#emit_filter を呼び出すことで、Webview のリストにイベントをトリガーすることもできます。 次の例では、「open-file」イベントを、メインおよびファイル・ビューアーの Webview に発行します:

src-tauri/src/lib.rs
use tauri::{AppHandle, Emitter, EventTarget};
#[tauri::command]
fn open_file(app: AppHandle, path: std::path::PathBuf) {
app.emit_filter("open-file", path, |target| match target {
EventTarget::WebviewWindow { label } => label == "main" || label == "file-viewer",
_ => false,
}).unwrap();
}

イベント・ペイロードは、任意の 直列化可能 serializable 型にすることができ、この型は Clone トレイトも実装できます。 イベントごとに多くの情報を発行するオブジェクトを使用して、「ダウンロード・イベント例」を拡張してみましょう。

src-tauri/src/lib.rs
use tauri::{AppHandle, Emitter};
use serde::Serialize;
#[derive(Clone, Serialize)]
#[serde(rename_all = "camelCase")]
struct DownloadStarted<'a> {
url: &'a str,
download_id: usize,
content_length: usize,
}
#[derive(Clone, Serialize)]
#[serde(rename_all = "camelCase")]
struct DownloadProgress {
download_id: usize,
chunk_length: usize,
}
#[derive(Clone, Serialize)]
#[serde(rename_all = "camelCase")]
struct DownloadFinished {
download_id: usize,
}
#[tauri::command]
fn download(app: AppHandle, url: String) {
let content_length = 1000;
let download_id = 1;
app.emit("download-started", DownloadStarted {
url: &url,
download_id,
content_length
}).unwrap();
for chunk_length in [15, 150, 35, 500, 300] {
app.emit("download-progress", DownloadProgress {
download_id,
chunk_length,
}).unwrap();
}
app.emit("download-finished", DownloadFinished { download_id }).unwrap();
}

Tauri は、Webview と Rust 双方のインターフェースでイベントを検知(リッスン)するための API を提供しています。

@tauri-apps/api NPM パッケージは、グローバル・イベントと Webview 固有のイベントの両方を検知(リッスン)するための API を提供しています。

  • グローバル・イベントの検知

    import { listen } from '@tauri-apps/api/event';
    type DownloadStarted = {
    url: string;
    downloadId: number;
    contentLength: number;
    };
    listen<DownloadStarted>('download-started', (event) => {
    console.log(
    `downloading ${event.payload.contentLength} bytes from ${event.payload.url}`
    );
    });
  • Webview 固有イベントの検知

    import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow';
    const appWebview = getCurrentWebviewWindow();
    appWebview.listen<string>('logged-in', (event) => {
    localStorage.setItem('session-token', event.payload);
    });

The listen 関数は、アプリケーションの全「ライフタイム」期間中、イベント・リスナーの登録は維持されたままです。 イベントの検知(リッスン)を停止するには、listen 関数によって返される unlisten 関数を使用できます:

import { listen } from '@tauri-apps/api/event';
const unlisten = await listen('download-started', (event) => {});
unlisten();

グローバル・イベントも Webview 固有のイベントも、Rust に登録されたリスナーに配信されます。

  • グローバル・イベントの検知

    src-tauri/src/lib.rs
    use tauri::Listener;
    #[cfg_attr(mobile, tauri::mobile_entry_point)]
    pub fn run() {
    tauri::Builder::default()
    .setup(|app| {
    app.listen("download-started", |event| {
    if let Ok(payload) = serde_json::from_str::<DownloadStarted>(&event.payload()) {
    println!("downloading {}", payload.url);
    }
    });
    Ok(())
    })
    .run(tauri::generate_context!())
    .expect("error while running tauri application");
    }
  • Webview 固有イベントの検知

    src-tauri/src/lib.rs
    use tauri::{Listener, Manager};
    #[cfg_attr(mobile, tauri::mobile_entry_point)]
    pub fn run() {
    tauri::Builder::default()
    .setup(|app| {
    let webview = app.get_webview_window("main").unwrap();
    webview.listen("logged-in", |event| {
    let session_token = event.data;
    // save token..
    });
    Ok(())
    })
    .run(tauri::generate_context!())
    .expect("error while running tauri application");
    }

The listen 関数は、アプリケーションの全「ライフタイム」期間中、イベント・リスナーの登録は維持されたままです。 イベントの検知(リッスン)を停止するには、listen 関数によって返される unlisten 関数を使用できます:

// unlisten outside of the event handler scope:
let event_id = app.listen("download-started", |event| {});
app.unlisten(event_id);
// unlisten when some event criteria is matched
let handle = app.handle().clone();
app.listen("status-changed", |event| {
if event.data == "ready" {
handle.unlisten(event.id);
}
});

さらに、Tauri はイベントを一度だけ検知(リッスン)するためのユーティリティ関数を提供しています:

app.once("ready", |event| {
println!("app is ready");
});

この場合、イベント・リスナーは最初のトリガー後にすぐに登録が解除されます。

《訳注》 チャネル 「流路、伝送路」。日本語では一般に「チャンネル」と表記されますが、コンピュータ用語としては「チャネル」表記が一般的であるので、その表記に従って記述します。

イベント・システムは、アプリケーションで「グローバル」に利用できるシンプルな双方向通信を行なうように設計されています。 内部的には直接 JavaScript コードを検証するため、大量のデータを送信するには適さない可能性があります。

チャネルは高速に、順序付けられたデータを配信するように設計されています。したがって、ダウンロードの進行状況、子プロセスの出力、WebSocket メッセージなどのストリーミング操作のために内部的に使用されます。

上述の「ダウンロード・コマンドの事例」を、「イベント・システム」の代わりに「チャネル」を使用するように書き直してみましょう:

src-tauri/src/lib.rs
use tauri::{AppHandle, ipc::Channel};
use serde::Serialize;
#[derive(Clone, Serialize)]
#[serde(rename_all = "camelCase", tag = "event", content = "data")]
enum DownloadEvent<'a> {
#[serde(rename_all = "camelCase")]
Started {
url: &'a str,
download_id: usize,
content_length: usize,
},
#[serde(rename_all = "camelCase")]
Progress {
download_id: usize,
chunk_length: usize,
},
#[serde(rename_all = "camelCase")]
Finished {
download_id: usize,
},
}
#[tauri::command]
fn download(app: AppHandle, url: String, on_event: Channel<DownloadEvent>) {
let content_length = 1000;
let download_id = 1;
on_event.send(DownloadEvent::Started {
url: &url,
download_id,
content_length,
}).unwrap();
for chunk_length in [15, 150, 35, 500, 300] {
on_event.send(DownloadEvent::Progress {
download_id,
chunk_length,
}).unwrap();
}
on_event.send(DownloadEvent::Finished { download_id }).unwrap();
}

ダウンロード・コマンドを呼び出すときには、チャネルを作成し、それを引数として指定する必要があります:

import { invoke, Channel } from '@tauri-apps/api/core';
type DownloadEvent =
| {
event: 'started';
data: {
url: string;
downloadId: number;
contentLength: number;
};
}
| {
event: 'progress';
data: {
downloadId: number;
chunkLength: number;
};
}
| {
event: 'finished';
data: {
downloadId: number;
};
};
const onEvent = new Channel<DownloadEvent>();
onEvent.onmessage = (message) => {
console.log(`got download event ${message.event}`);
};
await invoke('download', {
url: 'https://raw.githubusercontent.com/tauri-apps/tauri/dev/crates/tauri-schema-generator/schemas/config.schema.json',
onEvent,
});

Webview コンテキスト 上で JavaScript コードを直接実行するには、WebviewWindow#eval 関数を使用できます:

src-tauri/src/lib.rs
use tauri::Manager;
tauri::Builder::default()
.setup(|app| {
let webview = app.get_webview_window("main").unwrap();
webview.eval("console.log('hello from Rust')")?;
Ok(())
})

検証されるスクリプトがそれほど単純ではなく、Rust オブジェクトからの入力を使用しなければならない場合は、serialize-to-javascript クレートの使用をお勧めします。

【※ この日本語版は、「Feb 22, 2025 英語版」に基づいています】


© 2025 Tauri Contributors. CC-BY / MIT