【Vite対応】Laravel × ReactでSPA環境を作成する

laravel-react.png

LaravelにReactを導入してSPA環境を構築していきます。

また、jsのビルドツールとしてはLaravel標準のviteを利用していきます。

viteとは?

vite(ヴィート)とは複数のファイルを1つにまとめてくれるビルドツールです。
2020年に登場し、Vue.jsの開発者が開発しました。

これまでのwebpackなどのバンドルツールは全てのファイルをまとめて管理していたため、プロジェクトが大きくなるほどビルドに時間がかかっていました。

viteはフランス語で「速い」という意味で、リクエスト時に必要な箇所だけ読み込まれるため高速で動作します。

バージョン

PHP8.2

Laravel Framework 9.45.1

“react": “^18.2.0"

“react-dom": “^18.2.0"

“vite": “^4.0.0"

“react-router-dom": “^6.6.1"

環境構築

Laravelの環境構築

まずは最新のLaravelの環境の構築をしていきましょう。

当記事ではdocker環境にて進めていきます。

Laravelのdocker環境の構築はこちらの記事を参照して作成してください。

コンテナの作成までできたらappコンテナの中に入りましょう。

$ docker-compose exec app bash

Reactの導入

今回Reactの導入はLaravelのスターターキットを使って導入していきます。
※もしBreezeなど不要な導入をしたくない場合は通常通りnpmでreactを導入してください。

$ composer require laravel/breeze --dev
$ php artisan breeze:install react

上記を実行すると、npm installとnpm run buildまでやってくれます。

これでlocalhosstにアクセスしてみてください。

Laravelの初期画面が表示されるかと思いますが、実はこちらはresources/js/Pages/Welcome.jsxというReactファイルで作成されています。

これでReactの導入は完了です。

viteのポート設定(npm run dev)

Reactファイルを編集した際は、npm run buildをすると変更が反映されます。

しかし、これではviteを効率的に利用しているとは言えません。

ファイルを変更したら自動で画面も更新されてほしいものです(ホットリロード)。

そこで使うのがnpm run devです。

しかし、上記で作成したdocker環境で実行すると画面が真っ白になってしまいます。

これはdockerコンテナがviteを実行するデフォルトポート5173を開いていないためです。

その設定をしていきましょう。

docker-compose.ymlファイルにて、下記のようにappコンテナにportsを追加してください。

  # webアプリケーションのコンテナ
  app:
    build:
      context: .
      dockerfile: ./infra/app/Dockerfile
      target: php82
    volumes:
      - ./src:/var/www
    ports: # ⇦ここを追加
      - "5173:5173" # ⇦ここを追加
    working_dir: /var/www

編集したら、docker-compose up -dを実行しましょう。

package.jsonも追記が必要です。devに–hostオプションを追加しましょう。

"scripts": {
    "dev": "vite --host",
    "build": "vite build"
},

これで設定は完了です。npm run devして画面が適切に表示されれば完了です。

実際にjsxファイルを更新すると、画面にも反映されるようになります。

これでViteによるReact環境の構築は完了です。

SPA環境の構築をしたい方は以下もご覧ください。

Reactページを作ってみよう

SPA設定の前に一度Reactファイルを作成してみましょう。

resources/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">

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

    <title>{{ config('app.name', 'Laravel') }}</title>

    <!-- Fonts -->
    <link rel="dns-prefetch" href="//fonts.gstatic.com">
    <link href="https://fonts.bunny.net/css?family=Nunito" rel="stylesheet">


    <!-- bootstrap5 -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>

    <!-- Scripts -->
    @viteReactRefresh
    @vite(['resources/js/app.css', 'resources/js/app.jsx'])
</head>
<body>
<div id="app"></div>
</body>
</html>

<div id="app"></div>にReactファイルをレンダリングしていきます。

Reactファイルを作成します。

resources/js/Pages/Home.jsxを作成し、以下のように記述してください。

import React from 'react';
import { createRoot } from 'react-dom/client';
function App() {
    return (
        <div className="container mt-5">
            <div className="row justify-content-center">
                <div className="col-md-8">
                    <div className="card">
                        <div className="card-header">Home Component</div>
                        <div className="card-body">I'm an home component!</div>
                    </div>
                </div>
            </div>
        </div>
    );
}

const container = document.getElementById('app');
const root = createRoot(container);
root.render(<App />);

