T-CREATOR

SolidJS とは?超高速フロントエンドの全貌を 3 分で解説

SolidJS とは?超高速フロントエンドの全貌を 3 分で解説

フロントエンド開発の世界では、React、Vue、Angular といった確立されたフレームワークが長らく主流でした。しかし、ここ数年で新たな選択肢が登場しています。それが SolidJS です。「また新しいフレームワークか...」と思われるかもしれませんが、SolidJS は従来のフレームワークとは根本的に異なるアプローチを採用し、驚異的なパフォーマンスを実現しています。今回は、この SolidJS の全貌を 3 分で理解できるよう、わかりやすく解説していきます。

SolidJS とは何か

フロントエンドフレームワークの新星

SolidJS は、2021 年に Ryan Carniato 氏によって開発されたモダンな JavaScript フレームワークです。React ライクな記法を採用しながらも、Virtual DOM を使用せず、細粒度リアクティビティ(Fine-grained Reactivity)という独自の仕組みを採用しています。

SolidJS の最大の特徴は、その名前の通り「solid(堅実)」なパフォーマンスです。多くのベンチマークテストで、React や Vue を上回る実行速度を記録しており、特にランタイムパフォーマンスでは他の追随を許さない結果を示しています。

typescript// SolidJSの基本的な記法例
import { createSignal } from 'solid-js';

function Counter() {
  const [count, setCount] = createSignal(0);

  return (
    <div>
      <p>カウント: {count()}</p>
      <button onClick={() => setCount(count() + 1)}>
        増加
      </button>
    </div>
  );
}

上記のコードを見ると、React を知っている方なら馴染みやすい記法だとお分かりいただけるでしょう。

他のフレームワークとの違い

SolidJS と他の主要フレームワークとの主な違いを表にまとめました。

#項目ReactVue 3SolidJS
1Virtual DOM使用使用不使用
2バンドルサイズ42.2KB33.7KB7.3KB
3初回レンダリング普通速い非常に速い
4更新性能普通速い非常に速い
5学習コスト中程度中程度低い
6TypeScript 対応良好良好標準対応

この表からも分かるように、SolidJS は特にパフォーマンス面で圧倒的な優位性を持っています。

なぜ SolidJS が「超高速」なのか

リアクティブシステムの革新

SolidJS の高速性の秘密は、その独自のリアクティブシステムにあります。従来の Virtual DOM ベースのフレームワークでは、状態が変更されるたびに仮想的な DOM 構造全体を再計算し、実際の DOM との差分を検出して更新を行います。

一方、SolidJS は細粒度リアクティビティを採用しています。これは、状態の変更が発生した際に、その変更に依存する最小限の部分のみを直接更新する仕組みです。

typescript// SolidJSの細粒度リアクティビティの例
import { createSignal, createMemo } from 'solid-js';

function ReactiveExample() {
  const [firstName, setFirstName] = createSignal('田中');
  const [lastName, setLastName] = createSignal('太郎');

  // firstNameまたはlastNameが変更された時のみ再計算される
  const fullName = createMemo(
    () => `${firstName()} ${lastName()}`
  );

  return (
    <div>
      <p>フルネーム: {fullName()}</p>
      <button onClick={() => setFirstName('鈴木')}>
        名前を変更
      </button>
    </div>
  );
}

このコードでは、firstNameまたはlastNameが変更された時のみfullNameが再計算され、他の部分には一切影響を与えません。

バンドルサイズの最適化

SolidJS のバンドルサイズは、圧縮後わずか 7.3KB です。これは React の約 6 分の 1 という驚異的な軽量さです。

json{
  "name": "solid-app-example",
  "version": "1.0.0",
  "dependencies": {
    "solid-js": "^1.8.0"
  },
  "devDependencies": {
    "vite": "^5.0.0",
    "vite-plugin-solid": "^2.8.0"
  }
}

このように、SolidJS は依存関係も最小限に抑えられており、プロジェクトの軽量化に大きく貢献します。

ランタイムパフォーマンスの秘密

SolidJS がランタイムで高速に動作する理由は、コンパイル時の最適化にあります。SolidJS はビルド時にコードを分析し、リアクティブな依存関係を事前に構築します。

typescript// コンパイル前のSolidJSコード
function TodoItem(props) {
  return (
    <div class={props.completed ? 'completed' : ''}>
      <span>{props.text}</span>
    </div>
  );
}

// コンパイル後(簡略化)
function TodoItem(props) {
  const _el$ = document.createElement('div');
  const _el$2 = document.createElement('span');

  // リアクティブな更新のみを設定
  createEffect(
    () =>
      (_el$.className = props.completed ? 'completed' : '')
  );
  createEffect(() => (_el$2.textContent = props.text));

  return _el$;
}

