mcpField
F
frontend-development

frontend-development

github.com/samhvw8開発15174 回インストール今日更新
リポジトリ

frontend-development について

React、Vue 3、Svelte 5、Angularを扱うフロントエンド開発ガイドのスキルです。各フレームワークの指針に沿って、パターン選定やコンポーネント、ルーティング、データ取得、型付きのアプリ構成を支援します。

主な機能

  1. 014つのフロントエンドフレームワークの比較
  2. 02コンポーネントと機能構成の指針
  3. 03ルーティングとデータ取得の解説
  4. 04TypeScriptとパフォーマンスの実践
  5. 05フレームワーク別パターンの提示

活用シーン

  1. 01新しいフロントエンドプロジェクトの計画
  2. 02コンポーネントやアプリ機能の構築
  3. 03データ取得やルーティングの改善
  4. 04フレームワーク移行の検討

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/samhvw8/dotfiles --skill frontend-development

インストール後、Claude Code で/frontend-developmentを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

resources/
common-patterns.md
complete-examples.md
component-patterns.md
data-fetching.md
file-organization.md
loading-and-error-states.md
performance.md
routing-guide.md
styling-guide.md
typescript-standards.md

詳細

フロントエンド開発ガイドライン

目的

React、Vue 3、Svelte 5、Angular を対象とした、モダンなフロントエンド開発の包括的なガイドです。各フレームワーク固有のパターン、共通するアーキテクチャの原則、フレームワークを問わないベストプラクティスを取り上げます。

このスキルを使用する場面

  • React、Vue、Svelte、Angular でコンポーネントやページを作成する
  • フレームワーク固有のパターンを使って新機能を構築する
  • 状態管理を実装する(Pinia、Zustand、ストア、シグナル)
  • ルーティングを設定する(TanStack Router、Vue Router、SvelteKit、Angular Router)
  • データ取得パターンを実装する(TanStack Query、コンポーザブル、SvelteKit のロード関数)
  • フレームワーク全体のパフォーマンスを最適化する
  • コンポーネントの構成と再利用性を高める
  • TypeScript の統合とベストプラクティスに対応する
  • プロジェクトに適したフレームワークを選ぶ
  • フレームワーク間を移行する

フレームワーク選定ガイド

プロジェクトの要件に基づいてフレームワークを選択してください。

フレームワーク最適な用途学習曲線パフォーマンスエコシステム
React大規模アプリ、強い型付け、エンタープライズ中程度良好最大
Vue 3段階的な導入、習得しやすさ低い優秀拡大中
Svelte 5小〜中規模アプリ、ネイティブな使用感低い最高小規模
Angularエンタープライズ、多機能高い良好充実

簡単な判断基準:

  • 既存のコードベースがあるか? 既存のものを使うか、移行を計画します
  • チームに経験があるか? 既存の知識を活用します
  • プロジェクトの規模は? 大規模 → React/Angular、中規模 → Vue/React、小規模 → Svelte/Vue
  • パフォーマンスが重要か? Svelte > Vue > React ≈ Angular
  • TypeScript が必要か? すべて十分に対応しています(Angular は統合が最も優れています)
  • エコシステムの要件は? React > Vue > Angular > Svelte

各フレームワークの詳しいパターンについては、以下の各セクションを参照してください。


フレームワークの簡易比較

機能ReactVue 3Svelte 5Angular
リアクティビティフック(useState)ref/reactiveルーン($state)シグナル
コンポーネントJSX/TSXSFC (.vue)SFC (.svelte)デコレーター/クラス
状態管理Zustand/ContextPiniaストアサービス
ルーティングReact Router/TanStackVue RouterSvelteKitAngular Router
データ取得TanStack QueryComposables/VueQueryロード関数HttpClient/RxJS
スタイリングCSS-in-JS/Modulesスコープ付き CSSスコープ付き CSSコンポーネントスタイル
フルスタックNext.jsNuxtSvelteKitUniversal/SSR
バンドルサイズ~40KB~32KB~3KB~60KB
コンパイラーランタイムランタイムコンパイル時AOT コンパイラー

移動先:


React 開発

新規コンポーネントのチェックリスト

