Laravel+TypeScript+Reactで開発環境を構築する
こんばんわ。
家電量販店に行くとキーボード、マウス売り場でテンションが上がってしまうどうもみけぽんです。
業務では新しいフロントエンド技術に触る機会があまりないので、個人開発では新しめの技術に意識して触るようにしています。
LaravelとTypeScriptを組み合わせた開発環境を構築しようとして結構詰まってしまったので、備忘録で残しておこうと思います。
Laravel+TypeScript+Reactで開発環境を構築する
前提
下記は既にインストールしているものとします。
- Laravel8
- node.js
TypeScript、SCSSをインストールする
const mix = require('laravel-mix');
/*
|--------------------------------------------------------------------------
| Mix Asset Management
|--------------------------------------------------------------------------
|
| Mix provides a clean, fluent API for defining some Webpack build steps
| for your Laravel applications. By default, we are compiling the CSS
| file for the application as well as bundling up all the JS files.
|
*/
mix.ts('resources/ts/app.tsx', 'public/js')
.sass('resources/sass/app.scss', 'public/css');
まずはルートディレクトリ上にあるwebpack.min.jsを上記のように修正します。
$ npm install
npmコマンドでwebpack.min.jsに追加したモジュールをインストールします。
$ npm run watch
ルートディレクトリにあるpackage.jsonに記述のあるコマンドを実行すると途中でエラーになりますが、package.jsonにtypescriptやsassが追加されます。
Reactをダウンロードする
$ npm i -D react react-dom @types/react @types/react-dom
Reactと関連モジュールをダウンロードします。
globをダウンロードする
webpack.mix.jsですが、デフォルトだとtsディレクトリのファイル一つにつきチェーンメソッドで連結しなければなりません。
mix.ts('resources/assets/js/app.js', 'public/js')
.ts('resources/assets/js/hoge.js', 'public/js')
.ts('resources/assets/js/fuga.js', 'public/js')
.sass('resources/assets/sass/app.scss', 'public/css');
これだと分かりづらい上に冗長になってしまうので、globをインストールしてtsディレクトリ以下をワイルドカード指定できるようにします。
$ npm i -D glob
globをnpmコマンドでダウンロードします。
ダウンロードしたモジュールのインストール
$ npm install
npmコマンドでモジュールをインストールします。
"devDependencies": {
"@babel/preset-react": "^7.13.13",
"@types/react": "^17.0.21",
"@types/react-dom": "^17.0.9",
"axios": "^0.21",
"bootstrap": "^4.6.0",
"glob": "^7.1.7",
"jquery": "^3.6",
"laravel-mix": "^6.0.6",
"lodash": "^4.17.19",
"popper.js": "^1.16.1",
"postcss": "^8.1.14",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"resolve-url-loader": "^3.1.2",
"sass": "^1.32.11",
"sass-loader": "^11.0.1",
"ts-loader": "^9.2.5",
"typescript": "^4.4.3"
}
package.jsonを開くとダウンロードしたモジュールがインストールされていることが分かります。
TypeScriptの設定をする
$ tsc --init --jsx react
上記コマンドの実行でプロジェクトディレクトリにtsconfig.jsonファイルが作成されます。
"target": "es5",
↓
"target": "es6"
ちなみに私の場合はtargetの箇所だけ変更しています。
ts、scssディレクトリ以下の全ファイルをコンパイル対象にする
const mix = require('laravel-mix');
const glob = require('glob');
glob.sync('resources/ts/**/*').map(function (file) {
mix.ts(file, 'public/js')
});
glob.sync('resources/sass/**/*.scss').map(function (file) {
mix.sass(file, 'public/css')
});
globをダウンロードすることによって、ts及びscssディレクトリ以下をワイルドカード指定可能になりました。
それぞれpublic/js
、public/css
にコンパイルされます。
Reactの動作確認をする
import React from 'react'
import ReactDOM from 'react-dom'
const App = () => {
const title: string = 'hoge hoge'
return (
<h1>{title}</h1>
)
}
ReactDOM.render(
<App />,
document.getElementById('app')
)
resources/ts/hoge.tsxを作成し、上記の内容で保存します。
globをインストールして'resources/ts/**/*'
と記載しているため、resources/ts内の全ファイルはコンパイルの対象になります。
$ npm run dev
npmコマンドでresources/ts内のTypeScriptファイルをコンパイルすると、public/js内にhoge.jsが生成されます。
<!doctype html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="csrf-token" content="{{ csrf_token() }}">
</head>
<body>
<div id="app"></div>
<!-- Scripts -->
<script src="{{ mix('js/hoge.js') }}"></script>
</body>
</html>
viewファイルにidがappのdiv要素を作成し、public/jsにコンパイルされたhoge.jsをmix関数で読み込みます。
「hoge hoge
」と表示されれば成功です。
グローバル関数を定義する
function message(name: string): void {
console.log(name + 'さん、こんにちわ!');
}
例えば上記のようなtest.tsファイルでfunction messageを定義した場合、コンパイル後のjsファイルを見てみましょう。
/******/ (() => { // webpackBootstrap
/******/ "use strict";
var __webpack_exports__ = {};
/*!*******************************!*\
!*** ./resources/ts/test.ts ***!
\*******************************/
function message(name) {
console.log(name + 'さん、こんにちわ!');
}
/******/ })()
;
function messageがクロージャ―内で定義されてしまっています。
<!-- Scripts -->
<script src="{{ asset('js/app.js') }}"></script>
<script src="{{ mix('js/test.js') }}"></script>
<script>
message('山田');
</script>
</body>
</html>
この状態だとクロージャ―の外で呼び出すことが出来ず、viewファイルでmessage関数を呼び出すとfunction is not definedでエラーになります。
function message(name: string): void {
console.log(name + 'さん、こんにちわ!');
}
(window as any).message = message;
よってtsファイルの末尾でグローバル関数として定義します。
/******/ (() => { // webpackBootstrap
/******/ "use strict";
var __webpack_exports__ = {};
/*!******************************!*\
!*** ./resources/ts/test.ts ***!
\******************************/
function message(name) {
console.log(name + 'さん、こんにちわ!');
}
window.message = message;
/******/ })()
;
コンパイル後のjsファイルでもmessage関数がwindow.message
に代入され、グローバルに設定されていることが分かります。
これでviewファイルから関数を呼び出せるようになりました。