新しいサブウィンドウを開く (Vite Multi-page) (win-017) の構成を前提に、ウィンドウ作成時に親から子へデータを渡す実装方法を紹介します。
単純にウィンドウ作成直後にイベントを emit しても、子ウィンドウ側のロード(HTML/JSの読み込み)が完了しておらず、イベントを受け取れない「競合状態(Race Condition)」が発生します。
これを回避するため、子ウィンドウから「準備完了」イベントを送信し、それを受け取った親がデータを送る(ハンドシェイク) パターンを実装します。
前提条件: win-017 との差分
win-017 のディレクトリ構成(subwindow.html, src/subwindow/main.ts など)をそのまま使用します。
Permissions (権限) の設定
win-017 の権限設定に加え、イベントの送受信を行うために core:event:default が必要です。
src-tauri/capabilities/default.json の permissions 配列に以下を追加してください。
{
"permissions": [
...,
"core:window:default",
"core:webview:allow-create-webview-window",
"core:window:allow-close",
"core:event:default" // 追加: イベント送受信に必要
],
"windows": ["main", "settings-window"]
}
1. 子ページ(サブウィンドウ)の変更
サブウィンドウ側では、画面の準備が整ったことを親に通知し、その後親から送られてくるデータをリッスンします。
src/subwindow/main.ts
win-017 のコードに対し、イベントリスナーと通知を行う処理を追加します。
import { getCurrentWindow } from '@tauri-apps/api/window';
import { emit, listen } from '@tauri-apps/api/event';
const appWindow = getCurrentWindow();
const outputEl = document.getElementById('message-output');
// 1. 親からのデータを受信するリスナーを設定
listen('init-data', (event) => {
const data = event.payload as { userId: number, userName: string };
console.log('親からデータを受信:', data);
if (outputEl) {
outputEl.innerText = `UserID: ${data.userId}, Name: ${data.userName}`;
}
});
document.getElementById('close-btn')?.addEventListener('click', async () => {
await appWindow.close();
});
// 2. 準備完了を親ウィンドウに通知 (ハンドシェイク開始)
// HTML要素の描画やリスナー登録が終わったタイミングで実行します
emit('subwindow-ready');
※ subwindow.html に <div id="message-output"></div> などを追加して、データが表示されるようにしておくと動作確認がしやすくなります。
2. 親ページ(メインウィンドウ)の変更
親ウィンドウ側では、ウィンドウを作成する前に「準備完了」イベントのリスナーを待機させておき、イベントを受信したらデータを送信します。
src/main.ts
WebviewWindow を作成する前後のロジックを変更します。
import { WebviewWindow } from '@tauri-apps/api/webviewWindow';
import { listen, emitTo } from '@tauri-apps/api/event';
async function openSubWindowWithData() {
const label = 'settings-window';
// 既に存在するかチェック
const existingWin = await WebviewWindow.getByLabel(label);
if (existingWin) {
await existingWin.setFocus();
// 既に開いている場合もデータを再送したいならここで emitTo を呼ぶ
return;
}
// 1. サブウィンドウからの「準備完了」イベントを待ち受ける
// once: true にすることで、1回受信したらリスナーを自動解除
const unlisten = await listen('subwindow-ready', async (event) => {
console.log('サブウィンドウの準備が完了しました');
// 2. 特定のウィンドウ(ラベル: settings-window)に向けてデータを送信
// emitTo(label, eventName, payload)
await emitTo(label, 'init-data', {
userId: 1001,
userName: 'Tauri User'
});
// リスナー役目が終わったので明示的に解除(onceを使っていない場合など)
unlisten();
});
// 3. ウィンドウを作成
const webview = new WebviewWindow(label, {
url: '/subwindow.html',
title: '設定 (データ受信)',
width: 600,
height: 400,
});
webview.once('tauri://error', async (e) => {
console.error('ウィンドウ作成エラー:', e);
// エラー時はリスナーが残り続けないように解除
unlisten();
});
}
document.querySelector('#open-settings')?.addEventListener('click', openSubWindowWithData);
解説
ハンドシェイクの流れ
1. 親: listen('subwindow-ready') で待ち受け開始。
2. 親: new WebviewWindow(...) でウィンドウ作成。
3. 子: HTML/JS がロードされる。
4. 子: listen('init-data') でデータ待ち受け準備。
5. 子: emit('subwindow-ready') を発火。
6. 親: subwindow-ready を検知し、emitTo(..., 'init-data', ...) でデータを送信。
7. 子: init-data を受信し、画面に反映。
この手順を踏むことで、ウィンドウのロード待ちによるデータの取りこぼしを防ぐことができます。
別のアプローチ (Pull 型)
子ウィンドウ側から能動的にデータを取得する「Pull 型」のアプローチもあります。
子ウィンドウのマウント時(起動時)に invoke コマンドでバックエンドからデータを取得したり、親ウィンドウにリクエストを送って返答をもらう方法です。データの依存関係が単純な場合は Pull 型の方が実装がシンプルになる場合もあります。