Dioxus実践ガイド2025 - Rust製フルスタックフレームワークで次世代アプリ開発
Rustで構築されたクロスプラットフォームフレームワークDioxusの完全ガイド。React風のアプローチでWeb、デスクトップ、モバイルアプリを単一コードベースで開発する実践的な手法を詳しく解説します。
Tauri 2.0はRustベースの革新的なクロスプラットフォームフレームワーク。Electronの1/10のサイズで90%高速、iOS/Android対応でモバイルも制覇。WebViewを活用した軽量アーキテクチャと最高レベルのセキュリティで、次世代アプリ開発を実現する完全ガイド。
2024 年 10 月にリリースされた Tauri 2.0 は、デスクトップアプリ開発に革命をもたらしました。Electronの重さに悩む開発者にとって、Tauri は軽量・高速・セキュアな理想的な代替となっています。さらに iOS/Android サポートにより、真のクロスプラットフォーム開発が可能になりました。
Tauri 2.0 は単なるアップデートではなく、アプリケーション開発の新しいパラダイムを提示しています。
チャートを読み込み中...
モバイルサポート、新権限システム導入
公式プラグイン30種類以上
Rust製ブラウザエンジンとの連携
エンタープライズ採用急増中
# Rust のインストール
winget install Rustlang.Rust
# 必要な依存関係
npm install -g @tauri-apps/cli
# Visual Studio Build Tools も必要
# https://visualstudio.microsoft.com/visual-cpp-build-tools/
# Rust のインストール
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Xcode Command Line Tools
xcode-select --install
# Tauri CLI
npm install -g @tauri-apps/cli
# 依存関係のインストール
sudo apt update
sudo apt install libwebkit2gtk-4.0-dev \
build-essential \
curl \
wget \
file \
libssl-dev \
libgtk-3-dev \
libayatana-appindicator3-dev \
librsvg2-dev
# Rust のインストール
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# インタラクティブなプロジェクト作成
npm create tauri-app@latest my-app
# テンプレート選択
? Choose which language to use for your frontend
› TypeScript / JavaScript (pnpm, yarn, npm, bun)
Rust (cargo)
? Choose your package manager
› pnpm
? Choose your UI template
› React (TypeScript)
Vue (TypeScript)
Svelte (TypeScript)
Solid (TypeScript)
Angular (TypeScript)
Vanilla (TypeScript)
// src/App.tsx
import { invoke } from '@tauri-apps/api/tauri';
import { useState } from 'react';
function App() {
const [greeting, setGreeting] = useState('');
const [name, setName] = useState('');
async function greet() {
// Rustの関数を呼び出し
const response = await invoke<string>('greet', {
name
});
setGreeting(response);
}
return (
<div>
<input
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="名前を入力..."
/>
<button onClick={greet}>挨拶</button>
<p>{greeting}</p>
</div>
);
}
// src-tauri/src/main.rs
use tauri::Manager;
#[tauri::command]
fn greet(name: &str) -> String {
format!("こんにちは、{}さん!Tauriからの挨拶です。", name)
}
fn main() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![greet])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
// src/App.tsx
import { invoke } from '@tauri-apps/api/tauri';
import { useState } from 'react';
function App() {
const [greeting, setGreeting] = useState('');
const [name, setName] = useState('');
async function greet() {
// Rustの関数を呼び出し
const response = await invoke<string>('greet', {
name
});
setGreeting(response);
}
return (
<div>
<input
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="名前を入力..."
/>
<button onClick={greet}>挨拶</button>
<p>{greeting}</p>
</div>
);
}
// src-tauri/src/main.rs
use tauri::Manager;
#[tauri::command]
fn greet(name: &str) -> String {
format!("こんにちは、{}さん!Tauriからの挨拶です。", name)
}
fn main() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![greet])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
use std::fs;
use tauri::Manager;
#[tauri::command]
async fn read_file(path: String) -> Result<String, String> {
fs::read_to_string(&path)
.map_err(|e| e.to_string())
}
#[tauri::command]
async fn write_file(path: String, contents: String) -> Result<(), String> {
fs::write(&path, contents)
.map_err(|e| e.to_string())
}
#[tauri::command]
async fn list_directory(path: String) -> Result<Vec<String>, String> {
let entries = fs::read_dir(&path)
.map_err(|e| e.to_string())?;
let mut files = Vec::new();
for entry in entries {
if let Ok(entry) = entry {
if let Some(name) = entry.file_name().to_str() {
files.push(name.to_string());
}
}
}
Ok(files)
}
Tauri 2.0 では、より細かい権限制御が可能になりました:
// tauri.conf.json
{
"tauri": {
"security": {
"capabilities": [
{
"identifier": "main",
"description": "メインウィンドウの権限",
"windows": ["main"],
"permissions": [
"fs:read",
"fs:write",
{
"identifier": "fs:scope",
"allow": ["$APPDATA/*", "$DOCUMENT/*"],
"deny": ["$HOME/.ssh/*"]
}
]
}
]
}
}
}
# iOS開発環境のセットアップ
npm run tauri ios init
# 開発用ビルド
npm run tauri ios dev
# リリースビルド
npm run tauri ios build
Swift連携の例:
// ios/Sources/MyAppPlugin.swift
import Tauri
import UIKit
class MyAppPlugin: Plugin {
@objc public func showNativeAlert(_ call: Invoke) {
let title = call.getString("title") ?? "Alert"
let message = call.getString("message") ?? ""
DispatchQueue.main.async {
let alert = UIAlertController(
title: title,
message: message,
preferredStyle: .alert
)
alert.addAction(UIAlertAction(
title: "OK",
style: .default
))
self.presentViewController(alert)
call.resolve(["success": true])
}
}
}
// android/app/src/main/java/com/myapp/MyAppPlugin.kt
package com.myapp
import app.tauri.annotation.Command
import app.tauri.annotation.TauriPlugin
import app.tauri.plugin.Plugin
import app.tauri.plugin.Invoke
@TauriPlugin
class MyAppPlugin(private val activity: Activity): Plugin(activity) {
@Command
fun vibrate(invoke: Invoke) {
val duration = invoke.getLong("duration") ?: 100L
val vibrator = activity.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
vibrator.vibrate(
VibrationEffect.createOneShot(duration, VibrationEffect.DEFAULT_AMPLITUDE)
)
} else {
@Suppress("DEPRECATION")
vibrator.vibrate(duration)
}
invoke.resolve(mapOf("success" to true))
}
}
項目 | Tauri 2.0 | Electron | 差異 |
---|---|---|---|
バンドルサイズ | 3-10MB | 50MB以上 | 80-95%削減 |
メモリ使用量 | 〜50MB | 〜300MB | 83%削減 |
起動時間 | 〜0.5秒 | 〜2秒 | 75%高速 |
セキュリティ | Rust製・サンドボックス | Chromiumベース | 優位 |
開発言語 | Rust + Web技術 | JavaScript/Node.js | 学習曲線あり |
プラットフォーム | デスクトップ + モバイル | デスクトップのみ | Tauri優位 |
WebView | OS標準 | Chromium同梱 | 軽量 |
// Electronからの移行例
// Before (Electron)
const { app, BrowserWindow, ipcMain } = require('electron');
ipcMain.handle('read-file', async (event, path) => {
return fs.readFileSync(path, 'utf-8');
});
// After (Tauri)
// main.rs
#[tauri::command]
async fn read_file(path: String) -> Result<String, String> {
std::fs::read_to_string(path)
.map_err(|e| e.to_string())
}
// フロントエンド
import { invoke } from '@tauri-apps/api/tauri';
const content = await invoke('read_file', { path });
use tauri::{
CustomMenuItem, Manager, SystemTray, SystemTrayEvent,
SystemTrayMenu, SystemTrayMenuItem,
};
fn main() {
let quit = CustomMenuItem::new("quit".to_string(), "終了");
let hide = CustomMenuItem::new("hide".to_string(), "隠す");
let tray_menu = SystemTrayMenu::new()
.add_item(hide)
.add_native_item(SystemTrayMenuItem::Separator)
.add_item(quit);
let system_tray = SystemTray::new()
.with_menu(tray_menu);
tauri::Builder::default()
.system_tray(system_tray)
.on_system_tray_event(|app, event| match event {
SystemTrayEvent::LeftClick {
position: _,
size: _,
..
} => {
let window = app.get_window("main").unwrap();
window.show().unwrap();
}
SystemTrayEvent::MenuItemClick { id, .. } => {
match id.as_str() {
"quit" => {
std::process::exit(0);
}
"hide" => {
let window = app.get_window("main").unwrap();
window.hide().unwrap();
}
_ => {}
}
}
_ => {}
})
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
// カスタムプラグインの作成
use tauri::{
plugin::{Builder, TauriPlugin},
Manager, Runtime,
};
pub fn init<R: Runtime>() -> TauriPlugin<R> {
Builder::new("my-plugin")
.invoke_handler(tauri::generate_handler![
my_custom_command
])
.setup(|app| {
// プラグインの初期化処理
println!("My Plugin initialized!");
Ok(())
})
.build()
}
#[tauri::command]
async fn my_custom_command() -> String {
"Custom plugin response".to_string()
}
// main.rs での使用
fn main() {
tauri::Builder::default()
.plugin(my_plugin::init())
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
# Cargo.toml
[profile.release]
opt-level = "z" # サイズ最適化
lto = true # Link Time Optimization
codegen-units = 1 # 単一コード生成ユニット
strip = true # シンボルを削除
panic = "abort" # パニック時の巻き戻しを無効化
[dependencies]
# 必要な機能のみを選択
tauri = { version = "2.0", features = ["dialog", "fs", "notification"] }
// 遅延読み込みの実装
// App.tsx
import { lazy, Suspense } from 'react';
const HeavyComponent = lazy(() => import('./components/HeavyComponent'));
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<HeavyComponent />
</Suspense>
);
}
// tauri.conf.json
{
"tauri": {
"security": {
"csp": {
"default-src": "'self'",
"script-src": "'self' 'unsafe-inline'",
"style-src": "'self' 'unsafe-inline'",
"img-src": "'self' data: https:",
"connect-src": "'self' https://api.example.com"
},
"dangerousDisableAssetCspModification": false,
"freezePrototype": true
}
}
}
use tauri::updater::builder::UpdateBuilder;
#[tauri::command]
async fn check_update(app_handle: tauri::AppHandle) -> Result<bool, String> {
let update = UpdateBuilder::new()
.current_version(app_handle.package_info().version.clone())
.url("https://api.example.com/update/manifest.json")
.build()
.map_err(|e| e.to_string())?;
if let Some(update) = update {
update.download_and_install().await
.map_err(|e| e.to_string())?;
Ok(true)
} else {
Ok(false)
}
}
# .github/workflows/release.yml
name: Release
on:
push:
tags:
- 'v*'
jobs:
release:
strategy:
matrix:
platform: [macos-latest, ubuntu-latest, windows-latest]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Install dependencies (Ubuntu)
if: matrix.platform == 'ubuntu-latest'
run: |
sudo apt-get update
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev
- name: Build Tauri App
run: |
npm install
npm run tauri build
- name: Upload artifacts
uses: actions/upload-artifact@v3
with:
name: ${{ matrix.platform }}
path: src-tauri/target/release/bundle/
Tauri 2.0 は、2025 年のクロスプラットフォーム開発における最適解の 1 つです。
選択基準 | Tauriが最適 | Electronが最適 |
---|---|---|
パフォーマンス重視 | ✓ | |
小さいバンドルサイズ | ✓ | |
モバイル対応必須 | ✓ | |
高度なセキュリティ | ✓ | |
Node.jsエコシステム | ✓ | |
Chromium固有機能 | ✓ | |
既存Electronアプリ | ✓ |
Tauri 2.0 は、軽量・高速・セキュアなアプリケーション開発を可能にし、特にリソース制約のある環境やモバイル展開を考慮する場合に最適な選択肢となっています。