LaravelのBladeテンプレートをVue.jsっぽく書いてみた

## はじめに

Vue.jsを学習した時に、Vueコンポーネントのコード構成が非常に気に入りました。
そこで、LaravelのBladeテンプレートでも同様のコード構成ができるか検討してみたんですよね。
すると、自分の中では割と納得のいく形にできたんですよ。
なので、今回は私がLaravelのBladeテンプレートで採用したコード構成の例を紹介します:grinning:
*__※Vue.jsのベストプラクティスということではないので、誤解しないようにお願いします。__*

## この記事の対象者

- Laravelを学習されている方
- Bladeテンプレートの書き方のサンプルが欲しい方

## 作業環境
コンピュータ ： Mac mini(2018)
OS ： macOS Catalina バージョン10.15.7
仮想基盤 ： Docker version 19.03.8
ベースイメージ ： AmazonLinux2
言語 ： PHP7.4.11
フレームワーク ： Laravel Version 6.20.5

## Vue.jsのコード構成について
Vue.jsでは、コンポーネント（構成要素や部品の意）を作成する際に、HTML、CSS、Javascriptを１つのファイルに記述します。

#### page_titleコンポーネント
```vue:page_title.vue
<!-- HTML記述部-->
<template>
  <h1>{{title}}</h1>
</template>

<!-- Javascript記述部-->
<script>
export default {
    data(){
        return {
            title: "ページの大見出しだよ"
        }
    }
}
</script>

<!-- CSS記述部-->
<style>
  h1{ color: red; }
</style>
```

#### page_titleコンポーネントを呼び出す親コンポーネント
```vue:body.vue
<template>
    <!-- ここでは、読み込んだコンポーネントをタグとして利用しています -->
    <PageTitle></PageTitle>
    <!-- コンポーネントの読み込みは下のscriptタグ内で行っています -->
</template>

<script>
// ここで、pageタイトルコンポーネントを読み込んでいます。
import PageTitle from 'page_title.vue'

export default {
  components: {
    PageTitle
  }
}
</script>
```

#### 表示されたページ
|![スクリーンショット 2020-11-29 6.29.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/723274/3811dd3d-b29a-e8c3-b0d9-70e0031cd424.png)|
|:--|


## Laravel(Bladeテンプレート)のコード構成
Laravelでは、Bladeテンプレートでディレクティブと呼ばれる記述方法を使用できます。
今回は、@include、@yield、@section等のディレクティブを利用してVueコンポーネントの用にBladeテンプレートを作成します。

#### page_title.vueの代替
```PHP:page_title.blade.php
<!-- HTML記述部 -->
@section('page_title_template')
  <h1 id="page_title">title</h1>
@endsection


<!-- Javascript記述部 -->
@section('page_title_js')
  <script type="text/javascript">

    // windowロード時に呼び出す関数のセット
    window.onload = setH1InnerText;

    // windowロード時にh1タグのテキストを書き換える
    function setH1InnerText(){
      let h1_tag = document.getElementById('page_title');
      h1_tag.innerText = "ページの大見出しだよ";
    }

  </script>
@endsection


<!-- CSS記述部 -->
@section('page_title_css')
  <style>
    h1{ color: red; }
  </style>
@endsection

```

#### body.vueの代替
```PHP:body.blade.php
<!-- ここでpage_title.blade.phpを読み込み -->
@include('page_title')
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">

        <title>Laravel</title>

        <!-- Fonts -->
        <link href="https://fonts.googleapis.com/css2?family=Nunito:wght@200;600&display=swap" rel="stylesheet">

        <!-- CSSの呼び出し -->
            @yield('page_title_css')

    </head>
    <body>
        <!-- HTMLの呼び出し -->
        @yield('page_title_template')

        <!-- Javascriptの呼び出し -->
        @yield('page_title_js')
    </body>
</html>
```

Laravelでは、親ファイルで子コンポーネントのCSSも明記する必要はありますね。

## ホームを部品化してみよう！！
試しに、この構成を利用してLaravelのデフォルトホーム(Welcomeページ)を部品化してみます。

#### LaravelのWelcomeページのコード
```html:welcom.blade.php
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">

        <title>Laravel</title>

        <!-- Fonts -->
        <link href="https://fonts.googleapis.com/css2?family=Nunito:wght@200;600&display=swap" rel="stylesheet">

        <!-- Styles -->
        <style>
             html, body {
                background-color: #fff;
                color: #636b6f;
                font-family: 'Nunito', sans-serif;
                font-weight: 200;
                height: 100vh;
                margin: 0;
            }

            .full-height {
                height: 100vh;
            }

            .flex-center {
                align-items: center;
                display: flex;
                justify-content: center;
            }

            .position-ref {
                position: relative;
            }

            .top-right {
                position: absolute;
                right: 10px;
                top: 18px;
            }

            .content {
                text-align: center;
            }

            .title {
                font-size: 84px;
            }

            .links > a {
                color: #636b6f;
                padding: 0 25px;
                font-size: 13px;
                font-weight: 600;
                letter-spacing: .1rem;
                text-decoration: none;
                text-transform: uppercase;
            }

            .m-b-md {
                margin-bottom: 30px;
            }
        </style>
    </head>
    <body>
        <div class="flex-center position-ref full-height">

            <div class="content">
                <div class="title m-b-md">
                    Laravel
                </div>

                <div class="links">
                    <a href="https://laravel.com/docs">Docs</a>
                    <a href="https://laracasts.com">Laracasts</a>
                    <a href="https://laravel-news.com">News</a>
                    <a href="https://blog.laravel.com">Blog</a>
                    <a href="https://nova.laravel.com">Nova</a>
                    <a href="https://forge.laravel.com">Forge</a>
                    <a href="https://vapor.laravel.com">Vapor</a>
                    <a href="https://github.com/laravel/laravel">GitHub</a>
                </div>
            </div>
        </div>
    </body>
</html>
```
正直、長いですよね:sweat_smile:

