🐘

Laravel bladeの @yield、@section、@include、@component、@stuckの違いってなんだ?

2022/02/24に公開

ざっくりと用途の違いを羅列

なんとなくのイメージでは、どれもテンプレート使う時に一部をパーツ化するやつ?
という曖昧な印象を受けたので違いを調べて見ました。

前提として共通化されたbladeファイルで親が「app.blade.php」で子が「child.blade.php」があるとします。

@yield →  主に親テンプレートで使う。常にそれぞれのページで生み出すコンテンツに使う。
@section → 共通化したい処理を書く。内容を継承して個別の内容も追加できる。
@include → シンプルに他のbladeを読み込む。パーツ化に便利。
@component → 汎用性を持って他のbladeを読み込む。パーツ化に便利。
@stuck → jsとかcssをページごとに読み込むのに便利。

では、それぞれの詳細を見ていきましょう。


@yield

ページごとに内容が変わるページタイトルやメインコンテンツに使います。

子テンプレート(child.blade.php)

@extends('layouts.app')

@section('title','ページごとに違うタイトル')

@section('content')
ページごとに違うコンテンツ
@endsection

親テンプレート(app.blade.php)

<html>
    <head>
        <title>アプリ名 - @yield('title')</title>
    </head>
    <body>
        <div class="container">
            @yield('content')
        </div>
    </body>
</html>

@sectionで指定したセクション名で指定して、@yieldで内容を呼び出します。

@section

親テンプレート(app.blade.php)
親テンプレートに書いて継承したい場合は@showで締める。

<html>
    <head>
        <title>アプリ名 - @yield('title')</title>
    </head>
    <body>
        <div class="container">

          @section('notice')
          <p>共通のお知らせ</p>
          @show

          @yield('content')
        </div>
    </body>
</html>

子テンプレート(child.blade.php)

@extends('layouts.app')

@section('title','ページごとに違うタイトル')

// 親テンプレートで書いた「notice」を継承してオリジナルのコンテンツを追加
@section('notice')
  @parent
  <p>個別のお知らせ</p>
@endsection

@section('content')
ページごとに違うコンテンツ
@endsection

noticeの部分には以下の様に表示される。

<p>共通のお知らせ</p>
<p>個別のお知らせ</p>

公式では「@@parent」と書いているが@parentじゃないと動かない。
https://readouble.com/laravel/6.x/ja/blade.html

@include

他のbladeに書いたテンプレートをそのまま呼び出せます。
呼び出し元のテンプレートで使える変数はそのまま使えるのでシンプルにパーツ化したい場合は
includeを使うのが良いです。

例えば、フォームからの登録が成功したことを通知する部分をパーツ化する場合
shared/success.blade.php

@if (session('result'))
<div class="alert alert-success alert-dismissible fade show" role="success">
    {{ session('result') }}
    <button type="button" class="close" data-dismiss="alert" aria-label="Close">
	<span aria-hidden="true">&times;</span>
    </button>
</div>
@endif

呼び出したいテンプレートでincludeディレクティブを書けば呼び出せます。
ここで使える変数ならそのまま使えるのが便利です。

<!-- 登録完了通知 -->
@include('shared.success')

関連するディレクティブで他にも

@includeIf
@includeWhen
@includeUnless
@includeFirst

と言った物があります。
しかし、今の所使い所がちょっと浮かばないので必要に迫られた場合にまた記事にしたいと思います。

@component

@componentは@includeや@sectionとも似ています。
違いが一番わかりづらいかも知れません。

先程@includeで使用した例をそのまま@componentに置き換えてみます。

<!-- 登録完了通知 -->
@if (session('result'))

 @component('components.success')
    // ここに書いた内容がコンポーネントの$slotに反映される
    {{ session('result') }}
 @endcomponent

@endif

components/success.blade.php

<div class="alert alert-success alert-dismissible fade show" role="success">
    {{-- $slotに呼び出し元の記述が反映される --}}
    {{ $slot }}
    <button type="button" class="close" data-dismiss="alert" aria-label="Close">
	<span aria-hidden="true">&times;</span>
    </button>
</div>

急に出てきた$slotに戸惑いましたが、わかればどうということはないです。

@includeでは呼び出し元のテンプレートで使う変数がそのまま使えるので一見便利そうですが
ページごとに違う変数名になる可能性を考えると@componentの方がアプリ全体で使うパーツには適しているのかも知れません。

ただしそれは、htmlのパーツを細かく分けて一部だけ変わるパターンに限るような気もします。
ここが使い分けどころなのかも知れませんね。

@stuck、@push

@stuckディレクティブは@pushとセットで使用することで例えば共通で使用するjavascript以外の
jsファイルを読み込みたい場合に便利です。

親テンプレート(app.blade.php)

<!-- Scripts -->
<script src="{{ asset('js/app.js') }}" defer></script>
// 子テンプレートでpushされたjsを読み込む
@stack( 'script' )

子テンプレート(child.blade.php)

// 親テンプレートに必要なjsをpush
@push( 'script' )
<script src="{{ asset('js/sample.js') }}" defer></script>
@endpush

親で全部jsファイルを読み込むと不要なファイルを読み込んでバグが起きたり重くなったりする
と言うのがありますからこういった処理が必要になってきます。


まとめ

実際に使い分けに悩むのは@includeと@componentくらいで他のディレクティブは
個性が全然違うことがわかりました。

※検証はLaravel6系です。

Discussion