Tauriの基礎 - 開発環境

提供: MochiuWiki : SUSE, EC, PCB

概要

Tauriは、RustとWeb技術を組み合わせてデスクトップアプリケーションを構築するための軽量なフレームワークである。

ElectronやNW.jsのような既存のデスクトップアプリケーション開発フレームワークと比較して、Tauriは大幅に小さいバイナリサイズと優れたパフォーマンスを実現している。
これは、ChromiumやNode.jsをバンドルする代わりに、OSが提供するネイティブのWebViewを利用しているからである。

Tauriのアーキテクチャは、バックエンド (コア機能やシステムAPI) にRustを使用し、フロントエンド (ユーザインターフェース) には任意のWebフレームワークを使用できる柔軟な設計になっている。
React、Vue、Svelte、あるいは純粋なHTML / CSS / JavaScript等、開発者が慣れ親しんだ技術を選択することができる。

Tauriで開発されたアプリケーションは、Windows、MacOS、Linuxの各プラットフォーム向けにビルドできる。
また、モバイルプラットフォーム (iOS、Android) のサポートも進行中である。

バックエンドのRustコードは強力な型システムとメモリ安全性を提供し、フロントエンドのReactは豊富なエコシステムと開発者体験を提供する。
この組み合わせにより、安全で高速、かつ保守性の高いデスクトップアプリケーションを開発できる。


依存関係のパッケージのインストール

Tauriがネイティブアプリケーションをビルドするために必要な依存関係をインストールする。

webkit2gtk3はWebViewを表示するためのエンジン、opensslは暗号化通信、他のライブラリはアプリケーションの各種機能やアイコン表示に使用される。

# RHEL
## 環境によっては、webkit2gtk4.1-develが必要な場合がある
sudo dnf install epel-release
sudo dnf install curl wget file webkit2gtk4.0-devel openssl-devel libappindicator-gtk3-devel librsvg2-devel

# SUSE
sudo zypper install curl wget file webkit2gtk3-devel openssl-devel libappindicator3-devel librsvg-devel


Node.jsのインストール

Reactを動かすためのJavaScript環境が必要である。

最も推奨される方法は、Node Version Manager (nvm) を使用する方法である。
これを使用すると、異なるプロジェクトで異なるNode.jsのバージョンを簡単に切り替えることができる。

Node.jsのインストールは、インストール - Node.js のページを参照すること。


Rustのインストール

Rustのインストールは、インストール - Rust のページを参照すること。


Tauri CLIのインストール

Tauriのコマンドラインツールをインストールする。
これには2つの方法が存在するが、推奨される方法は npm を使用する方法である。

# ローカルにインストールする場合
npm install @tauri-apps/cli

# グローバルにインストールする場合
npm install -g @tauri-apps/cli

# Rustのパッケージマネージャであるcargoを使用する場合
cargo install tauri-cli


インストールが完了した後、動作確認をする。

npm exec tauri --version



プロジェクトの作成

任意のディレクトリに移動して、以下に示すコマンドを実行する。

npm create tauri-app@latest
  • プロジェクト名
    任意の名前 (例: my-tauri-app)
  • パッケージマネージャ
    npm
  • UIフレームワーク
    React
  • TypeScriptを使用するかどうか
    TypeScriptを推奨する。


プロジェクトのセットアップが完了した後、自動生成されたプロジェクトディレクトリに移動する。

cd <プロジェクト名>



プロジェクト構造

srcディレクトリにはReactのフロントエンドコード、src-tauriディレクトリにはRustのバックエンドコードが配置される。

index.html : アプリケーションの入口

index.htmlは、Tauriアプリケーション全体の入り口となるHTMLファイルである。
通常のWebサイトと同様、TauriのWebViewが最初に読み込むページとなる。

head タグの中には基本的なメタ情報、body タグの中には div タグが1つだけあり、そこにid="root"のような属性が付いている。
この div タグが、Reactアプリケーション全体が描画される場所になる。

ファイルの最後には script タグがあり、JavaScriptファイル (通常は、src/main.tsxやsrc/main.jsx) を読み込んでいる。