#### ブラウザで表示
|![スクリーンショット 2020-11-29 8.14.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/723274/1e808b85-486e-bff1-0812-4985adb74f19.png)|
|:--|


#### 部品化した場合のWelcomeページのコード
```html:welcome.blade.php
<!-- コンポーネントの読み込み -->
@include('view_components.common_all_page_css')
@include('view_components.title')
@include('view_components.links')
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">

        <title>Laravel</title>

        <!-- Fonts -->
        <link href="https://fonts.googleapis.com/css2?family=Nunito:wght@200;600&display=swap" rel="stylesheet">

        <!-- Styles -->
            <!-- 全てのページ共通のCSS -->
            @yield('common_all_page_css')

            <!-- タイトル部分のCSS -->
            @yield('title_css')

            <!-- リンク部分のCSS -->
            @yield('links_css')
    </head>
    <body>
        <div class="flex-center position-ref full-height">

            <div class="content">
                <!-- タイトル部分のHTML -->
                @yield('title_template')

                <!-- リンク部分のHTML -->
                @yield('links_template')

　　　　　　　　　　
　　　　　　　　　<!-- Jarascript -->
                @yield('title_js')
            </div>
        </div>
    </body>
</html>
```

次の３つのコンポーネントに分割しました

#### 全ページ共通のCSSコンポーネント
```html:common_all_page_css.blade.php
@section('common_all_page_css')
  <style>
    html, body {
      background-color: #fff;
      color: #636b6f;
      font-family: 'Nunito', sans-serif;
      font-weight: 200;
      height: 100vh;
      margin: 0;
  }

  .full-height {
      height: 100vh;
  }

  .flex-center {
      align-items: center;
      display: flex;
      justify-content: center;
  }

  .position-ref {
      position: relative;
  }

  .top-right {
      position: absolute;
      right: 10px;
      top: 18px;
  }

  .content {
      text-align: center;
  }
  </style>
@endsection
```

#### タイトル部分のコンポーネント
```html:title.blade.php
@section('title_template')
  <div id="page_title" class="title m-b-md">

  </div>
@endsection

@section('title_js')
<script type="text/javascript">
  // windowロード時に呼び出す関数のセット
  window.onload = setPageTitleInnerText;

// windowロード時にタイトルのテキストを書き換える
function setPageTitleInnerText(){
  let page_title = document.getElementById('page_title');
  page_title.innerText = "Laravel";
}
</script>
@endsection


@section('title_css')
  <style>
    .title {
        font-size: 84px;
    }
    #page_title{
      color: green;
    }
  </style>
@endsection
```

ついでなので、CSSでタイトルの文字色を緑色にしてみました。
まぁ、タイトル文字をJSで代入することはありませんが、あくまでコード構成の例ですのでそこは参考程度に。

#### リンク部分のコンポーネント
```html:links.blade.php
@section('links_template')
<div class="links">
    <a href="https://laravel.com/docs">Docs</a>
    <a href="https://laracasts.com">Laracasts</a>
    <a href="https://laravel-news.com">News</a>
    <a href="https://blog.laravel.com">Blog</a>
    <a href="https://nova.laravel.com">Nova</a>
    <a href="https://forge.laravel.com">Forge</a>
    <a href="https://vapor.laravel.com">Vapor</a>
    <a href="https://github.com/laravel/laravel">GitHub</a>
</div>
@endsection


@section('links_css')
<style>
  .links > a {
    color: #636b6f;
    padding: 0 25px;
    font-size: 13px;
    font-weight: 600;
    letter-spacing: .1rem;
    text-decoration: none;
    text-transform: uppercase;
  }
</style>
@endsection
```

#### ブラウザで表示
|![スクリーンショット 2020-11-29 9.18.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/723274/294bf02a-fea0-2b71-deb8-9abfa93fbb85.png)|
|:--|

無事、タイトルが緑色のWelcomeページが表示されました。


## 最後に
LaravelのBladeテンプレートでは、１ファイル内に1つの@sectionを使用するのが基本のように見受けられます。
なので、今回のコード構成はパフォーマンス面で不都合があるのかもしれません。
とはいえ、恐らく、Laravelだけでサービスを開発する場合は、あまり影響がないと思っています。
そこまでパフォーマンスを求めるサービスをLaravelだけで開発することは珍しいでしょうからね:sweat_smile:


この記事が誰かのお役に立てると嬉しいです。
それでは、最後まで読んでいただきありがとうございます:blush: