SolidJS のルーティング:solid-app-router 徹底解説

SolidJS のルーティングシステムについて、solid-app-router を使った実装方法を詳しく解説します。
SolidJS は、React ライクな構文を持ちながら、より優れたパフォーマンスを提供するモダンなフレームワークです。しかし、ルーティング機能は標準では提供されていないため、適切なルーティングライブラリの選択が重要になります。
この記事では、SolidJS の公式ルーティングライブラリである solid-app-router について、基礎から応用まで体系的に学んでいきます。実際のエラーとその解決方法も含めて、実践的な知識を身につけられる内容となっています。
solid-app-router とは
solid-app-router は、SolidJS の公式ルーティングライブラリです。軽量で高速、そして直感的な API を提供することで、SolidJS アプリケーションでのナビゲーションを簡単に実装できます。
SolidJS の公式ルーティングライブラリ
solid-app-router は、SolidJS コミュニティによって開発・メンテナンスされている公式のルーティングソリューションです。SolidJS の設計哲学に完全に準拠しており、リアクティブな特性を最大限に活用できます。
typescript// solid-app-routerの基本インポート
import { Router, Routes, Route } from '@solidjs/router';
このライブラリは、SolidJS の細粒度のリアクティビティシステムと統合されており、パフォーマンスを損なうことなく、スムーズなナビゲーション体験を提供します。
他のフレームワークとの違い
React Router や Vue Router と比較すると、solid-app-router には以下のような特徴があります:
特徴 | solid-app-router | React Router | Vue Router |
---|---|---|---|
バンドルサイズ | 非常に軽量 | 中程度 | 中程度 |
パフォーマンス | 最高 | 良好 | 良好 |
学習コスト | 低い | 中程度 | 中程度 |
TypeScript 対応 | 完全対応 | 良好 | 良好 |
solid-app-router は、特にバンドルサイズとパフォーマンスの面で優位性を持っています。これは、SolidJS のコンパイル時最適化と組み合わさることで実現されています。
なぜ solid-app-router を選ぶべきか
solid-app-router を選択する理由は、以下の通りです:
1. パフォーマンスの優位性 SolidJS の細粒度リアクティビティシステムにより、必要な部分のみが更新されます。これにより、ページ遷移時のパフォーマンスが大幅に向上します。
2. 開発者体験の向上 直感的な API 設計により、学習コストを最小限に抑えながら、高度なルーティング機能を実装できます。
3. 将来性 SolidJS の公式ライブラリとして、長期的なサポートと継続的な改善が期待できます。
実際に、多くの SolidJS プロジェクトで採用されており、コミュニティからの信頼も厚いライブラリです。
環境構築とセットアップ
solid-app-router を使った開発環境を構築していきましょう。段階的に設定を行い、確実に動作する環境を作ります。
プロジェクトの初期化
まず、新しい SolidJS プロジェクトを作成します。SolidJS の公式テンプレートを使用することで、最適な設定でプロジェクトを開始できます。
bash# Viteテンプレートを使用してプロジェクトを作成
yarn create vite my-solidjs-app --template solid-ts
# プロジェクトディレクトリに移動
cd my-solidjs-app
# 依存関係をインストール
yarn install
このコマンドを実行すると、TypeScript 対応の SolidJS プロジェクトが作成されます。プロジェクト構造は以下のようになります:
arduinomy-solidjs-app/
├── public/
├── src/
│ ├── components/
│ ├── App.tsx
│ ├── index.tsx
│ └── vite-env.d.ts
├── package.json
├── tsconfig.json
└── vite.config.ts
solid-app-router のインストール
プロジェクトが作成されたら、solid-app-router をインストールします。
bash# solid-app-routerをインストール
yarn add @solidjs/router
インストールが完了すると、package.json
に以下の依存関係が追加されます:
json{
"dependencies": {
"@solidjs/router": "^0.10.0",
"solid-js": "^1.8.0"
}
}
基本的な設定方法
solid-app-router の基本的な設定を行います。まず、src/App.tsx
を編集して、ルーティングの基盤を構築します。
typescript// src/App.tsx
import { Router, Routes, Route } from '@solidjs/router';
import { Component } from 'solid-js';
// 基本的なページコンポーネント
const Home: Component = () => {
return (
<div>
<h1>ホームページ</h1>
<p>
SolidJS +
solid-app-routerで作成されたアプリケーションです。
</p>
</div>
);
};
const About: Component = () => {
return (
<div>
<h1>アバウトページ</h1>
<p>このアプリケーションについての情報です。</p>
</div>
);
};
const App: Component = () => {
return (
<Router>
<Routes>
<Route path='/' component={Home} />
<Route path='/about' component={About} />
</Routes>
</Router>
);
};
export default App;
この設定により、基本的なルーティング機能が動作するようになります。/
にアクセスするとホームページが、/about
にアクセスするとアバウトページが表示されます。
基本的なルーティング設定
solid-app-router の基本的な機能について、詳しく見ていきましょう。各コンポーネントの役割と使用方法を理解することで、より柔軟なルーティング設定が可能になります。
Router コンポーネントの設定
Router コンポーネントは、アプリケーション全体をルーティングシステムで囲むためのコンテナです。これにより、アプリケーション内でのナビゲーションが可能になります。
typescript// src/App.tsx
import { Router } from '@solidjs/router';
import { Component } from 'solid-js';
const App: Component = () => {
return (
<Router>{/* ここにルーティング設定を記述 */}</Router>
);
};
Router コンポーネントには、いくつかのオプションを設定できます:
typescript// カスタム設定付きのRouter
<Router base='/app' data={{}} out={{}} preload={false}>
{/* ルーティング設定 */}
</Router>
よくあるエラーと解決方法:
bash# エラー: Router must be used within a SolidJS application
Error: Router must be used within a SolidJS application
# 解決方法: index.tsxで正しくレンダリングされているか確認
このエラーは、通常index.tsx
でのレンダリング設定に問題がある場合に発生します。
Routes と Route の使い方
Routes コンポーネントは、複数の Route をグループ化するためのコンテナです。Route コンポーネントは、個々のルートを定義します。
typescript// src/App.tsx
import { Routes, Route } from '@solidjs/router';
import { Component } from 'solid-js';
const App: Component = () => {
return (
<Router>
<Routes>
<Route path='/' component={Home} />
<Route path='/users' component={Users} />
<Route path='/products' component={Products} />
</Routes>
</Router>
);
};
Route コンポーネントには、以下のプロパティを設定できます:
typescript<Route
path='/example'
component={ExampleComponent}
data={async () => ({ title: 'Example' })}
children={[
<Route path='/nested' component={NestedComponent} />,
]}
/>
よくあるエラーと解決方法:
bash# エラー: No routes matched location "/undefined"
Error: No routes matched location "/undefined"
# 解決方法: パスが正しく設定されているか確認
<Route path="/" component={Home} /> // 正しい
<Route path="" component={Home} /> // 間違い
パスパラメータの取得方法
URL パラメータを取得するには、useParams
フックを使用します。これにより、動的なルーティングが可能になります。
typescript// src/components/UserDetail.tsx
import { useParams } from '@solidjs/router';
import { Component, createEffect } from 'solid-js';
const UserDetail: Component = () => {
const params = useParams();
createEffect(() => {
console.log('ユーザーID:', params.id);
});
return (
<div>
<h1>ユーザー詳細</h1>
<p>ユーザーID: {params.id}</p>
</div>
);
};
export default UserDetail;
対応するルート設定:
typescript// src/App.tsx
<Route path='/users/:id' component={UserDetail} />
よくあるエラーと解決方法:
bash# エラー: params is undefined
TypeError: Cannot read properties of undefined (reading 'id')
# 解決方法: useParamsの戻り値を適切に処理
const params = useParams();
if (!params) return <div>Loading...</div>;
動的ルーティング
動的ルーティングにより、URL パラメータに基づいてコンテンツを動的に表示できます。これにより、より柔軟でスケーラブルなアプリケーションを構築できます。
動的セグメントの実装
動的セグメントは、URL の一部を変数として扱う機能です。:
プレフィックスを使用して定義します。
typescript// src/App.tsx
import { Routes, Route } from '@solidjs/router';
import { Component } from 'solid-js';
const App: Component = () => {
return (
<Router>
<Routes>
<Route path='/' component={Home} />
<Route path='/posts/:id' component={PostDetail} />
<Route
path='/users/:userId/posts/:postId'
component={UserPost}
/>
</Routes>
</Router>
);
};
動的セグメントを使用するコンポーネント:
typescript// src/components/PostDetail.tsx
import { useParams } from '@solidjs/router';
import {
Component,
createSignal,
createEffect,
} from 'solid-js';
const PostDetail: Component = () => {
const params = useParams();
const [post, setPost] = createSignal(null);
const [loading, setLoading] = createSignal(true);
createEffect(async () => {
if (params.id) {
setLoading(true);
try {
// APIから投稿データを取得
const response = await fetch(
`/api/posts/${params.id}`
);
const data = await response.json();
setPost(data);
} catch (error) {
console.error('投稿の取得に失敗しました:', error);
} finally {
setLoading(false);
}
}
});
return (
<div>
{loading() ? (
<p>読み込み中...</p>
) : post() ? (
<div>
<h1>{post().title}</h1>
<p>{post().content}</p>
</div>
) : (
<p>投稿が見つかりません</p>
)}
</div>
);
};
export default PostDetail;
ネストしたルートの作成
ネストしたルートにより、親子関係のあるページ構造を作成できます。これにより、共通のレイアウトやナビゲーションを効率的に管理できます。
typescript// src/App.tsx
import { Routes, Route } from '@solidjs/router';
import { Component } from 'solid-js';
const App: Component = () => {
return (
<Router>
<Routes>
<Route path='/' component={Home} />
<Route
path='/dashboard'
component={DashboardLayout}
>
<Route path='/' component={DashboardHome} />
<Route path='/profile' component={Profile} />
<Route path='/settings' component={Settings} />
</Route>
</Routes>
</Router>
);
};
ダッシュボードレイアウトコンポーネント:
typescript// src/components/DashboardLayout.tsx
import { Outlet } from '@solidjs/router';
import { Component } from 'solid-js';
const DashboardLayout: Component = () => {
return (
<div class='dashboard'>
<nav class='dashboard-nav'>
<a href='/dashboard'>ホーム</a>
<a href='/dashboard/profile'>プロフィール</a>
<a href='/dashboard/settings'>設定</a>
</nav>
<main class='dashboard-content'>
<Outlet />
</main>
</div>
);
};
export default DashboardLayout;
よくあるエラーと解決方法:
bash# エラー: Outlet is not defined
ReferenceError: Outlet is not defined
# 解決方法: Outletを正しくインポート
import { Outlet } from "@solidjs/router";
ワイルドカードルートの活用
ワイルドカードルート(*
)を使用することで、マッチしないすべてのパスをキャッチできます。これは 404 ページの実装に特に有用です。
typescript// src/App.tsx
import { Routes, Route } from '@solidjs/router';
import { Component } from 'solid-js';
const App: Component = () => {
return (
<Router>
<Routes>
<Route path='/' component={Home} />
<Route path='/about' component={About} />
<Route path='/contact' component={Contact} />
<Route path='*' component={NotFound} />
</Routes>
</Router>
);
};
404 ページコンポーネント:
typescript// src/components/NotFound.tsx
import { useLocation } from '@solidjs/router';
import { Component } from 'solid-js';
const NotFound: Component = () => {
const location = useLocation();
return (
<div class='not-found'>
<h1>404 - ページが見つかりません</h1>
<p>
要求されたパス "{location.pathname}"
は存在しません。
</p>
<a href='/'>ホームに戻る</a>
</div>
);
};
export default NotFound;
ナビゲーション機能
ナビゲーション機能により、ユーザーはアプリケーション内を自由に移動できます。solid-app-router は、宣言的と命令的の両方のナビゲーション方法を提供します。
Link コンポーネントの使用方法
Link コンポーネントは、宣言的なナビゲーションを提供します。HTML の<a>
タグと同様に使用できますが、ページの再読み込みなしでナビゲーションが実行されます。
typescript// src/components/Navigation.tsx
import { Link } from '@solidjs/router';
import { Component } from 'solid-js';
const Navigation: Component = () => {
return (
<nav>
<ul>
<li>
<Link href='/' activeClass='active'>
ホーム
</Link>
</li>
<li>
<Link href='/about' activeClass='active'>
アバウト
</Link>
</li>
<li>
<Link href='/contact' activeClass='active'>
お問い合わせ
</Link>
</li>
</ul>
</nav>
);
};
export default Navigation;
Link コンポーネントの高度な使用方法:
typescript// 動的パラメータ付きのリンク
<Link href={`/users/${userId}`}>ユーザー詳細</Link>
// クエリパラメータ付きのリンク
<Link href="/search?q=keyword&page=1">検索結果</Link>
// カスタムクラスとスタイル
<Link
href="/dashboard"
class="nav-link"
activeClass="nav-link-active"
inactiveClass="nav-link-inactive"
>
ダッシュボード
</Link>
よくあるエラーと解決方法:
bash# エラー: Link component requires href prop
Error: Link component requires href prop
# 解決方法: hrefプロパティを必ず指定
<Link href="/path">リンクテキスト</Link>
プログラムによるナビゲーション
useNavigate
フックを使用することで、プログラム的にナビゲーションを実行できます。これは、フォーム送信後のリダイレクトや、条件付きナビゲーションに特に有用です。
typescript// src/components/LoginForm.tsx
import { useNavigate } from '@solidjs/router';
import { Component, createSignal } from 'solid-js';
const LoginForm: Component = () => {
const navigate = useNavigate();
const [email, setEmail] = createSignal('');
const [password, setPassword] = createSignal('');
const [loading, setLoading] = createSignal(false);
const handleSubmit = async (e: Event) => {
e.preventDefault();
setLoading(true);
try {
// ログイン処理
const response = await fetch('/api/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
email: email(),
password: password(),
}),
});
if (response.ok) {
// ログイン成功時はダッシュボードにリダイレクト
navigate('/dashboard');
} else {
alert('ログインに失敗しました');
}
} catch (error) {
console.error('ログインエラー:', error);
alert('ログイン中にエラーが発生しました');
} finally {
setLoading(false);
}
};
return (
<form onSubmit={handleSubmit}>
<div>
<label for='email'>メールアドレス:</label>
<input
id='email'
type='email'
value={email()}
onInput={(e) => setEmail(e.currentTarget.value)}
required
/>
</div>
<div>
<label for='password'>パスワード:</label>
<input
id='password'
type='password'
value={password()}
onInput={(e) =>
setPassword(e.currentTarget.value)
}
required
/>
</div>
<button type='submit' disabled={loading()}>
{loading() ? 'ログイン中...' : 'ログイン'}
</button>
</form>
);
};
export default LoginForm;
ナビゲーションの高度な使用方法:
typescript// 履歴スタックの操作
navigate('/new-page', { replace: true }); // 現在のページを置き換え
navigate(-1); // 前のページに戻る
navigate(2); // 2ページ先に進む
// 状態付きナビゲーション
navigate('/profile', { state: { from: 'login' } });
ナビゲーションガードの実装
ナビゲーションガードにより、特定の条件を満たさない場合のナビゲーションを防ぐことができます。認証や権限チェックに特に有用です。
typescript// src/components/ProtectedRoute.tsx
import { Route, Navigate } from '@solidjs/router';
import {
Component,
createSignal,
createEffect,
} from 'solid-js';
interface ProtectedRouteProps {
path: string;
component: Component;
requiredRole?: string;
}
const ProtectedRoute: Component<ProtectedRouteProps> = (
props
) => {
const [isAuthenticated, setIsAuthenticated] =
createSignal(false);
const [userRole, setUserRole] = createSignal('');
const [loading, setLoading] = createSignal(true);
createEffect(() => {
// 認証状態をチェック
checkAuthStatus();
});
const checkAuthStatus = async () => {
try {
const token = localStorage.getItem('authToken');
if (!token) {
setIsAuthenticated(false);
return;
}
// トークンの有効性を確認
const response = await fetch('/api/auth/verify', {
headers: { Authorization: `Bearer ${token}` },
});
if (response.ok) {
const userData = await response.json();
setIsAuthenticated(true);
setUserRole(userData.role);
} else {
setIsAuthenticated(false);
localStorage.removeItem('authToken');
}
} catch (error) {
console.error('認証チェックエラー:', error);
setIsAuthenticated(false);
} finally {
setLoading(false);
}
};
return (
<Route
path={props.path}
component={() => {
if (loading()) {
return <div>認証確認中...</div>;
}
if (!isAuthenticated()) {
return <Navigate href='/login' />;
}
if (
props.requiredRole &&
userRole() !== props.requiredRole
) {
return <div>アクセス権限がありません</div>;
}
return <props.component />;
}}
/>
);
};
export default ProtectedRoute;
使用例:
typescript// src/App.tsx
<Routes>
<Route path='/' component={Home} />
<Route path='/login' component={Login} />
<ProtectedRoute path='/dashboard' component={Dashboard} />
<ProtectedRoute
path='/admin'
component={AdminPanel}
requiredRole='admin'
/>
</Routes>
高度なルーティング機能
solid-app-router の高度な機能を活用することで、より洗練されたユーザー体験を提供できます。これらの機能は、大規模アプリケーションの開発に特に有用です。
遅延ローディング(コード分割)
遅延ローディングにより、必要なコンポーネントのみを動的に読み込むことができます。これにより、初期バンドルサイズを削減し、アプリケーションの起動速度を向上させます。
typescript// src/App.tsx
import { lazy } from 'solid-js';
import { Routes, Route } from '@solidjs/router';
import { Component } from 'solid-js';
// 遅延ローディング用のコンポーネント
const Dashboard = lazy(
() => import('./components/Dashboard')
);
const AdminPanel = lazy(
() => import('./components/AdminPanel')
);
const UserProfile = lazy(
() => import('./components/UserProfile')
);
const App: Component = () => {
return (
<Router>
<Routes>
<Route path='/' component={Home} />
<Route path='/dashboard' component={Dashboard} />
<Route path='/admin' component={AdminPanel} />
<Route path='/profile' component={UserProfile} />
</Routes>
</Router>
);
};
ローディング状態の管理:
typescript// src/components/LoadingWrapper.tsx
import { Suspense } from 'solid-js';
import { Component, JSX } from 'solid-js';
interface LoadingWrapperProps {
children: JSX.Element;
fallback?: JSX.Element;
}
const LoadingWrapper: Component<LoadingWrapperProps> = (
props
) => {
const defaultFallback = (
<div class='loading-spinner'>
<div class='spinner'></div>
<p>読み込み中...</p>
</div>
);
return (
<Suspense fallback={props.fallback || defaultFallback}>
{props.children}
</Suspense>
);
};
export default LoadingWrapper;
よくあるエラーと解決方法:
bash# エラー: lazy() requires a function that returns a Promise
Error: lazy() requires a function that returns a Promise
# 解決方法: 正しい形式でlazyを使用
const Component = lazy(() => import("./Component"));
ルートベースのレイアウト
ルートベースのレイアウトにより、特定のルートグループに対して共通のレイアウトを適用できます。これにより、コードの重複を避け、一貫したユーザーインターフェースを提供できます。
typescript// src/layouts/MainLayout.tsx
import { Outlet } from '@solidjs/router';
import { Component } from 'solid-js';
import Navigation from '../components/Navigation';
import Footer from '../components/Footer';
const MainLayout: Component = () => {
return (
<div class='main-layout'>
<header>
<Navigation />
</header>
<main class='main-content'>
<Outlet />
</main>
<footer>
<Footer />
</footer>
</div>
);
};
export default MainLayout;
typescript// src/layouts/AuthLayout.tsx
import { Outlet } from '@solidjs/router';
import { Component } from 'solid-js';
const AuthLayout: Component = () => {
return (
<div class='auth-layout'>
<div class='auth-container'>
<div class='auth-form'>
<Outlet />
</div>
</div>
</div>
);
};
export default AuthLayout;
レイアウトの適用:
typescript// src/App.tsx
import { Routes, Route } from '@solidjs/router';
import { Component } from 'solid-js';
const App: Component = () => {
return (
<Router>
<Routes>
<Route path='/' component={MainLayout}>
<Route path='/' component={Home} />
<Route path='/about' component={About} />
<Route path='/contact' component={Contact} />
</Route>
<Route path='/auth' component={AuthLayout}>
<Route path='/login' component={Login} />
<Route path='/register' component={Register} />
</Route>
</Routes>
</Router>
);
};
エラーハンドリング
エラーハンドリングにより、ルーティング中に発生するエラーを適切に処理できます。これにより、ユーザーに分かりやすいエラーメッセージを表示し、アプリケーションの安定性を向上させます。
typescript// src/components/ErrorBoundary.tsx
import { ErrorBoundary as SolidErrorBoundary } from 'solid-js';
import { Component, JSX } from 'solid-js';
interface ErrorBoundaryProps {
children: JSX.Element;
fallback?: (
error: Error,
reset: () => void
) => JSX.Element;
}
const ErrorBoundary: Component<ErrorBoundaryProps> = (
props
) => {
const defaultFallback = (
error: Error,
reset: () => void
) => (
<div class='error-boundary'>
<h2>エラーが発生しました</h2>
<p>{error.message}</p>
<button onClick={reset}>再試行</button>
</div>
);
return (
<SolidErrorBoundary
fallback={props.fallback || defaultFallback}
>
{props.children}
</SolidErrorBoundary>
);
};
export default ErrorBoundary;
ルートレベルでのエラーハンドリング:
typescript// src/App.tsx
import { Routes, Route } from '@solidjs/router';
import { Component } from 'solid-js';
import ErrorBoundary from './components/ErrorBoundary';
const App: Component = () => {
return (
<Router>
<ErrorBoundary>
<Routes>
<Route path='/' component={Home} />
<Route path='/about' component={About} />
<Route path='/contact' component={Contact} />
<Route path='*' component={NotFound} />
</Routes>
</ErrorBoundary>
</Router>
);
};
データ取得エラーの処理:
typescript// src/components/DataLoader.tsx
import {
Component,
createSignal,
createEffect,
JSX,
} from 'solid-js';
interface DataLoaderProps {
url: string;
children: (data: any) => JSX.Element;
fallback?: JSX.Element;
errorFallback?: (error: Error) => JSX.Element;
}
const DataLoader: Component<DataLoaderProps> = (props) => {
const [data, setData] = createSignal(null);
const [loading, setLoading] = createSignal(true);
const [error, setError] = createSignal(null);
createEffect(async () => {
try {
setLoading(true);
setError(null);
const response = await fetch(props.url);
if (!response.ok) {
throw new Error(
`HTTP error! status: ${response.status}`
);
}
const result = await response.json();
setData(result);
} catch (err) {
setError(err as Error);
} finally {
setLoading(false);
}
});
if (loading()) {
return props.fallback || <div>読み込み中...</div>;
}
if (error()) {
return (
props.errorFallback?.(error()) || (
<div class='error'>
<h3>データの読み込みに失敗しました</h3>
<p>{error().message}</p>
</div>
)
);
}
return props.children(data());
};
export default DataLoader;
実践的なサンプルアプリケーション
実際のユースケースを想定した、完全なルーティング設定例を見ていきましょう。このサンプルアプリケーションは、ブログシステムを想定しており、多くの実用的な機能を含んでいます。
完全なルーティング設定例
typescript// src/App.tsx
import { Router, Routes, Route } from '@solidjs/router';
import { lazy, Component } from 'solid-js';
import LoadingWrapper from './components/LoadingWrapper';
import ErrorBoundary from './components/ErrorBoundary';
// 遅延ローディング用のコンポーネント
const Home = lazy(() => import('./pages/Home'));
const Blog = lazy(() => import('./pages/Blog'));
const BlogPost = lazy(() => import('./pages/BlogPost'));
const About = lazy(() => import('./pages/About'));
const Contact = lazy(() => import('./pages/Contact'));
const Login = lazy(() => import('./pages/Login'));
const Register = lazy(() => import('./pages/Register'));
const Dashboard = lazy(() => import('./pages/Dashboard'));
const Admin = lazy(() => import('./pages/Admin'));
const NotFound = lazy(() => import('./pages/NotFound'));
// レイアウトコンポーネント
const MainLayout = lazy(
() => import('./layouts/MainLayout')
);
const AuthLayout = lazy(
() => import('./layouts/AuthLayout')
);
const DashboardLayout = lazy(
() => import('./layouts/DashboardLayout')
);
const App: Component = () => {
return (
<Router>
<ErrorBoundary>
<LoadingWrapper>
<Routes>
{/* メインレイアウト */}
<Route path='/' component={MainLayout}>
<Route path='/' component={Home} />
<Route path='/blog' component={Blog} />
<Route
path='/blog/:id'
component={BlogPost}
/>
<Route path='/about' component={About} />
<Route path='/contact' component={Contact} />
</Route>
{/* 認証レイアウト */}
<Route path='/auth' component={AuthLayout}>
<Route path='/login' component={Login} />
<Route
path='/register'
component={Register}
/>
</Route>
{/* ダッシュボードレイアウト */}
<Route
path='/dashboard'
component={DashboardLayout}
>
<Route path='/' component={Dashboard} />
<Route
path='/profile'
component={UserProfile}
/>
<Route
path='/settings'
component={Settings}
/>
</Route>
{/* 管理者レイアウト */}
<Route path='/admin' component={AdminLayout}>
<Route path='/' component={Admin} />
<Route
path='/users'
component={UserManagement}
/>
<Route
path='/posts'
component={PostManagement}
/>
</Route>
{/* 404ページ */}
<Route path='*' component={NotFound} />
</Routes>
</LoadingWrapper>
</ErrorBoundary>
</Router>
);
};
export default App;
実際のユースケースでの活用方法
ブログ投稿の詳細ページの実装例:
typescript// src/pages/BlogPost.tsx
import { useParams, useNavigate } from '@solidjs/router';
import {
Component,
createSignal,
createEffect,
Show,
} from 'solid-js';
import DataLoader from '../components/DataLoader';
const BlogPost: Component = () => {
const params = useParams();
const navigate = useNavigate();
const [post, setPost] = createSignal(null);
const [loading, setLoading] = createSignal(true);
const [error, setError] = createSignal(null);
createEffect(async () => {
if (params.id) {
try {
setLoading(true);
const response = await fetch(
`/api/posts/${params.id}`
);
if (!response.ok) {
if (response.status === 404) {
navigate('/blog', { replace: true });
return;
}
throw new Error('投稿の取得に失敗しました');
}
const data = await response.json();
setPost(data);
} catch (err) {
setError(err as Error);
} finally {
setLoading(false);
}
}
});
return (
<div class='blog-post'>
<Show
when={!loading()}
fallback={<div>読み込み中...</div>}
>
<Show
when={!error()}
fallback={
<div class='error'>
<h2>エラーが発生しました</h2>
<p>{error().message}</p>
<button onClick={() => navigate('/blog')}>
ブログ一覧に戻る
</button>
</div>
}
>
<Show when={post()}>
<article>
<header>
<h1>{post().title}</h1>
<div class='meta'>
<span>
投稿日:{' '}
{new Date(
post().createdAt
).toLocaleDateString()}
</span>
<span>著者: {post().author}</span>
</div>
</header>
<div
class='content'
innerHTML={post().content}
/>
<footer>
<div class='tags'>
{post().tags.map((tag) => (
<span class='tag'>{tag}</span>
))}
</div>
</footer>
</article>
</Show>
</Show>
</Show>
</div>
);
};
export default BlogPost;
ナビゲーションコンポーネント:
typescript// src/components/Navigation.tsx
import { Link, useLocation } from '@solidjs/router';
import {
Component,
createSignal,
createEffect,
} from 'solid-js';
const Navigation: Component = () => {
const location = useLocation();
const [isAuthenticated, setIsAuthenticated] =
createSignal(false);
const [userRole, setUserRole] = createSignal('');
createEffect(() => {
// 認証状態をチェック
const token = localStorage.getItem('authToken');
if (token) {
// トークンの有効性を確認
checkAuthStatus();
} else {
setIsAuthenticated(false);
}
});
const checkAuthStatus = async () => {
try {
const response = await fetch('/api/auth/me', {
headers: {
Authorization: `Bearer ${localStorage.getItem(
'authToken'
)}`,
},
});
if (response.ok) {
const userData = await response.json();
setIsAuthenticated(true);
setUserRole(userData.role);
} else {
setIsAuthenticated(false);
localStorage.removeItem('authToken');
}
} catch (error) {
setIsAuthenticated(false);
}
};
const handleLogout = () => {
localStorage.removeItem('authToken');
setIsAuthenticated(false);
setUserRole('');
window.location.href = '/';
};
return (
<nav class='main-navigation'>
<div class='nav-brand'>
<Link href='/'>MyBlog</Link>
</div>
<ul class='nav-menu'>
<li>
<Link
href='/'
activeClass='active'
class='nav-link'
>
ホーム
</Link>
</li>
<li>
<Link
href='/blog'
activeClass='active'
class='nav-link'
>
ブログ
</Link>
</li>
<li>
<Link
href='/about'
activeClass='active'
class='nav-link'
>
アバウト
</Link>
</li>
<li>
<Link
href='/contact'
activeClass='active'
class='nav-link'
>
お問い合わせ
</Link>
</li>
</ul>
<div class='nav-auth'>
<Show
when={isAuthenticated()}
fallback={
<div class='auth-buttons'>
<Link
href='/auth/login'
class='btn btn-outline'
>
ログイン
</Link>
<Link
href='/auth/register'
class='btn btn-primary'
>
登録
</Link>
</div>
}
>
<div class='user-menu'>
<Show when={userRole() === 'admin'}>
<Link href='/admin' class='btn btn-secondary'>
管理画面
</Link>
</Show>
<Link href='/dashboard' class='btn btn-outline'>
ダッシュボード
</Link>
<button
onClick={handleLogout}
class='btn btn-outline'
>
ログアウト
</button>
</div>
</Show>
</div>
</nav>
);
};
export default Navigation;
まとめ
solid-app-router を使った SolidJS のルーティングシステムについて、基礎から応用まで詳しく解説してきました。
この記事で学んだ主要なポイントを振り返ってみましょう:
1. solid-app-router の特徴
- SolidJS の公式ルーティングライブラリ
- 軽量で高速なパフォーマンス
- 直感的な API 設計
- TypeScript 完全対応
2. 基本的な機能
- Router、Routes、Route コンポーネントの使い方
- 動的セグメントとパラメータ取得
- Link コンポーネントによる宣言的ナビゲーション
- useNavigate フックによるプログラム的ナビゲーション
3. 高度な機能
- 遅延ローディングによるコード分割
- ネストしたルートとレイアウト
- ナビゲーションガードによる認証制御
- エラーハンドリングと 404 ページ
4. 実践的な活用
- 実際のプロジェクトでの構成例
- パフォーマンス最適化のテクニック
- エラー処理とユーザー体験の向上
solid-app-router は、SolidJS の優れたパフォーマンス特性を活かしながら、モダンな Web アプリケーションに必要なルーティング機能を提供します。この記事で学んだ知識を活用して、より良いユーザー体験を提供するアプリケーションを開発してください。
実際のプロジェクトでは、これらの機能を組み合わせることで、スケーラブルで保守性の高いルーティングシステムを構築できます。エラー処理やパフォーマンス最適化を適切に行うことで、ユーザーに信頼されるアプリケーションを作成できるでしょう。
関連リンク
- review
今の自分に満足していますか?『持たざる者の逆襲 まだ何者でもない君へ』溝口勇児
- review
ついに語られた業界の裏側!『フジテレビの正体』堀江貴文が描くテレビ局の本当の姿
- review
愛する勇気を持てば人生が変わる!『幸せになる勇気』岸見一郎・古賀史健のアドラー実践編で真の幸福を手に入れる
- review
週末を変えれば年収も変わる!『世界の一流は「休日」に何をしているのか』越川慎司の一流週末メソッド
- review
新しい自分に会いに行こう!『自分の変え方』村岡大樹の認知科学コーチングで人生リセット
- review
科学革命から AI 時代へ!『サピエンス全史 下巻』ユヴァル・ノア・ハラリが予見する人類の未来