※重要
画面の内容は、全てReactが動的に生成して、このrootのdiv要素の中に挿入していく。
これが、シングルページアプリケーション (SPA) と呼ばれる仕組みの基本である。

 <!doctype html>
 <html lang="en">
   <head>
     <!-- 文字エンコーディングをUTF-8に設定 (日本語等の多言語文字を正しく表示するために必要) -->
     <meta charset="UTF-8" />
 
     <!-- ファビコン (ブラウザのタブに表示される小さなアイコン) の設定 -->
     <!-- この場合はViteのロゴをSVG形式で使用している -->
     <link rel="icon" type="image/svg+xml" href="/vite.svg" />
 
     <!-- レスポンシブデザインのための設定 -->
     <!-- width=device-widthでデバイスの画面幅に合わせ、initial-scale=1.0で初期ズーム倍率を1倍に設定 -->
     <!-- これにより、モバイルデバイスでも適切に表示される -->
     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 
     <!-- ブラウザのタブやウィンドウのタイトルバーに表示されるタイトル -->
     <title>Tauri + React + Typescript</title>
   </head>
   <body>
     <!-- Reactアプリケーション全体が描画される起点となる要素 -->
     <!-- このdiv要素の中に、main.tsxで起動されたReactコンポーネントが挿入される -->
     <!-- id="root"という識別子により、JavaScriptからこの要素を特定できる -->
     <div id="root"></div>
 
     <!-- Reactアプリケーションのエントリーポイント (起動スクリプト) を読み込む -->
     <!-- type="module"により、ES6モジュール形式として扱われ、importやexportが使える -->
     <!-- main.tsxが実行されることで、Reactアプリケーションが初期化され、上記のroot要素に描画される -->
     <script type="module" src="/src/main.tsx"></script>
   </body>
 </html>


tsconfig.json : TypeScriptのメイン設定

TypeScriptコンパイラに対して、「どのようにTypeScriptコードをJavaScriptコードに変換すればよいか」を指示する設定ファイルである。

TypeScriptは、JavaScriptに型の概念を追加した言語で、開発時にはTypeScriptで記述するが、動作させる時にはJavaScriptに変換する必要がある。
この変換ルールがtsconfig.jsonに記述されている。

例えば、以下に示すような設定が含まれている。

  • どのバージョンのJavaScriptに変換するか
  • どのディレクトリのファイルをコンパイル対象にするか
  • strictモード (厳格な型チェック) を有効にするか


Reactを使用する場合は、JSXという特殊な構文をどう扱うかという設定も重要である。
JSXは、JavaScript内でHTMLのような記述ができる構文である。

 {
   // TypeScriptコンパイラのオプション設定
   // これらの設定により、TypeScriptがどのようにコードをJavaScriptに変換するかが決まる
   "compilerOptions": {
     // コンパイル対象となるJavaScriptのバージョンを指定
     // ES2020は2020年に標準化されたECMAScript仕様で、async/awaitやオプショナルチェイニングなどの機能が使える
     "target": "ES2020",
 
     // クラスフィールドの定義方法を新しい仕様(defineProperty)を使うように設定
     // これにより、クラスのプロパティがより予測可能な動作をする
     "useDefineForClassFields": true,
 
     // TypeScriptで使用できるライブラリの型定義を指定
     // ES2020: ES2020の標準ライブラリの型定義
     // DOM: ブラウザのDOM(document、windowなど)の型定義
     // DOM.Iterable: DOMの反復可能なオブジェクト(NodeListなど)の型定義
     "lib": ["ES2020", "DOM", "DOM.Iterable"],
 
     // モジュールシステムの形式を指定
     // ESNextは最新のECMAScriptモジュール仕様を使用することを意味する(import/export構文)
     "module": "ESNext",
 
     // node_modulesの型チェックをスキップして、ビルド速度を向上させる
     // サードパーティライブラリの型エラーを無視することで、開発効率を上げる
     "skipLibCheck": true,
 
     /* バンドラーモード関連の設定 */
     // ViteやWebpackなどのモダンなバンドラーツールと連携するための設定
 
     // モジュールの解決方法を「bundler」に設定
     // これにより、バンドラーが独自のモジュール解決ロジックを使うことを前提とした設定になる
     "moduleResolution": "bundler",
 
     // .ts拡張子を含むimport文を許可する
     // 通常、TypeScriptは拡張子なしでimportするが、バンドラーモードではこれが許可される
     "allowImportingTsExtensions": true,
 
     // JSONファイルをモジュールとしてimportできるようにする
     // import data from './data.json' のような書き方が可能になる
     "resolveJsonModule": true,
 
     // 各ファイルを独立したモジュールとして扱う
     // これにより、各ファイルが他のファイルに依存せずにコンパイルされ、Viteなどのツールと相性が良い
     "isolatedModules": true,
 
     // JavaScriptファイルを出力しない
     // Viteが独自にトランスパイルとバンドルを行うため、TypeScriptコンパイラはファイルを出力する必要がない
     "noEmit": true,
 
     // JSX(ReactのHTML風構文)を「react-jsx」形式で変換
     // React 17以降の新しいJSX変換方式を使用し、各ファイルで明示的にReactをimportする必要がなくなる
     "jsx": "react-jsx",
 
     /* コード品質チェック(Linting)関連の設定 */
     // これらの設定により、より厳格な型チェックと潜在的なバグの検出が行われる
 
     // strictモードを有効化
     // これにより、すべての厳格な型チェックオプションが一括で有効になる
     // (null安全性、暗黙のany型の禁止、thisの型チェックなど)
     "strict": true,
 
     // 使用されていないローカル変数がある場合にエラーを出す
     // コードの品質を保ち、不要な変数を削除するのに役立つ
     "noUnusedLocals": true,
 
     // 使用されていない関数のパラメータがある場合にエラーを出す
     // これにより、不要な引数を持つ関数を発見できる
     "noUnusedParameters": true,
 
     // switch文でcase句からbreak/returnなしで次のcase句に落ちる(fall through)場合にエラーを出す
     // 意図しないバグを防ぐための安全機能
     "noFallthroughCasesInSwitch": true
   },
 
   // TypeScriptコンパイラが処理対象とするディレクトリを指定
   // この場合、srcディレクトリ内のすべてのTypeScriptファイルが対象となる
   "include": ["src"],
 
   // プロジェクト参照を設定
   // tsconfig.node.jsonを参照することで、ビルドツール用の設定とアプリケーション用の設定を分離している
   // これにより、Node.js環境で実行されるコード(vite.config.tsなど)とブラウザで実行されるコードで
   // 異なるコンパイル設定を適用できる
   "references": [{ "path": "./tsconfig.node.json" }]
 }