このコンパイル時最適化により、ランタイムでのオーバーヘッドが大幅に削減されます。

SolidJS の特徴と利点

シンプルな記法

SolidJS の記法は、React を知っている開発者なら直感的に理解できます。しかし、よりシンプルで直接的な表現が可能です。

typescript// React風の記法だが、より直接的
import { createSignal, For } from 'solid-js';

function TodoList() {
  const [todos, setTodos] = createSignal([
    { id: 1, text: 'SolidJSを学ぶ', completed: false },
    { id: 2, text: 'アプリを作る', completed: false },
  ]);

  const [newTodo, setNewTodo] = createSignal('');

  const addTodo = () => {
    if (newTodo().trim()) {
      setTodos([
        ...todos(),
        {
          id: Date.now(),
          text: newTodo(),
          completed: false,
        },
      ]);
      setNewTodo('');
    }
  };

  return (
    <div class='todo-app'>
      <h1>SolidJS Todo App</h1>
      <div class='input-group'>
        <input
          type='text'
          value={newTodo()}
          onInput={(e) => setNewTodo(e.target.value)}
          placeholder='新しいTodoを入力'
        />
        <button onClick={addTodo}>追加</button>
      </div>

      <ul class='todo-list'>
        <For each={todos()}>
          {(todo) => (
            <li class={todo.completed ? 'completed' : ''}>
              <span>{todo.text}</span>
              <button
                onClick={() => {
                  setTodos(
                    todos().map((t) =>
                      t.id === todo.id
                        ? { ...t, completed: !t.completed }
                        : t
                    )
                  );
                }}
              >
                {todo.completed ? '戻す' : '完了'}
              </button>
            </li>
          )}
        </For>
      </ul>
    </div>
  );
}

TypeScript 標準対応

SolidJS は TypeScript で開発されており、型安全性が標準で提供されています。追加の設定なしで、優れた型推論と IntelliSense のサポートを受けられます。

typescript// 型安全なSolidJSコンポーネントの例
import { createSignal, Component } from 'solid-js';

// Propsの型定義
interface UserCardProps {
  user: {
    id: number;
    name: string;
    email: string;
    isActive: boolean;
  };
  onUserClick: (userId: number) => void;
}

const UserCard: Component<UserCardProps> = (props) => {
  const [isHovered, setIsHovered] = createSignal(false);

  return (
    <div
      class={`user-card ${
        props.user.isActive ? 'active' : 'inactive'
      } ${isHovered() ? 'hovered' : ''}`}
      onMouseEnter={() => setIsHovered(true)}
      onMouseLeave={() => setIsHovered(false)}
      onClick={() => props.onUserClick(props.user.id)}
    >
      <h3>{props.user.name}</h3>
      <p>{props.user.email}</p>
      <span class='status'>
        {props.user.isActive
          ? 'アクティブ'
          : '非アクティブ'}
      </span>
    </div>
  );
};

export default UserCard;

学習コストの低さ

React や Vue の経験がある開発者なら、SolidJS は比較的短期間で習得できます。基本的な概念は似ているため、既存の知識を活用しながら新しい機能を学べます。

実際に動かしてみよう

環境構築

SolidJS のプロジェクトを開始する最も簡単な方法は、公式のスターターテンプレートを使用することです。

bash# Yarnを使用してSolidJSプロジェクトを作成
yarn create solid

# プロジェクトディレクトリに移動
cd my-solid-app

# 依存関係をインストール
yarn install

# 開発サーバーを起動
yarn dev

この時、もし以下のようなエラーが発生した場合:

bashError: Cannot resolve dependency tree
npm ERR! ERESOLVE unable to resolve dependency tree

これは、Node.js のバージョンが古い可能性があります。Node.js 16 以上を使用してください:

bash# Node.jsバージョンを確認
node --version

# v16.0.0以上であることを確認

Hello World アプリの作成

まず、シンプルな Hello World アプリから始めましょう。

typescript// src/App.tsx
import { createSignal } from 'solid-js';

function App() {
  const [name, setName] = createSignal('World');

  return (
    <div class='app'>
      <h1>Hello, {name()}!</h1>
      <input
        type='text'
        value={name()}
        onInput={(e) => setName(e.target.value)}
        placeholder='名前を入力してください'
      />
      <p>
        入力された名前: <strong>{name()}</strong>
      </p>
    </div>
  );
}

export default App;
typescript// src/index.tsx
import { render } from 'solid-js/web';
import App from './App';

render(() => <App />, document.getElementById('root')!);

基本的なコンポーネントの書き方

SolidJS では、コンポーネントは関数として定義します。以下は、より実践的なコンポーネントの例です。

