Laravel × React + TypeScript で SPA の開発環境を構築（Laravel Sail を利用）

[Laravel Sail](https://readouble.com/laravel/9.x/ja/sail.html) を用いて、[Laravel9](https://laravel.com/docs/9.x/) × [React18](https://ja.reactjs.org/) の SPA の開発環境を構築したので紹介させていただきます。

Laravel × React で SPA を作成する記事は少なくないですが、Sail のデフォルトの設定を変更する方法（ Docker の知識なしで変更できる範囲で）や、Sass の導入方法なども併せてまとめているので、読んでいただけると嬉しいです。

2022年6月から、新たに Laravel のインストールした際のデフォルトのビルド・ツールとして、[vite(ヴィート)](https://ja.vitejs.dev/)が採用されました。本記事でも、[Laravel Mix](https://laravel.com/docs/9.x/mix) ではなく、vite を使用しています。

https://laravel.com/docs/9.x/vite

# Laravel Sail とは
　[Laravel Sail](https://readouble.com/laravel/9.x/ja/sail.html) は、**Docker の経験を必要とせず**、PHP、MySQL、Redis などを用いた Laravel アプリケーションの開発環境を Docker 上で構築することができるツールです。
（ただし、MySQL の代わりに MongoDB を使用したい場合など、Sail 側で用意されていない設定に変更する場合には Docker の知識が必要になることがあります。）


# ローカル環境
+ macOS Monterey v12.3 (MacBook Air M1 2020)
+ Docker v20.10.8


# Docker Desktopのインストール
Laravel Sail を用いた開発環境の構築には、Docker が必要です。
OS 環境によってインストールの方法が異なりますが、macOS か Windows の場合、Docker Desktop をインストールすることで簡単に Docker を利用できます。
以下のサイトなどを参考にしてください。

https://matsuand.github.io/docs.docker.jp.onthefly/desktop/mac/install/

https://matsuand.github.io/docs.docker.jp.onthefly/desktop/windows/install/

https://docs.docker.jp/desktop/index.html#desktop-download-and-install


Dockerについて学びたい方は以下の記事が参考になるかもしれません。Docker の基礎概念と用語の解説から、docker-compose.yml を用いた複数コンテナの定義・実行まで詳しくまとめられており、基礎固めができると思います。

https://zenn.dev/suzuki_hoge/books/2022-03-docker-practice-8ae36c33424b59

# Laravel の開発環境の作成
Laravel の開発環境を構築していきます。

ターミナル開き、任意のディレクトリに移動してください。
以下のコマンドで Laravel の開発環境が構築できます。
```bash:ターミナル
curl -s "https://laravel.build/example-spa?with=mysql,mailhog" | bash
```
+ ここではアプリケーション名を『 example-spa 』にしていますが、お好きな名前に変えていただいても問題ありません。
+ 『 ?with=mysql,mailpit 』をつけること、MySQL と MailHog を利用できます。（ ?with= をつけない場合、mysql、redis、meilisearch、mailpit、selenium が設定されます。）

+ MySQL の代わりに PostgreSQL を使いたい場合は、『 mysql 』を『 pgsql 』に変更してください。
?with= 以降に設定できるサービスについて詳しく知りたい方は[公式サイト(installation choosing-your-sail-services)](https://laravel.com/docs/9.x/installation#choosing-your-sail-services)を参考にしてください。



Laravel アプリケーションが作成されると、Password を求められるので パソコンの Password を入力します。
```bash:ターミナル
Application ready! Build something amazing.
Sail scaffolding installed successfully.

Please provide your password so we can make some final adjustments to your application's permissions.

Password:
```
Password 入力後、以下の文章が表示されると思いますので、

```bash:ターミナル
Thank you! We hope you build something incredible. 
Dive in with: cd example-spa && ./vendor/bin/sail up
```

指示通りにタイプし、Sail を起動します。

```bash:ターミナル
cd example-spa && ./vendor/bin/sail up
```

Web ブラウザで `http:// localhost` にアクセスし、Laravel と書かれたページが表示されるか確認します。
[Ctrl] + [C]キー で Sail を一度停止します。

Laravel Sail で構築したアプリケーションでは、Sail コマンドを用いて開発していきますが、毎回、`./vendor/bin/sail` を打つのは面倒なので、シェルエイリアスを設定します。
詳しくは、[公式サイト(sail configuring-a-shell-alias)](https://laravel.com/docs/9.x/sail#configuring-a-shell-alias)を参考にしてください。

以下のようにシェルエイリアスを設定することで、`./vendor/bin/sail` を `sail` に変更しても問題なく起動します。
```bash:ターミナル
alias sail='[ -f sail ] && bash sail || bash vendor/bin/sail'
sail up -d
```
`sail up` の後に `-d` をつけていますが、バックグラウンドで Sail を起動するためのオプションで、新たにターミナルを開かなくても他のコマンドを入力することができます。


# Reactの導入（TypeScript対応）
それでは、React の開発環境の導入を行います。

今回は、React の開発環境を Laravel の開発環境内に構築します。
具体的には、 `resources` ディレクトリ内に `ts` ディレクトリを作成し、その中で、Laravel のインストールと同時にインストールされた node や npm 、npx を利用しながら、React の開発を行います。




構築した Laravel の開発環境に node や npm、npxがインストールされているか確認します。
```bash:ターミナル
sail node --version
# v16.17.0
sail npm --version
# 8.18.0
sail npx --version
# 8.18.0
```

## React に必要なモジュールのインストール

以下のコマンドで、`package.json` ファイル内に記述されたパッケージ（ Laravel で Vite を使うのに必要なものなど）を一括でインストールされます。
`node_modules`ディレクトリ と `package-lock.json` も同時に作成されます。
``` bash:ターミナル
sail npm install
```

React に必要なモジュールのインストールします。

```bash:ターミナル
sail npm install -D react react-dom @types/react @types/react-dom
sail npm install -D @vitejs/plugin-react

# Reactでルーティングを実装したい場合は、以下のパッケージもインストール
# sail npm install -D react-router-dom
# sail npm install -D @types/react-router-dom
```

## typescriptを導入
```bash:ターミナル
sail npm install -D typescript

sail npx tsc --init --jsx react-jsx
```
`tsconfig.json` がルートディレクトリに作成されます。


## viteの設定
`vite.config.js` を編集します。
```js:vite.config.js
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import react from '@vitejs/plugin-react'; // <- 追加

export default defineConfig({
    plugins: [
        laravel({
            // input: ['resources/css/app.css', 'resources/js/app.js'],
            refresh: true,

            // typescriptとsassが使えるように変更
            input: [
                'resources/sass/app.scss', 
                'resources/ts/index.tsx'
            ],
        }),
        react(), // <- 追加
    ],
});
```
`input`内で、TSXファイルと、Sassファイルのエントリーポイントを指定します。
create-react-appでReactの環境を構築した場合、index.tsx がアプリケーションへのエントリポイント（パス）になるので、それに合わせていますが、任意のファイル名に変更も問題ありません。

`refresh: true` は、以下のような効果があるみたいです。
>refreshオプションがtrueの場合、resources/views/\*\*、app/View/Components/\*\*、routes/\*\*のファイルを保存すると、npm run dev実行中に、ブラウザがページのフル再ロードを行うようになります。
> **参考：[Laravel 和訳翻訳ドキュメント - vite](https://readouble.com/laravel/9.x/ja/vite.html)**

`react()`を追加することで Fast Refresh などの機能を取り込めます。詳しくは[@vitejs/plugin-react](https://github.com/vitejs/vite/tree/main/packages/plugin-react#readme)を参照してください。

## index.blade.php の作成
`views` ディレクトリ内に `index.blade.php` を作成します。

`welcome.blade.php` は不要なので削除してください。
```php:resouces/views/index.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>sample-api</title>

        {{-- react に変更があったとき自動で --}}
        @viteReactRefresh
        @vite(['resources/sass/app.scss', 'resources/ts/index.tsx'])

    </head>

    <body class="antialiased">
        <div id="app"></div>
    </body>
</html>
```
`@vite()` 内に TSXファイル と、 Sassファイル のエントリーポイントを設定します。
`@viteReactRefresh`  を `@vite` の前に呼び出す必要があります。

## index.tsxの作成
resourcesディレクトリ内に ts ディレクトリを追加し、その中に index.tsx を作成します。
```tsx:resources/ts/index.ts
import React from 'react';
import { createRoot } from 'react-dom/client';

const container = document.getElementById('app');
const root = createRoot(container!); // createRoot(container!) if you use TypeScript

root.render(
  // <React.StrictMode>
    <div className="text-red">
        Hello World
    </div>
// </React.StrictMode>,
);
```

## web.php の編集
`welcome` を `index` に変更します。
```php:routes/web.php
// Route::get('/', function () {
//     return view('welcome');
// });
Route::get('/', function () {
    return view('index');
});
```
## Sass の導入
`sass` のパッケージをインストールします。
```bash:ターミナル
sail npm install -D sass
```

`resources/css/app.css` を `resources/sass/app.scss` に変更します。
```scss:resources/sass/app.scss
.text-red {
    color: red;
}
```

## React と Sass の動作確認 
```bash:ターミナル
sail npm run dev
```
`http://localhost` にアクセスし、赤文字で `Hello World` と表示されれば、React と Sass は問題なく動作しています。

# Laravel と React の連携
最後に、Laravel と React の連携を行います。
## App.tsxの作成
`resources/ts` ディレクトリ配下に `App.tsx` を作成します。
```tsx:resources/ts/App.ts
import React, { useEffect } from 'react';
import axios from 'axios';

const App: React.FC = () => {
    useEffect(()=>{
        const fetchFromLaravel = async () => {
            const res  = await axios.get(`/api/hoge`);
            alert(res.data.hoge)
        };
        fetchFromLaravel();
    }, [])

    return (
        <div className="App"></div>
    );
}

export default App;
```

## index.tsxの編集
```tsx:resources/ts/index.ts
import React from 'react';
import { createRoot } from 'react-dom/client';
import App from './App'; // <- 追加

const container = document.getElementById('app');
const root = createRoot(container!); // createRoot(container!) if you use TypeScript

root.render(
  // <React.StrictMode>
    <div className="text-red">
        {/* Hello World */}
        <App /> {/* <- 追加 */}
    </div>
// </React.StrictMode>,
);
```

## api.phpの編集
```php:routes/api.php
Route::get('/hoge', function (Request $request) {
    return response()->json(
        [
            'hoge' => 'Hello from Laravel'
        ]
    );
});
```

`http:// localhost` にアクセスし、Alert で `Hello from Laravel` と表示されれば、Laravel と React の連携は完了です。


[Ctrl] + [C]キー で vite を停止します。

Sail も停止します。
```bash:ターミナル
sail down
```

# 終わりに
今回は、Laravel と React を連携するための最低限のセットアップにとどまり、認証機能の導入やデータベースの利用などをしていません。
Laravel では、SPA 用の認証として Laravel Sanctum が用意されているなど、Web API 開発のための機能が多く準備されているので公式ドキュメントなどを参考してください。