tsconfig.node.json : ビルドツール用のTypeScript設定

ビルドツール (Vite等) の設定ファイルをTypeScriptで記述する時に使用する設定である。

tsconfig.json および tsconfig.node.json が2つに分割されている理由として、実行環境が違うからである。

tsconfig.jsonはWebViewで動作するための設定、tsconfig.node.jsonはNode.js環境で動作する (ビルドやバンドルの処理を行うツール) ための設定である。
WebView環境 と Node.js環境 では、使用できる機能や推奨される設定が異なるため、設定を分離して管理した方が効率的である。

例えば、vite.config.ts ファイルはNode.js環境で実行される設定ファイルである。
このファイルがTypeScriptで記述されているため、それをコンパイルするための設定がtsconfig.node.jsonに記述されている。

 {
   // このファイルは、Node.js環境で実行されるビルドツール用のTypeScript設定ファイルです
   // 具体的には、vite.config.tsなどの設定ファイルをコンパイルする際に使用されます
 
   "compilerOptions": {
     // compositeモードを有効化
     // これにより、プロジェクト参照(Project References)機能が使えるようになる
     // 大規模なプロジェクトで複数のtsconfig.jsonを連携させる際に必要な設定
     // メインのtsconfig.jsonから、このtsconfig.node.jsonを参照できるようになる
     "composite": true,
 
     // node_modulesの型チェックをスキップ
     // サードパーティライブラリの型定義ファイルをチェックしないことで、コンパイル速度を向上させる
     // 開発体験の改善に役立つ設定
     "skipLibCheck": true,
 
     // モジュールシステムの形式を指定
     // ESNextは最新のECMAScriptモジュール仕様(import/export)を使用する
     // Node.jsの最新バージョンではESモジュールがネイティブでサポートされている
     "module": "ESNext",
 
     // モジュールの解決方法を「bundler」モードに設定
     // Viteのようなモダンなバンドラーツールが使用する独自のモジュール解決ロジックに対応
     // これにより、バンドラーが期待する形式でモジュールが解決される
     "moduleResolution": "bundler",
 
     // デフォルトエクスポートを持たないモジュールからのデフォルトインポートを許可
     // 例えば、CommonJS形式で書かれたライブラリを、ES6のimport文でインポートできるようになる
     // import React from 'react' のような書き方が可能になる(Reactはデフォルトエクスポートを持たない)
     "allowSyntheticDefaultImports": true
   },
 
   // このTypeScript設定ファイルが対象とするファイルを指定
   // この設定では、vite.config.tsファイルのみが対象となる
   // つまり、このtsconfig.node.jsonは、Viteの設定ファイルをコンパイルするためだけに使われる
   // アプリケーションのソースコード(srcディレクトリ)は、メインのtsconfig.jsonで処理される
   "include": ["vite.config.ts"]
 }