コンポーネントを作成しますか?以下のチェックリストに従ってください。

  • TypeScript で React.FC<Props> パターンを使用する
  • 大きなコンポーネントは遅延読み込みする:React.lazy(() => import())
  • ローディング状態には <SuspenseLoader> でラップする
  • データ取得には useSuspenseQuery を使用する
  • インポートエイリアス:@/、~types、~components、~features
  • スタイル:100 行未満ならインライン、100 行を超えるなら別ファイルにする
  • 子コンポーネントに渡すイベントハンドラーには useCallback を使用する
  • デフォルトエクスポートは末尾に置く
  • ローディングスピナーを伴う早期リターンは使用しない
  • ユーザーへの通知には useMuiSnackbar を使用する
新機能チェックリスト

機能を作成しますか? 次の構成を用意してください。

  • features/{feature-name}/ ディレクトリを作成する
  • サブディレクトリを作成する:api/、components/、hooks/、helpers/、types/
  • API サービスファイルを作成する:api/{feature}Api.ts
  • types/ に TypeScript の型を設定する
  • routes/{feature-name}/index.tsx にルートを作成する
  • 機能コンポーネントを遅延読み込みする
  • Suspense 境界を使用する
  • 機能の index.ts から公開 API をエクスポートする

インポートエイリアス早見表

エイリアス解決先例
@/src/import { apiClient } from '@/lib/apiClient'
~typessrc/typesimport type { User } from '~types/user'
~componentssrc/componentsimport { SuspenseLoader } from '~components/SuspenseLoader'
~featuressrc/featuresimport { authApi } from '~features/auth'

定義場所:vite.config.ts の 180~185 行


よく使うインポート早見表

// React & Lazy Loading
import React, { useState, useCallback, useMemo } from 'react';
const Heavy = React.lazy(() => import('./Heavy'));

// MUI Components
import { Box, Paper, Typography, Button, Grid } from '@mui/material';
import type { SxProps, Theme } from '@mui/material';

// TanStack Query (Suspense)
import { useSuspenseQuery, useQueryClient } from '@tanstack/react-query';

// TanStack Router
import { createFileRoute } from '@tanstack/react-router';

// Project Components
import { SuspenseLoader } from '~components/SuspenseLoader';

// Hooks
import { useAuth } from '@/hooks/useAuth';
import { useMuiSnackbar } from '@/hooks/useMuiSnackbar';

// Types
import type { Post } from '~types/post';

トピック別ガイド

🎨 コンポーネントのパターン

最新の React コンポーネントでは、次を使用します。

  • 型安全性のための React.FC<Props>
  • コード分割のための React.lazy()
  • ロード状態のための SuspenseLoader
  • 名前付き const + デフォルトエクスポートのパターン

主な概念:

  • 重いコンポーネント(DataGrid、グラフ、エディター)を遅延読み込みする
  • 遅延読み込みするコンポーネントは、必ず Suspense でラップする
  • SuspenseLoader コンポーネント(フェードアニメーション付き)を使用する
  • コンポーネントの構成:Props → Hooks → Handlers → Render → Export

📖 完全ガイド:resources/component-patterns.md


📊 データ取得

基本パターン:useSuspenseQuery

  • Suspense 境界と併用する
  • キャッシュ優先の戦略(API の前にグリッドキャッシュを確認)
  • isLoading のチェックを置き換える
  • ジェネリクスによる型安全性

API サービスレイヤー:

  • features/{feature}/api/{feature}Api.ts を作成する
  • axios インスタンスの apiClient を使用する
  • 機能ごとにメソッドを集約する
  • ルート形式:/form/route(/api/form/route ではありません)

📖 完全ガイド:resources/data-fetching.md


📁 ファイル構成

features/ と components/ の違い:

  • features/:ドメイン固有(投稿、コメント、認証)
  • components/:真に再利用可能なもの(SuspenseLoader、CustomAppBar)

機能のサブディレクトリ:

features/
  my-feature/
    api/          # API service layer
    components/   # Feature components
    hooks/        # Custom hooks
    helpers/      # Utility functions
    types/        # TypeScript types

📖 完全ガイド:resources/file-organization.md


🎨 スタイル設定

