Laravel9 から Laravel は JavaScript ファイルのコンパイルに Vite を用いることを推奨しています。Vite のコンパイル結果は次の様に呼び出せます。
@vite(‘resources/js/app.js’)
プロジェクトルートからみたエントリーポイントとなっている JavaScript ファイルのパスを指定します。これによって app-0821f0b4.js の様なランダム文字列を含むコンパイル後のファイルを呼び出す script タグを楽に埋め込めます。これはこれで便利なのですが、何らかの問題で JavaScript ファイルが読めなかった時のエラーハンドリングができないという不便を抱えてもいます。この不便を解決する方法を紹介します。
解決のためにまず @vite で実行され処理の内容を変更します。これは Laravel のサービスコンテナから差し替えができます。これは次の様にできます。
<?php
namespace App\Providers;
use Illuminate\Foundation\Vite;
use Illuminate\Support\ServiceProvider;
// アプリケーションのサービスプロバイダクラス
class AppServiceProvider extends ServiceProvider
{
/**
* アプリケーションのサービスを登録します。
*/
public function register(): void
{
// Viteクラスのインスタンスをシングルトンとして登録します。
app()->singleton(\Illuminate\Foundation\Vite::class, function () {
// 無名クラスを使用して、元のViteクラスを拡張します。
return new class() extends \Illuminate\Foundation\Vite {
/**
* 指定されたURLと属性のスクリプトタグを生成します。
*
* @param string $url
* @param array $attributes
* @return string
*/
protected function makeScriptTagWithAttributes($url, $attributes)
{
// URLが.jsで終わっている場合、エラーが発生したときの処理を属性として追加します。
if (str_ends_with($url, '.js')) {
$attributes['onerror'] = 'notifyReadJavaScriptError()';
}
// 親クラスのメソッドを呼び出して、スクリプトタグを生成します。
return parent::makeScriptTagWithAttributes($url, $attributes);
}
};
});
}
/**
* アプリケーションのサービスを起動します。
*/
public function boot(): void
{
// このメソッドは特に何も行っていませんが、アプリケーションの起動時に実行される処理を追加することができます。
}
}
@vite では\Illuminate\Foundation\Vite内の処理が呼び出されています。そこで\Illuminate\Foundation\Viteのインスタンス化処理を @vite について目的に即したクラスのインスタンス化処理に置き換えます。ここでは script タグを生成するならば onerror 属性を追加する様にしています。onerror 属性はでエラーが発生した場合のイベントハンドラとして使用される属性です。script の他にもimg、video などでもよく使用されます。この属性は指定されたリソース(例えば、画像やスクリプト)の読み込みが失敗した場合に実行されるJavaScriptの関数やコード片を指定できます。上記のコードではnotifyReadJavaScriptErrorという名前の関数を呼び出すように指定しています。
上記のように @vite のふるまいを変更した後は次の様に blade を作ります。
<html>
<head>
{{-- HTMLの<head>セクション。ここにページのメタデータやリンク、スクリプトなどが配置されます。 --}}
{{-- titleやmetaタグなど、ページに関する基本的な情報をここに記述します。 --}}
<script>
// JavaScriptのスクリプトセクション
// Viteで読み込むJavaScriptファイルの読み込みが失敗したときのエラーハンドリング関数
function notifyReadJavaScriptError() {
// 'app'というIDを持つ要素の内容を更新して、エラーメッセージを表示します。
document.getElementById('app').innerHTML = 'JavaScriptファイルの読み込みに失敗しました。';
}
</script>
{{-- Viteを使用して、プロジェクトルートから見て'resources/js/app.js'というパスのJavaScriptファイルを読み込みます。 --}}
@vite('resources/js/app.js')
</head>
<body>
{{-- React や Vue などを描画する先の要素 --}}
<div id="app">
ページを読み込んでいます。
</div>
</body>
</html>
React なり Vue なりでページを描画する想定です。何事もなく JavaScript ファイルの読み込みに成功すればそちらが表示され、読み込みに失敗すると notifyReadJavaScriptError が実行されてエラーメッセージが画面上に表示されるという形です。
この様にして読み込み失敗時のハンドリングができます。例では JavaScript でしたが link で読み込む css などでも同様に対応ができます。