typescript// src/components/WeatherWidget.tsx
import {
  createSignal,
  createResource,
  Show,
} from 'solid-js';

// 天気データの型定義
interface WeatherData {
  location: string;
  temperature: number;
  condition: string;
  humidity: number;
}

// 天気データを取得する関数(モック)
const fetchWeatherData = async (
  city: string
): Promise<WeatherData> => {
  // 実際のAPIコールをシミュレート
  await new Promise((resolve) => setTimeout(resolve, 1000));

  // エラーシミュレーション
  if (city === 'error') {
    throw new Error('Weather data not found for this city');
  }

  return {
    location: city,
    temperature: Math.floor(Math.random() * 35) + 5,
    condition: ['晴れ', '曇り', '雨'][
      Math.floor(Math.random() * 3)
    ],
    humidity: Math.floor(Math.random() * 50) + 30,
  };
};

const WeatherWidget = () => {
  const [city, setCity] = createSignal('東京');

  // createResourceを使用してデータフェッチング
  const [weatherData] = createResource(
    city,
    fetchWeatherData
  );

  return (
    <div class='weather-widget'>
      <h2>天気情報</h2>

      <div class='city-input'>
        <label>都市名:</label>
        <input
          type='text'
          value={city()}
          onInput={(e) => setCity(e.target.value)}
          placeholder='都市名を入力'
        />
      </div>

      <Show
        when={!weatherData.loading}
        fallback={
          <div class='loading'>
            天気データを読み込み中...
          </div>
        }
      >
        <Show
          when={!weatherData.error}
          fallback={
            <div class='error'>
              エラー: {weatherData.error?.message}
              <br />
              <small>都市名を確認してください</small>
            </div>
          }
        >
          <div class='weather-info'>
            <h3>{weatherData()?.location}</h3>
            <div class='temperature'>
              {weatherData()?.temperature}°C
            </div>
            <div class='condition'>
              {weatherData()?.condition}
            </div>
            <div class='humidity'>
              湿度: {weatherData()?.humidity}%
            </div>
          </div>
        </Show>
      </Show>
    </div>
  );
};

export default WeatherWidget;
css/* src/styles/WeatherWidget.css */
.weather-widget {
  max-width: 400px;
  margin: 20px auto;
  padding: 20px;
  border: 1px solid #ddd;
  border-radius: 8px;
  background-color: #f9f9f9;
}

.city-input {
  margin-bottom: 20px;
}

.city-input label {
  display: block;
  margin-bottom: 5px;
  font-weight: bold;
}

.city-input input {
  width: 100%;
  padding: 8px;
  border: 1px solid #ccc;
  border-radius: 4px;
}

.loading {
  text-align: center;
  color: #666;
  font-style: italic;
}

.error {
  background-color: #ffebee;
  color: #c62828;
  padding: 10px;
  border-radius: 4px;
  border-left: 4px solid #c62828;
}

.weather-info {
  text-align: center;
}

.temperature {
  font-size: 2.5em;
  font-weight: bold;
  color: #2196f3;
}

.condition {
  font-size: 1.2em;
  margin: 10px 0;
  color: #666;
}

.humidity {
  color: #888;
}

エラーハンドリングの例として、無効な都市名を入力した場合のエラー表示も実装しています。上記のコードで「error」と入力すると、以下のようなエラーメッセージが表示されます:

kotlinError: Weather data not found for this city

このように、SolidJS ではShowコンポーネントと組み合わせて、条件付きレンダリングを簡潔に記述できます。

まとめ

SolidJS は、フロントエンド開発における新たな選択肢として、非常に魅力的なフレームワークです。

SolidJS の主な利点:

#利点詳細
1圧倒的なパフォーマンスVirtual DOM 不使用による高速レンダリング
2軽量なバンドルサイズ7.3KB という驚異的な軽さ
3学習コストの低さReact 経験者なら短期間で習得可能
4TypeScript 標準対応追加設定不要の型安全性
5細粒度リアクティビティ必要最小限の更新による効率性

特に、パフォーマンスが重要なアプリケーションや、バンドルサイズを極限まで削減したいプロジェクトでは、SolidJS の導入を強く検討する価値があります。

React や Vue の豊富なエコシステムと比較すると、SolidJS はまだ発展途上の部分もありますが、そのコアとなる技術的優位性は非常に印象的です。今後の Web アプリケーション開発において、SolidJS は間違いなく重要な選択肢の一つとなるでしょう。

ぜひ、実際に SolidJS を触ってみて、その高速性と開発体験の良さを体感してみてください。きっと、フロントエンド開発に対する新たな視点を得られるはずです。

関連リンク