インラインと分離:

  • 100 行未満:インラインの const styles: Record<string, SxProps<Theme>>
  • 100 行超:独立した .styles.ts ファイル

基本的な方法:

  • MUI コンポーネントには sx プロパティを使用する
  • SxProps<Theme> による型安全性
  • テーマへのアクセス:(theme) => theme.palette.primary.main

MUI v7 Grid:

<Grid size={{ xs: 12, md: 6 }}>  // ✅ v7 syntax
<Grid xs={12} md={6}>             // ❌ Old syntax

📖 完全ガイド:resources/styling-guide.md


🛣️ ルーティング

TanStack Router - フォルダーベース:

  • ディレクトリ:routes/my-route/index.tsx
  • コンポーネントを遅延読み込みする
  • createFileRoute を使用する
  • loader にパンくずリストのデータを設定する

例:

import { createFileRoute } from '@tanstack/react-router';
import { lazy } from 'react';

const MyPage = lazy(() => import('@/features/my-feature/components/MyPage'));

export const Route = createFileRoute('/my-route/')({
    component: MyPage,
    loader: () => ({ crumb: 'My Route' }),
});

📖 完全ガイド:resources/routing-guide.md


⏳ ロード状態とエラー状態

重要なルール:早期リターンをしない

// ❌ NEVER - Causes layout shift
if (isLoading) {
    return <LoadingSpinner />;
}

// ✅ ALWAYS - Consistent layout
<SuspenseLoader>
    <Content />
</SuspenseLoader>

理由: Cumulative Layout Shift(CLS)を防ぎ、UX を改善できます。

エラー処理:

  • ユーザーへのフィードバックには useMuiSnackbar を使用する
  • react-toastify は絶対に使用しない
  • TanStack Query の onError コールバックを使用する

📖 完全ガイド:resources/loading-and-error-states.md


⚡ パフォーマンス

最適化パターン:

  • useMemo:負荷の高い計算(filter、sort、map)
  • useCallback:子に渡すイベントハンドラー
  • React.memo:負荷の高いコンポーネント
  • デバウンス検索(300~500ms)
  • メモリーリークの防止(useEffect 内でクリーンアップ)

📖 完全ガイド:resources/performance.md


📘 TypeScript

標準:

  • strict モードを使用し、any 型は禁止します
  • 関数には明示的な戻り値の型を指定します
  • 型のインポート:import type { User } from '~types/user'
  • コンポーネントの props インターフェースには JSDoc を付けます

📖 完全ガイド:resources/typescript-standards.md


🔧 共通パターン

対象トピック:

  • Zod バリデーションを使った React Hook Form
  • DataGrid ラッパーの契約
  • ダイアログコンポーネントの標準
  • 現在のユーザーを取得する useAuth フック
  • キャッシュ無効化を伴うミューテーションパターン

📖 完全ガイド:resources/common-patterns.md


📚 完全な例

動作する完全な例:

  • すべてのパターンを取り入れた最新のコンポーネント
  • 完全な機能の構成
  • API サービスレイヤー
  • 遅延読み込みを使用するルート
  • Suspense + useSuspenseQuery
  • バリデーション付きフォーム

📖 完全ガイド:resources/complete-examples.md


React 専用ナビゲーションガイド

必要な作業参照するリソース
React コンポーネントを作成するcomponent-patterns.md
TanStack Query でデータを取得するdata-fetching.md
ファイル/フォルダーを整理するfile-organization.md
MUI v7 でスタイルを設定するstyling-guide.md
TanStack Router を設定するrouting-guide.md
読み込み中やエラーを処理するloading-and-error-states.md
React のパフォーマンスを最適化するperformance.md
TypeScript の型typescript-standards.md
フォーム/認証/DataGridcommon-patterns.md
React の完全な例を見るcomplete-examples.md

注: 上記のリソースは React 専用です。Vue/Svelte/Angular については、このドキュメントの各フレームワークのセクションを参照してください。