resources/js/app.jsxを以下のように書き換えてください。

import './bootstrap';
import '../css/app.css';
import './Pages/Home';

最後に、web.phpに下記を追加してください。

Route::get('/home', function () {
    return view('index');
});

これで完了です。localhost/homeにアクセスしてみてください。

SPAの設定

react-router-domの導入

ではお待ちかね、SPAの設定をしていきます。

Reactのルーティングのライブラリはreact-router-domを利用していきます。

$ npm install react-router-dom

ちなみにreact-router-domのv5とv6では記述方法が変わっているため、ググる時はご注意ください。

ルーティング設定

ルーティング設定用のファイルを/js直下にroute.jsxを作成しましょう。

import React from 'react';
import { createRoot } from 'react-dom/client';
import {BrowserRouter, Route, Routes} from 'react-router-dom';
import Home from './Pages/Home';

function App() {
    return (
        <BrowserRouter>
            <Routes>
                <Route path='/'  element={<Home />} />
            </Routes>
        </BrowserRouter>
    );
}

const container = document.getElementById('app');
const root = createRoot(container);
root.render(<App />);

各ページ分<Route>タグを記述していきます。

Home.jsxを以下のように書き換えます。

import React from 'react';

function Home() {
    return (
        <div className="container mt-5">
            <div className="row justify-content-center">
                <div className="col-md-8">
                    <div className="card">
                        <div className="card-header">Home Component</div>
                        <div className="card-body">I'm an home component!</div>
                    </div>
                </div>
            </div>
        </div>
    );
}

export default Home;

app.jsxでroute.jsxを呼び出すように、下記のように書き換えてください。ファイルは3行のみになります。

import './bootstrap';
import '../css/app.css';
import './route';

web.phpを下記のように書き換えてください。

<?php

use Illuminate\Support\Facades\Route;

Route::get('{any}', function () {
    return view('index');
})->where('any','.*');

anyとはどんなURLを指定されてもという意味です。全てindex.blade.phpに行くようにします。

これでhttp://localhostにアクセスしてみてください。先ほどのHome.jsxが表示されればOKです。

遷移先のページ作成

では遷移先のページとして、js/Pages/Page.jsxを作成しましょう。

import React from 'react';
import {Link} from "react-router-dom";

function Page() {
    return (
        <div className="container mt-5">
            <div className="row justify-content-center">
                <div className="col-md-8">
                    <div className="card">
                        <div className="card-header">Page Component</div>
                        <div className="card-body">I'm an page component!</div>
                        <Link to={'/'} className="btn btn-primary">Homeへ遷移</Link>
                    </div>
                </div>
            </div>
        </div>
    );
}

export default Page;

Linkタグで遷移用のリンクを作成します。

ページを増やしたのでroute.jsxにも追記しましょう。

import React from 'react';
import { createRoot } from 'react-dom/client';
import {BrowserRouter, Route, Routes} from 'react-router-dom';
import Home from './Pages/Home';
import Page from './Pages/Page';

function App() {
    return (
        <BrowserRouter>
            <Routes>
                <Route path='/'  element={<Home />} />
                <Route path='/page'  element={<Page />} />
            </Routes>
        </BrowserRouter>
    );
}

const container = document.getElementById('app');
const root = createRoot(container);
root.render(<App />);

Home.jsxにもリンクを設置しましょう。

import React from 'react';
import {Link} from "react-router-dom";
function Home() {
    return (
        <div className="container mt-5">
            <div className="row justify-content-center">
                <div className="col-md-8">
                    <div className="card">
                        <div className="card-header">Home Component</div>
                        <div className="card-body">I'm an home component!</div>
                        <Link to={'/page'} className="btn btn-primary">Pageへ遷移</Link>
                    </div>
                </div>
            </div>
        </div>
    );
}

export default Home;

これで完了です!画面をみてみましょう。

ボタンを押すと画面が遷移されるかともいます。

終わりに

今回はLaravelにReactを導入してSPA環境を作成しました。

viteというツールが登場したり、Reactがバージョンアップしたりフロントの世界では変わりが盛んです。

私もいざReact環境を作った際に参考にした記事が古く、動かないことがあったので今回記事に書いてみました。機会があれば、Reactのチュートリアル記事も作成したいと思います。

みなさんもぜひ、Reactにチャレンジしてみてください。

参考

Laravel 9.x アセットの構築(Vite)

Laravel 9.x スターターキット