スクリプトとイベントハンドリング
標準的なHTMLの<script>
タグを使用すれば、React・Svelte・Vue等のUIフレームワークを利用せずにインタラクティブ性をAstroコンポーネントへ追加できます。これによってブラウザ上で実行されるJavaScriptを送信してAstroコンポーネントに機能を追加できるようになります。
クライアントサイドスクリプト
スクリプトは、イベントリスナーの追加やアナリティクスデータの送信、アニメーションの再生など、JavaScriptがウェブ上でできることすべてに使用できます。
デフォルトでは、Astroは<script>
タグを処理してバンドルし、npmモジュールのインポートやTypeScriptの記述などをサポートします。
Astroで<script>
を使用する
.astro
ファイル内に単数(もしくは複数)の<script>
タグを追加することによってクライアントサイドJavascriptを追加できます。
下の例では、<Hello />
コンポーネントをページに追加することでブラウザコンソール上にログメッセージを出力します。
スクリプトの処理
デフォルトで<script
>タグはAstroで処理されます。
- すべてのインポートはバンドルされ、ローカルファイルやNodeモジュールをインポートできます。
- 処理されたスクリプトは、
type="module"
としてページの<head>
に挿入されます。 - TypeScriptを対応しており、TypeScriptファイルもインポートできます
- コンポーネントがページに複数回使われている場合、スクリプトは一度だけ含まれます。
type="module"
属性は、ブラウザにスクリプトをJavaScriptモジュールとして扱わせます。これにはいくつかのパフォーマンス上のメリットがあります。
- レンダリングがブロックされません。モジュールスクリプトとその依存関係がロードされる間、ブラウザは残りのHTMLの処理を続けます。
- ブラウザはモジュールスクリプトを実行する前に、HTMLが処理されるのを待ちます。“load”イベントをリッスンする必要はありません。
async
とdefer
属性は不要です。モジュールスクリプトは常に延期されます。
async
属性はレンダリングのブロックを防ぐため、通常のスクリプトにとって価値があります。しかし、モジュールスクリプトでは既にこの動作が含まれています。モジュールスクリプトにasync
を追加すると、ページが完全に読み込まれる前にモジュールスクリプトが実行されます。おそらくこれはあなたが望んでいることではありません。
処理の対象外にする
Astroがスクリプトを処理しないようにするには、is:inline
ディレクティブを追加します。
Astroは、状況によってはスクリプトタグを処理しません。特に、<script>
タグにtype="module"
やsrc
以外の属性を追加すると、Astroはそのタグをis:inline
ディレクティブがあるかのように扱います。スクリプトがJSX式で記述されている場合も同様です。
📚 <script>
タグで利用可能なディレクティブについてもっと知りたい場合は、テンプレートディレクティブ (EN)を参照してください。
ページにJavaScriptファイルを含める
スクリプトを.js
や.ts
ファイルのように分離して書きたい場合や、他のサーバーから外部スクリプトを参照したい場合には<script>
タグのsrc
属性の参照を使えます。
ローカルスクリプトのインポート
これを使うケース: スクリプトがsrc
に存在している時。
Astroはスクリプトの処理のルールに従って、ビルド、最適化、そしてページ内にスクリプトを追加します。
外部スクリプトをロードする
これを使うケース: JavaScriptファイルがpublic
ディレクトリに存在しているか、CDNの時。
プロジェクトのsrc
フォルダ以外のスクリプトをロードするには、is:inline
ディレクティブを含めます。このアプローチでは、上記のようにスクリプトをインポートする際にAstroが提供しているJavaScriptの処理・バンドル・最適化はスキップされます
共通スクリプトパターン
onclick
や他のイベントをハンドリングする
いくつかのUIフレームワークではonclick={...}
(React/Preact)や@click="..."
(Vue)のように独自の構文でイベントを処理しています。Astroは標準的なHTMLにより近く、イベントのために独自の構文は利用しません。
その代わりに、ユーザーのインタラクションをハンドリングするために<script>
タグ内にaddEventListener
を利用します。
ページに複数の<AlertButton />
コンポーネントが存在する場合でも、Astroは複数回スクリプトを実行しません。スクリプトはページに1つしか含まれないようにバンドルされます。querySelectorAll
を利用すると、このスクリプトはページ上でalert
クラスを持つすべてのボタンにイベントリスナーをアタッチします。
カスタム要素を持つWebコンポーネント
Webコンポーネント標準を使うことで独自の動作をするHTML要素を作成できます。.astro
コンポーネントでカスタム要素を定義するとUIフレームワークライブラリを利用しなくてもインタラクティブなコンポーネントを作ることができます。
下の例では、ハートボタンがクリックされた回数を記録し、その最新のカウント数を<span>
に更新する<astro-heart>
HTML要素を新しく定義しています。
ここでカスタム要素を利用するメリットは2つあります。
document.querySelector()
を使って全ページを検索する変わりに、this.querySelector()
を使えば検索範囲が現在のカスタム要素のインスタンスに限られます。これにより、一回で一つのコンポーネントだけを操作しやすくなります。<script>
は一度だけしか実行されませんが、ブラウザはページ上の<astro-heart>
を見つける度にカスタム要素のconstructor()
メソッドを実行します。これにより、ページで複数回コンポーネントを使用する場合でも一つのコンポーネントを安全に記述できます。
📚 web.dev’s Reusable Web Components guideとMDN’s introduction to custom elementsから多くのカスタム要素について学ぶことができます。
フロントマター変数をスクリプトに渡す
Astroコンポーネントでは、---
フェンスの間にあるフロントマターのコードはサーバー上で実行され、ブラウザでは利用できません。サーバーからクライアントへ変数を渡すには、変数を保存しておきJavaScriptがブラウザで実行されたときに読み込む必要があります。
これを実現するための1つの手段はdata-*
attributesを利用してHTML出力に変数の値を保存する方法です。カスタム要素を含むスクリプトはブラウザ上にHTMLがロードされると要素のdataset
を利用することで、この属性を読み込むことができます。
下の例では、message
propをdata-message
属性に保存されているので、カスタム要素がthis.dataset.message
を読み込みブラウザ上で値を取得しています。
これでコンポーネントは何度でも利用でき、異なるmessageを表示できます
この方法はReactなどのUIフレームワークを用いて書かれたコンポーネントにpropsを渡す際にAstroが裏で行っていることです!client:*
ディレクティブを持つコンポーネントに対して、Astroは<astro-island>
カスタム要素にprops
属性を作成してサーバーサイドのprops
をHTML出力結果に保存します。