基本原則(すべてのフレームワーク)

  1. 重いコンポーネントを遅延読み込みする:ルート、データグリッド、チャート、エディター、モーダル
  2. 一貫した読み込み状態:適切な読み込み UI を用意し、レイアウトシフトを避けます
  3. 型安全性:すべてのコンポーネントと API に TypeScript を使用します
  4. コンポーネントの合成:責務が明確な、小さく焦点を絞ったコンポーネントを作成します
  5. 単方向のデータフロー:props は下へ、イベントは上へ
  6. パフォーマンスを最優先する:コストの高い計算をメモ化し、リストには仮想スクロールを使用します
  7. 機能を整理する:関連するコード(コンポーネント、状態、型、ユーティリティ)をまとめます
  8. フレームワークの慣用パターン:フレームワーク固有のパターン(フック、コンポーザブル、ルーン、サービス)を使用します
React 固有の原則
  1. useSuspenseQuery:新しいコードでの主要なデータ取得パターン
  2. 早期リターンをしない:レイアウトシフトを防ぎます(Suspense 境界を使用します)
  3. インポートエイリアス:@/、~types、~components、~features を使用します
  4. サイズに応じたスタイル:インラインは <100、別ファイルは >100
Vue 固有の原則
  1. Composition API:再利用性を高めるため、Options API より優先します
  2. Script Setup:より簡潔な構文と高いパフォーマンスのために使用します
  3. コンポーザブル:React フックのように、再利用可能なロジックを切り出します
  4. 状態管理には Pinia:TypeScript に対応した最新の Vuex 代替手段です
Svelte 固有の原則
  1. リアクティビティにはルーン:$state、$derived、$effect を使用します(Svelte 5+)
  2. アプリには SvelteKit:ファイルベースのルーティングとサーバー関数を活用します
  3. ストアを最小限にする:ルーンにより、コンポーネントでストアを使う必要性が減ります
  4. プログレッシブエンハンスメント:JavaScript なしで構築し、JavaScript で機能を強化します
Angular 固有の原則
  1. スタンドアロンコンポーネント:v17 以降のデフォルトです(NgModules は避けます)
  2. 状態管理にはシグナル:シンプルなリアクティビティでは RxJS より優先します
  3. OnPush 検出:変更検出を最適化します
  4. 依存性注入:最新の DI には inject() を使用します

クイックリファレンス:ファイル構成

src/
  features/
    my-feature/
      api/
        myFeatureApi.ts       # API service
      components/
        MyFeature.tsx         # Main component
        SubComponent.tsx      # Related components
      hooks/
        useMyFeature.ts       # Custom hooks
        useSuspenseMyFeature.ts  # Suspense hooks
      helpers/
        myFeatureHelpers.ts   # Utilities
      types/
        index.ts              # TypeScript types
      index.ts                # Public exports

  components/
    SuspenseLoader/
      SuspenseLoader.tsx      # Reusable loader
    CustomAppBar/
      CustomAppBar.tsx        # Reusable app bar

  routes/
    my-route/
      index.tsx               # Route component
      create/
        index.tsx             # Nested route

モダンなコンポーネントテンプレート(すぐにコピー)

import React, { useState, useCallback } from 'react';
import { Box, Paper } from '@mui/material';
import { useSuspenseQuery } from '@tanstack/react-query';
import { featureApi } from '../api/featureApi';
import type { FeatureData } from '~types/feature';

interface MyComponentProps {
    id: number;
    onAction?: () => void;
}

export const MyComponent: React.FC<MyComponentProps> = ({ id, onAction }) => {
    const [state, setState] = useState<string>('');

    const { data } = useSuspenseQuery({
        queryKey: ['feature', id],
        queryFn: () => featureApi.getFeature(id),
    });

    const handleAction = useCallback(() => {
        setState('updated');
        onAction?.();
    }, [onAction]);

    return (
        <Box sx={{ p: 2 }}>
            <Paper sx={{ p: 3 }}>
                {/* Content */}
            </Paper>
        </Box>
    );
};

export default MyComponent;

完全なサンプルについては、resources/complete-examples.md をご覧ください。


Vue 3 の開発

コンポーネントのパターン

Composition API - <script setup> を使ったモダンな Vue 3 のアプローチ:

<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import type { User } from '@/types/user'

interface Props {
  userId: number
}

const props = defineProps<Props>()
const emit = defineEmits<{
  update: [user: User]
}>()

