Vue.jsを参考に、BladeファイルにJavaScript, CSSをまとめてみる。

# Vue.jsの開発者的メリット
- HTML, JavaScript, CSSを1つのファイルに記述するというのがシンプルで良い
- scopedが便利
- コードが複雑化しにくい

そして思ったのです。
**これって普通のアプリケーションにある程度応用できそう。**

# 実際に考えてみる
ちょっと考えてみました。方針としては、

- 仕様に応じてBladeのディレクトリ構成をシンプルに考える
- JavaScript, CSSはBladeのディレクトリ構成に合わせる。
- 影響範囲は対応するBladeファイル内にとどまるように気をつける。

## Bladeファイルのディレクトリ構成
サンプルです。実際には画面仕様などにより異なるはずです。

- ルール
    - views/直下は、layouts/および、メニューに対応するディレクトリ（menus/を作ろうと思ったが階層が深くなりすぎるのが嫌なので却下）
    - メニューごとのディレクトリには、ページに対応するbladeファイルがある
    - メニューごとのディレクトリには、parts/があり、ページを構成する部品を格納する。
    - formやbuttonなどの部品はparts/に格納する。
    - ページに対応するファイルからは、parts/配下のファイルをincludeする。
    - parts/配下のファイルからは、parts/配下のファイルををincludeする
    - ページに対応するファイルから、parts/配下の部品をincludeしない
    - 同階層のファイルをincludeしない

```:resources/
- views/
    - layouts/
        - app
        - parts/
            - header
            - footer
    - home/
        - index
        - parts/
            - eye_catching
            - new_product
    - product/
        - index
        - show
        - edit
        - parts/
            - product
            - details
    - account/
        - show
        - edit
        - parts/
            - table
    - parts/
        - forms/
            - radio
            - checkbox
        - buttons/
            - search
            - submit
            - delete
        - flash_message
```
## JavaScriptとCSS(Sass)は、Bladeファイルと同じディレクトリ構成
JavaScript, CSS(Sass)はBladeファイルと同じディレクトリ構成とします。

```:resources/
- js
    - modules/
        - confirm
    - layouts/
        - app
        - parts/
            - header
    - ...

- sass
    - layouts/
        - app
        - parts/
            - header
    - ...
```

# Bladeファイルに、JavaScriptとCSSを記述する。
まず、レイアウトファイル

```html:layout/app.blade.php
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">

  {{-- CSRF Token --}}
  <meta name="csrf-token" content="{{ csrf_token() }}">

  {{-- title --}}
  <title>@yield('title', 'laravel app')</title>

  {{-- 対応するCSSファイル --}}
  <link rel="stylesheet" href="{{ mix('css/layouts/app.css') }}">

  {{-- include先のcssファイルがここに追加される --}}
  @yield('css')
</head>
<body>

  @include('layouts.parts.header')

  <div id="container">
    @yield('content')
  </div>

  @include('layouts.parts.footer')

  {{-- 対応するJSファイル --}}
  <script src="{{ mix('js/layout/app.js') }}"></script>

  {{-- include先のJSファイルがここに追加される --}}
  @yield('js')

</body>
</html>
```
そして、includeするファイル

```html:layouts/parts/header.blade.php
@section('css')
  <script src={{ mix('/js/layouts/parts/header.css') }}>
@endsection

@section('js')
  <script src={{ mix('/js/layouts/parts/header.js') }}>
@endsection

<header>
  <div>ヘッダー</div>
</header>
```

これでOKと思ったのですが、これでは、include先に記述されたJavaScript, CSSが反映されません。
そこで@stack - @pushを利用します。

```html:layout/app.blade.php
<head>
  (省略)

  {{-- include先のcssファイルがここに追加される --}}
  @stack('css')
</head>

<body>
  (省略)

  {{-- include先のJSファイルがここに追加される --}}
  @stack('js')
</body>
```

```html:layouts/parts/header.blade.php
@push('css')
  <script src={{ mix('/js/layouts/parts/header.css') }}>
@endpush

@push('js')
  <script src={{ mix('/js/layouts/parts/header.js') }}>
@endpush

<header>
  <div>ヘッダー</div>
</header>
```
これで、include先で記述されたJavaScript, CSSファイルが反映されるようになりました。
ただし、同じファイルを複数回includeすると回数分JavaScript, CSSが読み込まれてしまいます。

```:home/index.blade.php
  // deleteボタンを複数回includeすると回数分JavaScript, CSSが読み込まれる。
  @include('parts.buttons.delete')
  @include('parts.buttons.delete')
  @include('parts.buttons.delete')
```

ですので、1度だけ読み込むように、カスタムディレクティブを設定します。
[参考: bladeのcomponent化による再利用](https://qiita.com/ttn_tt/items/9a3256101f50894827a2)

```php:app/Providers/AppServiceProvider.php
/**
 * Bootstrap any application services.
 *
 * @return void
 */
public function boot()
{
    \Blade::directive('pushonce', function ($expression) {
        $var = '$__env->{"__pushonce_" . md5(__FILE__ . ":" . __LINE__)}';

        return "<?php if(!isset({$var})): {$var} = true; \$__env->startPush({$expression}); ?>";
    });

    \Blade::directive('endpushonce', function ($expression) {
        return '<?php $__env->stopPush(); endif; ?>';
    });
}
```

```html:layouts/parts/header.blade.php
@pushonce('css')
  <script src={{ mix('/js/layouts/parts/header.css') }}>
@endpushonce

@pushonce('js')
  <script src={{ mix('/js/layouts/parts/header.js') }}>
@endpushonce

<header>
  <div>ヘッダー</div>
</header>
```

レイアウトファイルも少し修正します。
includeする前に@stackを記述するとその時点では、pushされるファイルがないので、
include先のファイルが反映されません。
そのため、bodyの閉じタグの直前に記述します。(ここは少し悩ましいところです。。。)
[参考: Blade Stacks in Sub-View](https://github.com/laravel/framework/issues/13998)

```:layout/app.blade.php
<head>
  (省略)
  
  // ここだとinclude先のファイルが反映されない
  {{-- @stack('css') --}}
</head>

<body>
  (省略)

  {{-- include先のcssファイルがここに追加される --}}
  @stack('css')

  {{-- include先のJSファイルがここに追加される --}}
  @stack('js')
</body>
```
これで、include先のファイルも反映されるようになります。

なお、CSSは原則そのファイルにしかスタイルが適用されないよう、
スコープを限定するように記載します。

```scss:home/index.scss

// home/index.blade.php
// <div class="home-index">
//   <div class="test-class">
//     コンテンツ
//　 </div>
// </div>

.home-index { // スコープを限定する
  .test-class {
    
  }
}
```
以上です。

# 参考
[bladeのcomponent化による再利用](https://qiita.com/ttn_tt/items/9a3256101f50894827a2)
[Blade Stacks in Sub-View](https://github.com/laravel/framework/issues/13998)
[Blade Templates](https://laravel.com/docs/6.x/blade)