vite.config.ts : Viteの設定ファイル

Viteというビルドツールの設定ファイルである。
Viteは、フロントエンド開発で人気のある開発サーバとビルドツールである。

このファイルには、以下に示す設定が記述されている。

  • 開発サーバをどのポートで起動するか
  • どのプラグインを使うか
  • ビルド時にどのような最適化を行うか


Tauri + Reactのプロジェクトでは、Reactを使用するためのプラグインやTauriとViteを連携させるための設定が含まれている。

Viteでは、開発中にソースコードを変更すると瞬時にアプリケーションに反映される ホットモジュールリプレースメント (HMR) という機能でがある。
これにより、ソースコードを変更するたびに変更した部分だけがリロードされるため、開発効率が大幅に向上する。

src/main.tsx : Reactアプリケーションの起動コード

Reactアプリケーションを実際に起動させるソースコードを記述する。

ReactDOMライブラリを使用して、Appコンポーネントをindex.htmlのroot要素にマウント (描画) している。
このファイルが実行されることにより、Reactアプリケーションが動作する。

また、このファイルでは、アプリケーション全体に影響する初期設定やグローバルなスタイルシートのインポートも行われることがある。
アプリケーションが起動する直後に実行したい処理がある場合は、このファイルに記述する。

 // このファイルの役割:
 // 1. index.htmlのroot要素を特定する
 // 2. その要素をReactアプリケーションのコンテナとして設定する
 // 3. Appコンポーネントをレンダリング (描画) する
 // 4. 開発時の品質チェック (StrictMode) を有効化する
 //
 // このファイルはアプリケーション全体のエントリーポイントであり、
 // index.htmlから<script>タグで読み込まれることで、Reactアプリケーションが起動する
 
 // Reactライブラリの本体をインポート
 // Reactは、UIを構築するためのコアライブラリ
 // このインポートにより、JSX構文やReactの基本機能が使えるようになる
 import React from "react";
 
 // ReactDOMのクライアントサイド機能をインポート
 // ReactDOM/clientは、Reactコンポーネントを実際のHTML DOM要素に変換し、Webブラウザの画面に描画するための機能を提供する
 // "react-dom/client"は、React 18以降で導入された新しいAPIで、以前の"react-dom"より高速かつ柔軟なレンダリングが可能になっている
 import ReactDOM from "react-dom/client";
 
 // アプリケーションのメインコンポーネント (App) をインポート
 // このコンポーネントが、アプリケーション全体の構造と機能を定義している
 // "./App"という相対パスは、同じディレクトリ (src) 内のApp.tsxファイルを指す
 // .tsxや.jsxの拡張子は、インポート時に省略できる
 import App from "./App";
 
 // ReactDOMのcreateRootメソッドを使って、Reactアプリケーションのルート (起点) を作成
 // この処理は、Reactアプリケーションを起動するための最も重要なステップ
 //
 // document.getElementById("root"):
 //   - index.html内の<div id="root"></div>要素を取得
 //   - この要素が、Reactアプリケーション全体を表示するコンテナとなる
 //   - 全てのReactコンポーネントは、最終的にこの要素の中に描画される
 //
 // as HTMLElement:
 //   - TypeScriptの型アサーション (型の明示的な指定)
 //   - getElementById()はHTMLElement | nullを返す可能性があるが、
 //     ここでは確実にHTMLElementが存在することを開発者が保証している
 //   - もし要素が見つからない場合、実行時エラーが発生するので注意が必要
 //
 // createRoot():
 //   - React 18で導入された新しいAPIで、アプリケーションのルートを作成
 //   - この関数が返すルートオブジェクトを使って、Reactコンポーネントを描画する
 //   - 以前のReactDOM.render()と比べて、並行レンダリングなどの新機能が使える
 ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
   // React.StrictModeコンポーネントでアプリケーション全体を包む
   // StrictModeは、開発時にのみ動作する特殊なコンポーネントで、以下の機能を提供する:
   //
   // 1. 潜在的な問題の検出:
   //    - 非推奨のライフサイクルメソッドの使用を警告
   //    - レガシーなAPI (findDOMNode等) の使用を警告
   //    - 予期しない副作用 (side effects) を検出
   //
   // 2. 2重レンダリング:
   //    - コンポーネントを意図的に2回レンダリングすることで、
   //      副作用の問題を早期に発見できる
   //    - 本番環境では、この2重レンダリングは行われない
   //
   // 3. より良いコードの習慣を促す:
   //    - 将来のReactのバージョンに対応しやすいコードを書くことを推奨
   //
   // StrictModeは画面に何も描画せず、子コンポーネントのチェックのみを行う
   // 開発環境でのみ動作し、本番ビルドでは完全に無効化されるため、パフォーマンスへの影響はない
   <React.StrictMode>
     {/* Appコンポーネントを描画 */}
     {/* このコンポーネントが、アプリケーションの実際の内容を表示する */}
     {/* App.tsxで定義されたすべてのUI要素が、ここから展開される */}
     <App />
   </React.StrictMode>,
 );