const user = ref<User | null>(null)
const isLoading = ref(true)
const displayName = computed(() => user.value?.name ?? 'Unknown')

onMounted(async () => {
  user.value = await fetchUser(props.userId)
  isLoading.value = false
})

function handleUpdate() {
  if (user.value) emit('update', user.value)
}
</script>

<template>
  <div class="user-profile">
    <div v-if="isLoading">Loading...</div>
    <div v-else>
      <h2>{{ displayName }}</h2>
      <button @click="handleUpdate">Update</button>
    </div>
  </div>
</template>

<style scoped>
.user-profile {
  padding: 1rem;
}
</style>

主なパターン:

  • <script setup> - 簡潔な Composition API 構文
  • defineProps<T>() - ジェネリクスによる型安全な props
  • defineEmits<T>() - 型安全なイベント
  • ref()、reactive() - リアクティビティのプリミティブ
  • computed() - 派生状態
  • watch()、watchEffect() - 副作用
  • onMounted、onUnmounted - ライフサイクルフック
状態管理(Pinia)
// stores/userStore.ts
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'

export const useUserStore = defineStore('user', () => {
  // State
  const users = ref<User[]>([])
  const currentUser = ref<User | null>(null)

  // Getters
  const userCount = computed(() => users.value.length)
  const isAuthenticated = computed(() => currentUser.value !== null)

  // Actions
  async function fetchUsers() {
    users.value = await api.getUsers()
  }

  function setCurrentUser(user: User) {
    currentUser.value = user
  }

  return { users, currentUser, userCount, isAuthenticated, fetchUsers, setCurrentUser }
})

// Component usage
<script setup lang="ts">
import { useUserStore } from '@/stores/userStore'
const userStore = useUserStore()
</script>
ルーティング(Vue Router)
// router/index.ts
import { createRouter, createWebHistory } from 'vue-router'

const router = createRouter({
  history: createWebHistory(),
  routes: [
    {
      path: '/',
      component: () => import('@/views/HomeView.vue')
    },
    {
      path: '/users/:id',
      component: () => import('@/views/UserView.vue'),
      props: true
    }
  ]
})

// Component with route params
<script setup lang="ts">
import { useRoute, useRouter } from 'vue-router'

const route = useRoute()
const router = useRouter()
const userId = route.params.id

function goBack() {
  router.push('/')
}
</script>
データ取得のパターン

コンポーザブル - 再利用可能なロジック:

// composables/useUser.ts
import { ref, type Ref } from 'vue'

export function useUser(id: Ref<number>) {
  const user = ref<User | null>(null)
  const loading = ref(false)
  const error = ref<Error | null>(null)

  async function fetchUser() {
    loading.value = true
    try {
      user.value = await api.getUser(id.value)
    } catch (e) {
      error.value = e as Error
    } finally {
      loading.value = false
    }
  }

  watchEffect(() => {
    fetchUser()
  })

  return { user, loading, error, refetch: fetchUser }
}

// Usage in component
<script setup lang="ts">
const props = defineProps<{ userId: number }>()
const { user, loading, error } = useUser(toRef(props, 'userId'))
</script>

Nuxt.js のサーバーサイド:

<script setup lang="ts">
// Nuxt auto-imports composables
const { data: user, pending, error } = await useFetch(`/api/users/${route.params.id}`)
</script>
Vue 3 のベストプラクティス
  • Options API より Composition API - TypeScript のサポートと再利用性が向上します
  • Script setup - 定型コードを減らし、自動登録を実現します
  • Vuex より Pinia - API がシンプルで、TypeScript のサポートに優れ、Composition API と互換性があります
  • コンポーザブル - 再利用可能なロジックを抽出します(React のフックに相当)
  • v-memo - コストの高いレンダリングを最適化します(React.memo に相当)
  • defineOptions - script setup でコンポーネントのオプションを設定します
  • 浅いリアクティビティ - 大きなオブジェクトには shallowRef() を使います

Svelte 5 の開発

Runes ベースのリアクティビティ

Svelte 5 Runes - 新しいリアクティビティシステム:

<script lang="ts">
import type { User } from '$lib/types/user'

