上級 dom

Wasm + フロントエンドフレームワーク

パターン

Wasmは計算処理に、フレームワークはUIに使います:

┌─────────────────┐
│  React/Svelte   │  ← UIレンダリング、状態、イベント
│  (JavaScript)   │
├─────────────────┤
│  Wasmモジュール  │  ← 重い計算処理、データ処理
│  (Rust)         │
└─────────────────┘

React統合

// hooks/useWasm.ts
import { useState, useEffect } from 'react';

export function useWasm() {
  const [wasm, setWasm] = useState(null);

  useEffect(() => {
    async function load() {
      const module = await import('../pkg/my_wasm.js');
      await module.default();  // init
      setWasm(module);
    }
    load();
  }, []);

  return wasm;
}

// components/ImageEditor.tsx
function ImageEditor() {
  const wasm = useWasm();
  const [processing, setProcessing] = useState(false);

  async function applyBlur() {
    if (!wasm) return;
    setProcessing(true);

    const processor = new wasm.ImageProcessor(800, 600);
    const imageData = ctx.getImageData(0, 0, 800, 600);
    processor.blur(imageData.data, 3);
    ctx.putImageData(imageData, 0, 0);

    setProcessing(false);
  }

  return (
    <button onClick={applyBlur} disabled={!wasm || processing}>
      {processing ? 'Processing...' : 'Apply Blur'}
    </button>
  );
}

Svelte統合

<!-- WasmProcessor.svelte -->
<script lang="ts">
  import { onMount } from 'svelte';

  let wasm: any = null;
  let ready = false;

  onMount(async () => {
    const module = await import('../pkg/my_wasm.js');
    await module.default();
    wasm = module;
    ready = true;
  });

  function processData() {
    if (!wasm) return;
    const result = wasm.process(inputData);
    // 結果でUIを更新
  }
</script>

{#if ready}
  <button on:click={processData}>Process</button>
{:else}
  <p>Wasmを読み込み中...</p>
{/if}

Vue統合

<!-- WasmComponent.vue -->
<script setup>
import { ref, onMounted } from 'vue';

const wasm = ref(null);
const ready = ref(false);

onMounted(async () => {
  const module = await import('../pkg/my_wasm.js');
  await module.default();
  wasm.value = module;
  ready.value = true;
});

function compute() {
  if (!wasm.value) return;
  const result = wasm.value.heavy_calculation(data);
}
</script>

<template>
  <button @click="compute" :disabled="!ready">Compute</button>
</template>

Vite設定

// vite.config.ts
import { defineConfig } from 'vite';

export default defineConfig({
  optimizeDeps: {
    exclude: ['my-wasm-app']  // Wasmをプリバンドルしない
  },
  server: {
    headers: {
      // SharedArrayBufferに必要(オプション)
      'Cross-Origin-Opener-Policy': 'same-origin',
      'Cross-Origin-Embedder-Policy': 'require-corp',
    }
  }
});

Wasmをフレームワークと使うべき場面

Wasmに適した処理 JS/フレームワークに残すべき処理
画像・動画処理 UIコンポーネント、ルーティング
物理シミュレーション 状態管理(Reduxなど)
暗号処理 フォーム処理、バリデーション
データパース(CSV、バイナリ) DOM操作
オーディオ処理 イベントハンドリング
圧縮・解凍 HTTPリクエスト(パフォーマンスが重要でない場合)

経験則: CPU集約的でJavaScriptで10ms以上かかる処理は、Wasmに移行しましょう。

試してみよう

スターターコードは、任意のフレームワークで動作するImageProcessor構造体を示しています。生のピクセルデータを受け取り、Rustで処理します。

試してみる

チャプタークイズ

すべての問題に正解してレッスンを完了しましょう