src/App.tsx : メインのReactコンポーネント

アプリケーションのメインとなるReactコンポーネントである。
コンポーネントはReactにおける部品のようなもので、画面の一部分や機能をまとめたものである。

Appコンポーネントは、最上位のコンポーネントであり、アプリケーション全体の構造を定義する。

このファイルでは、function App() のような関数定義があり、その中でJSXを使用して画面の構造が記述されている。
初期状態では、Tauriのロゴや簡単なカウンタのボタン等が含まれている。(動作確認のためのサンプルコード)

実際の開発では、App.tsxファイルに全てを記述するのではなく、機能ごとに複数のコンポーネントを作成して、それらをApp.tsxで組み合わせる。
例えば、ヘッダコンポーネント、サイドバーコンポーネント、メインコンテンツコンポーネント等を別々に作成して、Appコンポーネントでそれらを配置する。

 // Reactのフック (状態管理機能) をインポート
 // useStateは、コンポーネント内で動的に変化する値(状態)を管理するための関数
 // Reactでは、状態が変化すると自動的にコンポーネントが再描画される仕組みになっている
 import { useState } from "react";
 
 // Reactのロゴ画像をインポート
 // Viteでは、画像ファイルを通常のモジュールとしてインポートでき、
 // インポートされた値は画像のURLになる
 import reactLogo from "./assets/react.svg";
 
 // Tauriが提供するinvoke関数をインポート
 // invoke関数は、フロントエンド(React)からバックエンド(Rust)の関数を呼び出すための重要な機能
 // これにより、JavaScriptでは実現できない高速な処理やシステムレベルの操作をRustで実行できる
 import { invoke } from "@tauri-apps/api/core";
 
 // このコンポーネント専用のCSSファイルをインポート
 // インポートすることで、スタイルが自動的に適用される
 import "./App.css";
 
 // Appコンポーネントの定義
 // 関数コンポーネントは、ReactでUIを構築するための基本的な単位
 // この関数は、画面に表示される内容(JSX)を返す
 function App() {
   // greetMsgという状態変数と、それを更新するための関数setGreetMsgを定義
   // useState("")の引数""は初期値で、最初は空文字列が設定される
   // この状態は、Rustのgreet関数から返されたメッセージを保持する役割を持つ
   const [greetMsg, setGreetMsg] = useState("");
 
   // nameという状態変数と、それを更新するための関数setNameを定義
   // この状態は、ユーザーが入力フィールドに入力した名前を保持する
   const [name, setName] = useState("");
 
   // greet関数の定義
   // asyncキーワードにより、この関数は非同期関数となり、awaitが使用できる
   // 非同期処理は、処理が完了するまで待機する必要がある処理(ここではRustの関数呼び出し)に使用される
   async function greet() {
     // Tauriのinvoke関数を使って、Rust側で定義された"greet"コマンドを呼び出す
     // 公式ドキュメント: https://tauri.app/develop/calling-rust/
 
     // invoke関数の仕組み:
     // 第1引数: 呼び出したいRust関数の名前(文字列)
     // 第2引数: Rust関数に渡す引数をオブジェクト形式で指定
     //          ここでは、{ name }という省略記法を使っている({ name: name }と同じ意味)
 
     // awaitキーワードにより、Rust関数の実行が完了するまで待機する
     // Rust関数から返された値は、自動的にJavaScript/TypeScriptの値に変換される
     // 返された値をsetGreetMsg関数に渡すことで、greetMsg状態を更新する
     // 状態が更新されると、Reactは自動的にコンポーネントを再描画し、新しいメッセージが画面に表示される
     setGreetMsg(await invoke("greet", { name }));
   }
 
   // コンポーネントが返すJSX(JavaScriptに埋め込まれたHTML風の構文)
   // このJSXが実際に画面に描画される内容を定義している
   return (
     // main要素にcontainerクラスを適用
     // このクラスはApp.cssで定義されており、中央揃えのレイアウトを提供する
     <main className="container">
       {/* 見出し(h1タグ) */}
       {/* Tauri + Reactアプリケーションへの歓迎メッセージ */}
       <h1>Welcome to Tauri + React</h1>
 
       {/* ロゴを横並びで表示するコンテナ */}
       <div className="row">
         {/* Viteのロゴとリンク */}
         {/* target="_blank"で新しいタブで開き、rel属性はセキュリティのために必要 */}
         <a href="https://vite.dev" target="_blank">
           {/* /vite.svgはpublicフォルダ内の画像を参照している */}
           {/* classNameはJSXでのクラス属性の指定方法(HTMLのclassに相当) */}
           {/* "logo vite"のように複数のクラスを空白区切りで指定できる */}
           <img src="/vite.svg" className="logo vite" alt="Vite logo" />
         </a>
 
         {/* Tauriのロゴとリンク */}
         <a href="https://tauri.app" target="_blank">
           <img src="/tauri.svg" className="logo tauri" alt="Tauri logo" />
         </a>
 
         {/* Reactのロゴとリンク */}
         <a href="https://react.dev" target="_blank">
           {/* reactLogoは上部でインポートした変数 */}
           {/* JSX内で変数を使う場合は、波括弧{}で囲む必要がある */}
           <img src={reactLogo} className="logo react" alt="React logo" />
         </a>
       </div>
 
       {/* 案内メッセージ */}
       <p>Click on the Tauri, Vite, and React logos to learn more.</p>
 
       {/* フォーム要素 */}
       {/* onSubmitイベントハンドラで、フォーム送信時の動作を定義 */}
       {/* アロー関数 (e) => {...} は、イベントオブジェクトeを受け取る関数 */}
       <form
         className="row"
         onSubmit={(e) => {
           // e.preventDefault()で、フォームのデフォルト動作(ページリロード)を防ぐ
           // これにより、シングルページアプリケーションの挙動を維持できる
           e.preventDefault();
 
           // greet関数を呼び出して、Rust側の処理を実行
           greet();
         }}
       >
         {/* テキスト入力フィールド */}
         <input
           id="greet-input"
 
           {/* onChangeイベントハンドラで、入力値が変更されるたびに実行される処理を定義 */}
           {/* e.currentTarget.valueで、入力フィールドの現在の値を取得 */}
           {/* setName関数を呼び出すことで、name状態を更新 */}
           {/* これにより、ユーザーの入力がリアルタイムで状態に反映される */}
           onChange={(e) => setName(e.currentTarget.value)}
 
           {/* プレースホルダーテキスト(入力フィールドが空のときに表示されるヒント) */}
           placeholder="Enter a name..."
         />
 
         {/* 送信ボタン */}
         {/* type="submit"により、このボタンをクリックするとフォームのonSubmitイベントが発火する */}
         <button type="submit">Greet</button>
       </form>
 
       {/* 挨拶メッセージの表示エリア */}
       {/* 波括弧{}内のgreetMsgは、状態変数の現在の値を表示する */}
       {/* greet関数が実行されると、この値が更新され、新しいメッセージが表示される */}
       {/* 最初は空文字列なので何も表示されないが、ボタンをクリックすると */}
       {/* Rust側から返されたメッセージがここに表示される */}
       <p>{greetMsg}</p>
     </main>
   );
 }
 
 // Appコンポーネントをエクスポート
 // export defaultにより、このコンポーネントが他のファイルからインポート可能になる
 // main.tsxファイルで、このコンポーネントがインポートされ、画面に描画される
 export default App;