interface Props {
  userId: number
  onUpdate?: (user: User) => void
}

let { userId, onUpdate }: Props = $props()

// Reactive state with $state
let user = $state<User | null>(null)
let isLoading = $state(true)

// Derived state with $derived
let displayName = $derived(user?.name ?? 'Unknown')
let userAge = $derived.by(() => {
  if (!user?.birthDate) return null
  return calculateAge(user.birthDate)
})

// Effects with $effect
$effect(() => {
  // Runs when userId changes
  loadUser(userId)
})

async function loadUser(id: number) {
  isLoading = true
  user = await fetchUser(id)
  isLoading = false
}

function handleUpdate() {
  if (user) onUpdate?.(user)
}
</script>

{#if isLoading}
  <div>Loading...</div>
{:else if user}
  <div class="user-profile">
    <h2>{displayName}</h2>
    {#if userAge}
      <p>Age: {userAge}</p>
    {/if}
    <button onclick={handleUpdate}>Update</button>
  </div>
{/if}

<style>
.user-profile {
  padding: 1rem;
}
</style>

Svelte 5 Runes:

  • $state() - リアクティブな状態(リアクティビティ用の let を置き換えます)
  • $derived - 計算値($: を置き換えます)
  • $derived.by() - 複雑な派生状態
  • $effect() - 副作用($: 文を置き換えます)
  • $props() - 分割代入によるコンポーネントの props
  • $bindable() - props の双方向バインディング
  • $inspect() - リアクティブな値のデバッグ
状態管理(Svelte Stores)
// stores/user.ts
import { writable, derived, readonly } from 'svelte/store'

function createUserStore() {
  const { subscribe, set, update } = writable<User[]>([])

  return {
    subscribe,
    setUsers: (users: User[]) => set(users),
    addUser: (user: User) => update(users => [...users, user]),
    removeUser: (id: number) => update(users => users.filter(u => u.id !== id)),
    reset: () => set([])
  }
}

export const users = createUserStore()
export const userCount = derived(users, $users => $users.length)

// Component usage (Svelte 4 style)
<script>
import { users } from '$lib/stores/user'
</script>

<p>Total users: {$users.length}</p>

// Or with runes (Svelte 5)
<script>
import { users } from '$lib/stores/user'
let currentUsers = $state($users)
</script>
SvelteKit のルーティングとデータ読み込み
// src/routes/users/[id]/+page.ts
import type { PageLoad } from './$types'

export const load: PageLoad = async ({ params, fetch }) => {
  const user = await fetch(`/api/users/${params.id}`).then(r => r.json())

  return {
    user
  }
}

// src/routes/users/[id]/+page.svelte
<script lang="ts">
import type { PageData } from './$types'

let { data }: { data: PageData } = $props()
let { user } = $derived(data)
</script>

<h1>{user.name}</h1>

SvelteKit のパターン:

  • +page.svelte - ページコンポーネント
  • +page.ts - ページデータの読み込み(サーバーとクライアントの両方で実行)
  • +page.server.ts - サーバー専用のロード関数
  • +layout.svelte - 共有レイアウト
  • +server.ts - API エンドポイント
  • フォームアクション - プログレッシブエンハンスメント
Svelte 5 のベストプラクティス
  • Runes を使う - モダンなリアクティビティ(Svelte 5 以降)
  • 明示的なリアクティビティ - 暗黙的な let の代わりに $state() を使います
  • $: より $derived** - 意図が明確になり、最適化も向上します
  • コンポーネントの合成 - スロットとスニペットを使います
  • アプリには SvelteKit - ルーティングと SSR を備えたフルスタックフレームワークです
  • ストアを最小限にする - Runes によりコンポーネント内でストアを使う必要性が減ります
  • プログレッシブエンハンスメント - より良い UX のためにフォームアクションを使います

Angular の開発

スタンドアロンコンポーネント(モダン)
// user-profile.component.ts
import { Component, Input, Output, EventEmitter, signal, computed } from '@angular/core'
import { CommonModule } from '@angular/common'
import type { User } from '@/types/user'

@Component({
  selector: 'app-user-profile',
  standalone: true,
  imports: [CommonModule],
  template: `
    <div class="user-profile" *ngIf="!isLoading(); else loading">
      <h2>{{ displayName() }}</h2>
      <p>Age: {{ userAge() }}</p>
      <button (click)="handleUpdate()">Update</button>
    </div>
    <ng-template #loading>
      <div>Loading...</div>
    </ng-template>
  `,
  styles: [`
    .user-profile {
      padding: 1rem;
    }
  `]
})
export class UserProfileComponent {
  @Input({ required: true }) userId!: number
  @Output() userUpdate = new EventEmitter<User>()

  // Signals - reactive primitives
  user = signal<User | null>(null)
  isLoading = signal(true)

  // Computed signals
  displayName = computed(() => this.user()?.name ?? 'Unknown')
  userAge = computed(() => {
    const birthDate = this.user()?.birthDate
    return birthDate ? this.calculateAge(birthDate) : null
  })

  async ngOnInit() {
    this.user.set(await this.fetchUser(this.userId))
    this.isLoading.set(false)
  }

  handleUpdate() {
    const currentUser = this.user()
    if (currentUser) this.userUpdate.emit(currentUser)
  }
}

Angular Signals - 新しいリアクティビティシステム(v16 以降):

  • signal() - 書き込み可能なリアクティブ値
  • computed() - 派生状態
  • effect() - 副作用
  • .set()、.update() - シグナル値を変更します
  • () - シグナル値を読み取ります(関数として呼び出します)
状態管理(サービス + シグナル)
// services/user.service.ts
import { Injectable, signal, computed } from '@angular/core'
import { HttpClient } from '@angular/common/http'

@Injectable({ providedIn: 'root' })
export class UserService {
  private users = signal<User[]>([])
  private currentUser = signal<User | null>(null)

  // Public computed signals
  readonly userCount = computed(() => this.users().length)
  readonly isAuthenticated = computed(() => this.currentUser() !== null)

  constructor(private http: HttpClient) {}

  async fetchUsers() {
    const users = await this.http.get<User[]>('/api/users').toPromise()
    this.users.set(users)
  }

  setCurrentUser(user: User) {
    this.currentUser.set(user)
  }
}

// Component usage
export class MyComponent {
  constructor(public userService: UserService) {}

  // Access in template
  // {{ userService.userCount() }}
}
ルーティング(Angular Router)
// app.routes.ts
import { Routes } from '@angular/router'

export const routes: Routes = [
  {
    path: '',
    loadComponent: () => import('./home/home.component').then(m => m.HomeComponent)
  },
  {
    path: 'users/:id',
    loadComponent: () => import('./users/user-detail.component').then(m => m.UserDetailComponent)
  }
]

// Component with route params
import { Component } from '@angular/core'
import { ActivatedRoute, Router } from '@angular/router'

export class UserDetailComponent {
  userId = signal<string>('')

  constructor(
    private route: ActivatedRoute,
    private router: Router
  ) {
    this.userId.set(this.route.snapshot.paramMap.get('id') ?? '')
  }

  goBack() {
    this.router.navigate(['/'])
  }
}
データ取得(RxJS + Signals)
import { Component, signal } from '@angular/core'
import { HttpClient } from '@angular/common/http'
import { toSignal } from '@angular/core/rxjs-interop'

export class UserListComponent {
  private http = inject(HttpClient)

  // Convert Observable to Signal
  users = toSignal(this.http.get<User[]>('/api/users'), { initialValue: [] })

  // Or manual signal management
  manualUsers = signal<User[]>([])

  async loadUsers() {
    const users = await this.http.get<User[]>('/api/users').toPromise()
    this.manualUsers.set(users)
  }
}
Angular のベストプラクティス
  • スタンドアロンコンポーネント - v17 以降のデフォルトです(NgModules は不要)
  • RxJS より Signals - 状態管理をシンプルにするリアクティビティ
  • toSignal - 必要に応じて Observable を Signal に変換します
  • inject() - モダンな依存性注入(コンストラクターは不要)
  • 遅延読み込み - ルートには loadComponent を使います
  • OnPush 変更検知 - レンダリングを最適化します
  • 型付きフォーム - 型安全性のために FormGroup<T> を使います

フレームワークに依存しないパターン

状態管理の比較
パターンReactVue 3Svelte 5Angular
ローカル状態useStateref()$state()signal()
派生状態useMemocomputed()$derivedcomputed()
副作用useEffectwatch/watchEffect$effect()effect()
グローバル状態Zustand/ContextPiniaストアサービス
非同期状態TanStack QueryVueQuery/ComposablesストアRxJS/Signals
コンポーネントの合成パターン

Props/Events パターン(すべてのフレームワーク):

  • 親から子へ props を介してデータを渡します
  • 子から親へイベントを送信します
  • 単方向のデータフローです

Slots/Children パターン:

// React
<Layout>
  <Header />
  <Content />
</Layout>

// Vue
<Layout>
  <template #header><Header /></template>
  <template #content><Content /></template>
</Layout>

// Svelte
<Layout>
  <Header slot="header" />
  <Content slot="content" />
</Layout>

// Angular
<app-layout>
  <app-header header></app-header>
  <app-content content></app-content>
</app-layout>
ルーティングパターン

ファイルベースのルーティング:

  • Next.js(React)、Nuxt(Vue)、SvelteKit(Svelte)
  • フォルダー構成によってルートが定義されます
  • 動的セグメントには [id] を使用します

プログラムによるルーティング:

  • React Router、Vue Router、Angular Router
  • 設定でルートを定義します
  • より柔軟ですが、記述量が増えます
パフォーマンスの最適化

共通の手法:

  1. コード分割 - ルートや大きなコンポーネントを遅延読み込みします
  2. メモ化 - コストの高い計算結果をキャッシュします
  3. 仮想スクロール - 表示範囲内の項目だけを描画します
  4. デバウンス - コストの高い処理(検索、サイズ変更)を制限します
  5. 画像の最適化 - 遅延読み込み、レスポンシブ画像、最新のフォーマットを使用します
  6. バンドル分析 - サイズの大きな依存関係を特定して削除します

フレームワーク固有の手法:

  • React: React.memo、useMemo、useCallback、Suspense
  • Vue: v-memo、shallowRef、markRaw、KeepAlive
  • Svelte: 自動最適化、$derived、最小限のランタイム
  • Angular: OnPush、signals、trackBy、純粋パイプ

関連スキル

重要:UI/UX デザイン作業では、専門スキルを呼び出してください:

Skill("ui-ux-pro-max")  → UI/UX design, visual hierarchy, color theory, spacing, typography

UI/デザイン:

  • ui-ux-pro-max:UI/UX デザインの原則、視覚的階層、色彩理論、スペーシングシステム、タイポグラフィ。デザインに関する判断、UI の美化、色やフォントの選択、レイアウトの設計にはこれを使用してください。
  • ui-design-system:コンポーネントデザインシステム、アクセシビリティ、デザイントークン
  • ui-styling:Tailwind CSS、すべてのフレームワークに対応したスタイリングパターン

バックエンドとの連携:

  • backend-development:フロントエンドで利用する API パターン
  • better-auth:認証の実装(すべてのフレームワークで動作します)

フルスタックフレームワーク:

  • web-frameworks:Next.js(React)、Nuxt(Vue)、SvelteKit(Svelte)の詳細解説

開発:

  • debugging:フレームワークに依存しないデバッグ戦略
  • refactoring-expert:コード品質とリファクタリングのパターン
  • chrome-devtools:ブラウザーのデバッグ、パフォーマンスのプロファイリング

ui-ux-pro-max を呼び出すタイミング

次の場合は Skill("ui-ux-pro-max") を呼び出してください:

  • 新しい UI コンポーネントやページをデザインするとき
  • カラーパレット、タイポグラフィ、スペーシングを選ぶとき
  • 視覚的階層やレイアウトを改善するとき
  • インターフェースをプロフェッショナル/モダン/すっきりとした見た目にするとき
  • ユーザーが「見栄えが悪い」「一貫性がない」「ごちゃごちゃしている」「デザインの助けが必要」と述べたとき
  • ダッシュボード、管理パネル、SaaS インターフェースを構築するとき

スキルのステータス:段階的な情報開示によるマルチフレームワーク対応

コメント