Table of Contents
はじめに
前回はvite_railsをRailsに導入してReact/Vite環境を構築という記事を書いたので、次はLaravelにReact + TypeScript環境を構築してみようと思います。
Laravel 9ではViteがデフォルトで使えるのでReactを導入するだけで、あまりLaravel側を触らずに済むので良さそうです。
環境
- Laravel 9
- Vite 3
- React 18
Laravelプロジェクト作成
まずはLaravelプロジェクトを Laravel Sail を使って作成します。
$ curl -s "https://laravel.build/example-app" | bash]
$ cd laravel-vite-app && ./vendor/bin/sail up
うまくいったらデフォルトはlocalhost:80 をブラウザで開くと最初の画面が見れると思います。
Reactの追加
次にLaravelプロジェクトにReactを導入します。
LaravelではデフォルトでVite環境が用意されているので割と簡単にできます。
Viteではテンプレートが用意されているのでそれをもとに導入していきます。
React以外にもVueなど様々なテンプレートが用意されているので詳しくは以下を参照してください。
Vueでも基本的にほぼ同じやり方になると思います。
$ cd resources
$ rm -rf js
$ npm create vite@latest react -- --template react-ts
$ cd react
$ rm index.html package.json vite.config.ts
ここではpackage.jsonやvite.config.tsなどLaravelプロジェクト作成時にすでにあるため、消しています。
次にプロジェクトルートにあるvite.config.jsをReactが使えるように設定を変えます。
今回はTypeScript環境なため、拡張子をvite.config.tsに変えています。
vite.config.js → vite.config.ts
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [
laravel([
'resources/react/src/main.tsx',
]),
react(),
],
});
laravel()
のところではエントリポイントとしてresources/react/main.tsx
を渡しています。
複数のViewで別のReactコンポーネントを呼び出したい場合は entrypoints
のようなディレクトリを用意してそこにまとめるのも良いかもしれません。
今回は面倒だったので、そのままのmain.tsxを指定しています。
そしてpackge.jsonは以下のようになりました。
package.json
{
"private": true,
"scripts": {
"dev": "vite",
"build": "vite build"
},
"devDependencies": {
"@types/react": "^18.0.15",
"@types/react-dom": "^18.0.6",
"@vitejs/plugin-react": "^2.0.0",
"typescript": "^4.6.4",
"axios": "^0.25",
"laravel-vite-plugin": "^0.5.0",
"lodash": "^4.17.19",
"postcss": "^8.1.14",
"react": "^18.2.0",
"react-dom": "^18.2.0"
"vite": "^3.0.0"
}
}
これで設定周りは完了したので、パッケージのインストールをします。
$ ./vendor/bin/sail npm install
ここからはReactコンポーネントをLaravelから表示させてみます。
以下のApp.tsxを使います。
resources/react/src/App.tsx
import './App.css'
function App() {
return (
<div className="App">
<h1>Vite + React + Laravel</h1>
</div>
)
}
export default App
すでにあるwelcome.blade.phpを編集して以下のようにします。
resources/view/welcome.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>
@viteReactRefresh
@vite('resources/react/src/main.tsx')
</head>
<body>
<div id="root"></div>
</body>
</html>
@vite()
ではエントリポイントとなるファイルを指定します。
@viteReactRefresh
は @vite()
より先に読み込むようにしないとダメなようです。
これで表示する準備ができました。
Viteサーバーを起動して確認してみます。
$ ./vendor/bin/sail npm run dev
ブラウザでは以下のように表示されるはずです。
Controllerからデータを渡す
少し応用して今度はLaravelのControllerからデータをReactコンポーネントへ渡してみます。
まずはartisanコマンドでControllerを作成します。
$ ./vendor/bin/sail php artisan make:controller WelcomeController
作成されたWelcomeControllerではmessage
という変数をViewに渡しています。
app/Http/Controllers/WelcomController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class WelcomeController extends Controller
{
public function index()
{
return view('welcome', [
'message' => "Hello, world!",
]);
}
}
次にルーティングの設定をします。
routes/web.php
<?php
use App\Http\Controllers\WelcomeController;
use Illuminate\Support\Facades\Route;
Route::get('/', [WelcomeController::class, 'index']);
resources/views/welcome.blade.phpを少し変えてdata attributeに先ほど用意した変数を渡してReactコンポーネントまで伝えます。
resources/views/welcome.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>
@viteReactRefresh
@vite('resources/react/src/main.tsx')
</head>
<body>
<div id="root" data-message="{{ $message }}"></div>
</body>
</html>
エントリポイントとなるmain.tsxでは渡ってきた変数をAppコンポーネントにそのまま流しています。
resources/react/src/main.tsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
import './index.css'
const element = document.getElementById('root')
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<React.StrictMode>
<App {...element?.dataset} />
</React.StrictMode>
)
Appコンポーネントではpropsで渡ってきたmessage
をそのまま表示しています。
resources/react/src/App.tsx
import './App.css'
type Props = {
message?: string;
}
function App(props: Props) {
return (
<div className="App">
{props.message}
</div>
)
}
export default App
これでブラウザを確認するとLaravelのControllerで用意したデータを表示することができました。
さいごに
Laravelではすでにフロントエンド環境が準備されてあるLaravel BreezeやLaravel Jetstreamなどのスターターキットがあります。
今回はスターターキットを使わずにフロントエンド環境を構築してみましたが、デフォルトでViteが使える状態なので、かなり簡単でした。
今回はできませんでしたが、次はInertiaでフロントエンドとバックエンドの連携を楽にできたらと思っていましたが、この記事を書いている2022年8月2日時点ではReact18対応がまだリリースされておらず試せませんでした。
Related Posts
Daiki Urata
2024/06/10
Daiki Urata
2023/05/22