src/App.css : Appコンポーネントのスタイル

App.tsxコンポーネントの見た目を定義するCSSファイルである。

App.tsxファイルでは、App.cssをインポートしている。

Reactでは、コンポーネントごとにスタイルシートを分離して管理することが一般的である。
"このスタイルはどのコンポーネントで使われているのか" が明確になり、保守性が上がる。

また、TailwindCSSのようなCSSフレームワークを使用する場合は、このファイルの役割が変わることもある。

src/vite-env.d.ts : Viteの型定義ファイル

TypeScriptの型定義ファイルである。
ファイル名の末尾にある「.d.ts」というのは、declaration type scriptの略で、型の宣言のみを記述するファイル という意味である。

Viteが提供する型定義を参照する指示が記述されている。
これにより、Vite特有の機能、環境変数の読み込み、モジュールのインポート等に使用する時に、TypeScriptのエディタ上で補完および型チェックができる。

このファイルは、基本的に編集する必要はない。プロジェクト作成時に自動生成され、そのまま使用し続けるものである。
ただし、Vite以外の特殊な型定義を追加する場合は、このファイルを編集することもある。

 // このファイルは、TypeScriptの型定義ファイル (declaration file) である
 // ファイル名の末尾にある「.d.ts」という拡張子は、「これは型の宣言だけを含むファイルですよ」という意味を持つ
 //
 // TypeScriptでは、JavaScriptのライブラリやツールを使う際に、それらがどのような型 (関数の引数や戻り値等) を持っているかを知る必要がある
 // この型情報を提供するのが型定義ファイルの役割である
 
 // /// <reference types="..." />
 // これは「トリプルスラッシュディレクティブ」と呼ばれる特殊なコメントである
 // TypeScriptコンパイラに対して、特定の型定義ファイルを参照するよう指示する役割を持つ
 //
 // この行により、Viteが提供するすべての型定義がプロジェクト全体で利用可能になる
 // 具体的には、以下のような機能の型情報が提供される:
 //
 // 1. 環境変数 (import.meta.env) の型定義
 //    - import.meta.env.VITEで始まる環境変数にアクセスする際、TypeScriptがこれらの変数の存在を認識し、適切な補完やエラーチェックを行える
 //
 // 2. 静的アセット (画像、SVG、CSSなど) のインポートに関する型定義
 //    - import logo from './logo.svg' のような記述をした時、TypeScriptが「SVGファイルをインポートしても大丈夫」だと理解できる
 //    - 通常、TypeScriptは画像ファイルをモジュールとして認識しないため、この型定義がないとエラーが発生してしまう
 //
 // 3. Vite独自の機能 (HMR、glob importなど) の型定義
 //    - import.meta.hot (ホットモジュールリプレースメント用のAPI)
 //    - import.meta.glob (複数ファイルの一括インポート)
 //    などの機能を使う際に、正しい型情報が提供される
 //
 // このファイルは通常、プロジェクト作成時に自動生成され、開発者が手動で編集する必要はほとんどない
 // ただし、独自の型定義を追加したい場合には、このファイルに追記することもできる
 //
 // 例えば、カスタムファイル形式 (.glslや.wasmなど) をインポートしたい場合、このファイルに以下のような型定義を追加することができる:
 //
 // declare module '*.glsl' {
 //   const content: string;
 //   export default content;
 // }
 //
 // このように、プロジェクト固有の型定義を一元管理する場所としても機能する
 /// <reference types="vite/client" />



