Laravel+Vue.jsで作成したSPAサイトでOGP対応

#はじめに
個人開発でLaravel5.5とVue.jsを使用して作成したSPAサイトがあり、長らく放置していたのですが、最近勉強を兼ねて少しリファクタリングをしようかなと思いました。

[ラブライブ専用掲示板](https://lovelivebbs.jp)

上記が作成したサイト（掲示板）になります。
治すべき部分はたくさんあるのですが、まず気になったのがTwitterカードの表示です。
特に、掲示板のスレッドURLをツイートした際に、デフォルトの情報が表示されてしまうのが見た目の部分で致命的でした。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254954/a4475d41-aa84-c23b-8d30-9e0baada6e36.png)

## まずやったこと
最初にapiでスレッドの情報を読み込んだ後のタイミングで`querySelector`を使用して動的にmetaタグを変更しました。

```javascript
//APIでデータ取得後
document.title = this.thread_header[0].title + ' | LoveLiveBBS'; 
document.querySelector("meta[property='og:title']").setAttribute('content', this.thread_header[0].title + ' | LoveLiveBBS');
document.querySelector("meta[property='description']").setAttribute('content', this.thread_response[0]['writing']);
document.querySelector("meta[property='og:description']").setAttribute('content', this.thread_response[0]['writing']);
```

当たり前ですが、これでは書き換わる前のmetaが読み込まれてしまうので結果は変わりませんでした。。。

今回は`/thread/[thread_id]`ページのみの修正ということで、その為だけにわざわざプリレンダリングやSSRはしたくないなと思い、以下のような対応を行いました。

## Laravelのbladeファイルでの対応

`/thread/[thread_id]`にアクセスされたときにLaravel側でデータを取得してmetaタグに設定する方法をとりました。

### bladeファイルの作成
修正前は初回アクセス時にのみ使用する`spa.blade.php`のみが存在していました。
スレッドページ用の`thread.blade.php`を作成しました。

```PHP:resources/views/threadPage.blade.php
<!DOCTYPE html>
<html lang="ja">
<head>
    //コントローラーで作成したデータを表示
    <title>{{ $title }}</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <meta name="csrf-token" content="{!!csrf_token()!!}">
    <link href="https://fonts.googleapis.com/css?family=Kosugi+Maru&display=swap&subset=japanese" rel="stylesheet">
　　//コントローラーで作成したデータを表示
    <meta name="description" content="{{ $description }}">
    <meta property="og:url" content="https://lovelivebbs.jp" />
　　//コントローラーで作成したデータを表示
    <meta property="og:title" content="{{ $title }}" />
    <meta property="og:type" content="website">
　　//コントローラーで作成したデータを表示
    <meta property="og:description" content="{{ $description }}" />
    <meta name="twitter:card" content="summary" />
    <meta name="twitter:site" content="@lovelivebbs" />
　　//コントローラーで作成したデータを表示
    <meta property="og:site_name" content="{{ $title }}" />
    <meta property="og:locale" content="ja_JP" />
</head>
<body>
<div id="app">
    <app></app>
</div>

<script src="{{ mix('js/app.js') }}"></script>
</body>
</html>
```

### コントローラーにスレッドページ用のメソッドを追加
`spa.blade.php`を返す役割だけのコントローラーに`threadPage`メソッドを追加しました。

```php:app/Http/Controllers/SpaController.php
namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Response;
use App\Thread;
use Exception;
use Illuminate\Support\Facades\Log;

class SpaController extends Controller
{
    public function index()
    {
        return view('spa');
    }

    /**
     * スレッドページのみmetaタグをbladeファイルで設定
     * @param $id
     * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
     */
    public function threadPage($id) {
        $meta = [
            'title' => 'LoveLive!BBS!',
            'description' => '当サイトはラブライブシリーズ専用掲示板です。'
        ];
        try {
            $threadDetail = Thread::where('id', $id)->first();
            if (is_null($threadDetail)) {
                return view('threadPage', $meta);
            } else {
                //取得したデータをmetaタグに設定
                $response1 = Response::where('thread_id', $id)->first();
                $meta['title'] = $threadDetail->title . ' | LoveLive!BBS!';
                $meta['description'] = $response1->writing;
                return view('threadPage', $meta);
            }
        } catch (Exception $exception) {
            Log::error($exception);
            return view('spa');
        }
    }
}
```
DBから取得した値をまとめてbladeファイルに渡しています。

### ルーティングの追加
```php:routes/web.php
+ Route::get('/thread/{id}', 'SpaController@threadPage');

Route::get('/{any}', 'SpaController@index')->where('any', '.*');
```

### 結果
上記の変更を加えた後に、Twitterにスレッドのリンクを張ってみました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254954/846fab69-bb56-078b-90a8-8799e78af843.png)

無事、スレッドタイトルと内容が反映されました！🎉🎆🌌
こうなると次はOGPの画像生成をしたくなりますね。

あまり良いやり方ではないかもしれませんが、なんとかなりました。
もっといい方法があったら教えてください。

後このサイトですが、残念ながら全然使われてないのでラブライブが好きな方はぜひ書き込みだけでもしてみてください。。。（切実）
[ラブライブ専用掲示板](https://lovelivebbs.jp)
[ラブライブ専用掲示板:ABOUTページ](https://lovelivebbs.jp/about)

よろしくお願いします。💦