タイトル: ヘッダー/フッターの共通コンポーネント
SEOタイトル: 【Vue.js】ヘッダー/フッターの共通コンポーネントの作り方
この本文には実行可能なタグ等が含まれるため、安全のためHTMLソースとして表示しています。本文データは変更していません。
<table border="1" cellpadding="1" cellspacing="1" style="width:1000px">
<thead><tr><th style="background-color:#f39c12;color:#ffffff;text-align:left">この記事の要点</th></tr></thead>
<tbody><tr><td style="background-color:#fef9e7"><ul>
<li>Web アプリのヘッダー / フッターを<strong>共通コンポーネント</strong>として再利用する方法</li>
<li><strong>Laravel Blade</strong>: <code>@include</code> / <code>@yield</code> / <code>@extends</code></li>
<li><strong>Vue.js</strong>: コンポーネント import + <code><Header /></code> / <code><Footer /></code></li>
<li><strong>React</strong>: コンポーネント import + JSX で配置</li>
<li><strong>Next.js / Nuxt.js</strong>: Layout コンポーネントで自動適用</li>
</ul></td></tr></tbody>
</table>
<p> </p>
<h2>Laravel Blade のパターン</h2>
<h3>方法 1: @extends と @yield(推奨)</h3>
<pre style="background:#0f172a;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;font-family:Consolas,Monaco,monospace;font-size:0.9em;line-height:1.5"><code><!-- resources/views/layouts/app.blade.php -->
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>@yield('title', 'デフォルトタイトル')</title>
@yield('styles')
</head>
<body>
@include('partials.header')
<main>
@yield('content')
</main>
@include('partials.footer')
@yield('scripts')
</body>
</html></code></pre>
<pre style="background:#0f172a;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;font-family:Consolas,Monaco,monospace;font-size:0.9em;line-height:1.5"><code><!-- resources/views/partials/header.blade.php -->
<header class="site-header">
<h1><a href="{{ route('home') }}">My Site</a></h1>
<nav>
<ul>
<li><a href="{{ route('home') }}">ホーム</a></li>
<li><a href="{{ route('users.index') }}">ユーザー</a></li>
@auth
<li><a href="{{ route('profile') }}">マイページ</a></li>
<li><a href="{{ route('logout') }}">ログアウト</a></li>
@else
<li><a href="{{ route('login') }}">ログイン</a></li>
@endauth
</ul>
</nav>
</header>
<!-- resources/views/partials/footer.blade.php -->
<footer class="site-footer">
<p>© {{ date('Y') }} My Site</p>
</footer></code></pre>
<pre style="background:#0f172a;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;font-family:Consolas,Monaco,monospace;font-size:0.9em;line-height:1.5"><code><!-- resources/views/users/index.blade.php -->
@extends('layouts.app')
@section('title', 'ユーザー一覧')
@section('content')
<h2>ユーザー一覧</h2>
@foreach ($users as $user)
<p>{{ $user->name }}</p>
@endforeach
@endsection
@section('scripts')
<script src="{{ asset('js/users.js') }}"></script>
@endsection</code></pre>
<h3>方法 2: コンポーネント(Laravel 7+)</h3>
<pre style="background:#0f172a;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;font-family:Consolas,Monaco,monospace;font-size:0.9em;line-height:1.5"><code><!-- resources/views/components/site-header.blade.php -->
<header class="site-header">
<h1>{{ $title ?? config('app.name') }}</h1>
<nav>{{ $slot }}</nav>
</header>
<!-- 使い方 -->
<x-site-header title="My Page">
<ul>
<li><a href="/">ホーム</a></li>
</ul>
</x-site-header></code></pre>
<h2>Vue.js のパターン</h2>
<h3>Vue 3 + Composition API</h3>
<pre style="background:#0f172a;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;font-family:Consolas,Monaco,monospace;font-size:0.9em;line-height:1.5"><code><!-- components/AppHeader.vue -->
<template>
<header class="site-header">
<h1>
<router-link to="/">{{ title }}</router-link>
</h1>
<nav>
<router-link to="/">ホーム</router-link>
<router-link to="/users">ユーザー</router-link>
<span v-if="user">
{{ user.name }} さん
<button @click="logout">ログアウト</button>
</span>
<router-link v-else to="/login">ログイン</router-link>
</nav>
</header>
</template>
<script setup>
defineProps({ title: { type: String, default: "My Site" } });
import { useAuthStore } from "@/stores/auth";
const authStore = useAuthStore();
const user = computed(() => authStore.user);
function logout() {
authStore.logout();
}
</script></code></pre>
<pre style="background:#0f172a;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;font-family:Consolas,Monaco,monospace;font-size:0.9em;line-height:1.5"><code><!-- App.vue -->
<template>
<AppHeader title="My Site" />
<main>
<router-view />
</main>
<AppFooter />
</template>
<script setup>
import AppHeader from "./components/AppHeader.vue";
import AppFooter from "./components/AppFooter.vue";
</script></code></pre>
<h2>React のパターン</h2>
<pre style="background:#0f172a;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;font-family:Consolas,Monaco,monospace;font-size:0.9em;line-height:1.5"><code>// components/Header.tsx
import { Link } from "react-router-dom";
import { useAuth } from "../hooks/useAuth";
export default function Header({ title = "My Site" }) {
const { user, logout } = useAuth();
return (
<header className="site-header">
<h1><Link to="/">{title}</Link></h1>
<nav>
<Link to="/">ホーム</Link>
<Link to="/users">ユーザー</Link>
{user ? (
<>
<span>{user.name} さん</span>
<button onClick={logout}>ログアウト</button>
</>
) : (
<Link to="/login">ログイン</Link>
)}
</nav>
</header>
);
}</code></pre>
<pre style="background:#0f172a;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;font-family:Consolas,Monaco,monospace;font-size:0.9em;line-height:1.5"><code>// App.tsx
import Header from "./components/Header";
import Footer from "./components/Footer";
function App() {
return (
<>
<Header title="My Site" />
<main>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/users" element={<Users />} />
</Routes>
</main>
<Footer />
</>
);
}</code></pre>
<h2>Next.js の Layout</h2>
<pre style="background:#0f172a;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;font-family:Consolas,Monaco,monospace;font-size:0.9em;line-height:1.5"><code>// app/layout.tsx (App Router)
import Header from "@/components/Header";
import Footer from "@/components/Footer";
export default function RootLayout({ children }) {
return (
<html lang="ja">
<body>
<Header />
<main>{children}</main>
<Footer />
</body>
</html>
);
}
// → 全ページで自動的に Header / Footer が表示される</code></pre>
<h2>Nuxt.js の Layout</h2>
<pre style="background:#0f172a;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;font-family:Consolas,Monaco,monospace;font-size:0.9em;line-height:1.5"><code><!-- layouts/default.vue -->
<template>
<div>
<AppHeader />
<main>
<slot />
</main>
<AppFooter />
</div>
</template>
<!-- pages/users.vue → 自動的に default レイアウトが適用 -->
<template>
<div>
<h2>ユーザー一覧</h2>
</div>
</template>
<!-- 別レイアウトを指定 -->
<script setup>
definePageMeta({ layout: "auth" }); // layouts/auth.vue を使う
</script></code></pre>
<h2>Web Components(純粋 HTML / JS)</h2>
<pre style="background:#0f172a;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;font-family:Consolas,Monaco,monospace;font-size:0.9em;line-height:1.5"><code>// header.js
class SiteHeader extends HTMLElement {
connectedCallback() {
this.innerHTML = `
<header>
<h1><a href="/">My Site</a></h1>
<nav>
<a href="/">ホーム</a>
<a href="/users">ユーザー</a>
</nav>
</header>
`;
}
}
customElements.define("site-header", SiteHeader);
// HTML
<site-header></site-header></code></pre>
<h2>共通コンポーネント設計の Tips</h2>
<ul>
<li><strong>props で柔軟性</strong>: タイトル・サブメニュー等を props で受け取って汎用化</li>
<li><strong>slot を活用</strong>: ヘッダー内に任意の要素を差し込めるように</li>
<li><strong>認証状態に応じて</strong>: ログイン中はマイページリンク、未ログインはログインリンク</li>
<li><strong>アクティブリンクのハイライト</strong>: 現在ページに応じてスタイル変更</li>
<li><strong>SP/PC で別表示</strong>: メディアクエリ + display 切替、または別コンポーネント</li>
<li><strong>SEO への配慮</strong>: <code><header></code>, <code><nav></code>, <code><footer></code> 等の意味的タグ</li>
</ul>
<h2>関連記事</h2>
<ul>
<li><a href="/article/20923">テンプレートの作成と共通化</a></li>
<li><a href="/article/21303">aリンクの貼り方と動的URLの作成</a></li>
</ul>