IDE

IDEA Ultimate

基本設定

IDEA Ultimateでは、プロジェクトのルートディレクトリを開く。

Tauriプロジェクトは、2つの異なるプロジェクトを組み合わせた構造である。
2つを統合的に管理するための設定ファイルや情報が、全てルートディレクトリに存在する。

  • Reactを使用したフロントエンドのプロジェクト
  • Rustで記述したバックエンドのプロジェクト


ルートディレクトリには、package.jsonというNode.jsプロジェクトの設定ファイルが存在する。
IDEA Ultimateはこれらのファイルを発見した時、Node.jsのプロジェクトと理解して適切なJavaScriptやReactの開発サポート機能を有効にする。
同時に、src-tauriディレクトリの中にあるCargo.toml (Rustプロジェクトの設定ファイル) も認識して、Rustのソースコードサポートも提供する。

ルートディレクトリを開く時、IDEA Ultimateは次のように動作する。

  1. まず、プロジェクトを開いた直後、IDEA Ultimateは自動的に依存関係を解析する。
  2. 解析が完了した後、srcディレクトリ内のReactコンポーネントを編集する場合は、Reactの関数やフックの補完が利用できるようになる。
  3. 同様に、src-tauriディレクトリのRustコードを編集する場合は、Rustの型情報や関数の補完が利用できるようになる。


IDEA UltimateのみでRust開発を行う場合は、Rustプラグイン (Rust) のインストールが必要である。
プラグインのインストール手順は、以下の通りである。

  1. [ファイル]メインメニュー - [設定]を選択する。
  2. 左側のメニューから[Plugins]を選択する。
  3. 上部の検索バーに Rust と入力する。
  4. 検索結果に表示されるRustプラグインをインストールする。
  5. インストール後、IDEを再起動する。


Rustプラグインをインストールすると、以下に示す機能が利用可能になる。

  • シンタックスハイライト
  • コード補完機能
  • Cargoコマンドの統合により、IDE内から直接ビルドや実行が可能になる。


任意でTauriプラグインもインストール可能である。
このプラグインは、Tauriのコマンド定義やコンフィグファイルの編集をサポートする。

プロジェクト構成

プロジェクトは以下に示す構造である。

  • Rustソースコード
    src-tauri/src
  • ビルド出力
    src-tauri/targe


Rustツールチェーンの設定
  1. [ファイル] - [設定]を開く。
  2. [言語 & フレームワーク] - [Rust]を選択する。
  3. [ツールチェーンの場所]にて、RustおよびCargoのパスを設定する。
    設定例 : $USER_HOME$/cargo/bin


実行 / デバッグ構成の作成

[実行]メニューバー - [構成の編集]を選択する。

まず、JavaScriptデバッグ構成を作成する。

  1. [+]アイコン - [JavaScriptデバッグ]を選択する。
  2. [名前]欄に、任意の名前を入力する。
  3. [URL]欄に、http://localhost:1420 (Tauriのデフォルトポート) を入力する。
  4. [ブラウザー]欄に、Chromium系ブラウザを選択する。
  5. [OK]ボタンを押下する。


次に、npm実行構成 (フロントエンド) を作成する。

  1. 画面右ペインにある[+]アイコン - [npm]を選択する。
  2. [名前]欄に、任意の名前を入力する。
  3. [package.json]欄に、プロジェクトルートの package.json ファイルを選択する。
  4. [コマンド]プルダウンで、run を選択する。
  5. [スクリプト]プルダウンで、dev を選択する。
  6. [OK]ボタンを押下する。


最後に、複合構成 (フルスタックデバッグ) を作成する。
複合構成を使用すると、フロントエンドとバックエンドを同時にデバッグすることができる。

  • Tauri + React + Rust構成 (フロントエンド + バックエンド構成)
    1. 画面左ペインにある[+]アイコン - [複合]を選択する。
    2. [名前]欄に、任意の名前を入力する。
    3. 画面右ペインにある[+]アイコンを選択して、以下に示す設定を追加する。
      • Run tauri-app
      • JavaScript Debug
      • dev
    4. [OK]ボタンを押下する。

  • Tauri + React構成 (フロントエンドのみ)
    1. 画面左ペインにある[+]アイコン - [複合]を選択する。
    2. [名前]欄に、任意の名前を入力する。
    3. 画面右ペインにある[+]アイコンを選択して、以下に示す設定を追加する。
      • JavaScriptデバッグ
      • dev
    4. [OK]ボタンを押下する。

  • Tauri + Rust構成 (バックエンドのみ)
    1. 画面左ペインにある[+]アイコン - [複合]を選択する。
    2. [名前]欄に、任意の名前を入力する。
    3. 画面右ペインにある[+]アイコンを選択して、以下に示す設定を追加する。
      • Run tauri-app
      • dev
    4. [OK]ボタンを押下する。


ログポイント
  1. ブレークポイントを右クリック - [More]を選択する。
  2. [Breakpoint hit]メッセージをカスタマイズする。
  3. [Suspend]チェックボックスのチェックを外す場合、停止せずにログだけ出力する。


監視式 (Watches)
  1. デバッガーツールウィンドウの[Watches]タブを選択する。
  2. [+]アイコンを押下して、新しい監視式を追加する。
  3. 任意の式を入力する。
    例: vec.len()
  4. デバッグ時に常に評価結果が表示される。


メモリビュー
  1. デバッグ中に変数を右クリック - [View Memory]を選択する。
  2. バイナリデータを確認することができる。


RustRover

RustRoverでは、プロジェクトのルートディレクトリを開く。

RustRoverは、JetBrainsが提供するRust専用の統合開発環境である。
IDEA Ultimateと同じJetBrainsファミリーの製品だが、Rustに特化して設計されている。

ルートディレクトリを開くと、RustRoverはsrc-tauriディレクトリ内のCargo.tomlを発見し、そのディレクトリをRustプロジェクトの中心として認識する。

Rustのソースコードに対しては、IDEA Ultimateよりもさらに高度な解析機能が提供される。
マクロの展開結果の表示、詳細な型推論、Cargoコマンドの統合実行環境等、Rust開発に特化した機能が豊富に用意されている。

RustRoverはReactやTypeScriptのサポートについては、IDEA Ultimateほど充実しておらず、
基本的なJavaScriptやTypeScriptの編集はできるが、Reactコンポーネントの補完やリファクタリング機能等は限定的である。

実務では、以下に示すような使い分けが推奨される。
フロントエンドとバックエンドの両方を頻繁に行き来する場合は、IDEA Ultimateを使用する。
これは、1つのIDEウィンドウで両方のコードを快適に編集できるためである。
しかし、Rustのソースコードを記述する時間が長い場合や複雑なRustのソースコードを扱う場合は、RustRoverを使用する。