テンプレート式のリファレンス
Astroコンポーネントの構文はHTMLのスーパーセットです。この構文はHTMLやJSXを書いたことのある人にとって親しみやすいように設計され、コンポーネントやJavaScriptの式がサポートされています。
JSXライクな式
Astroコンポーネントのフロントマターのコードフェンス(---)の間に、JavaScriptのローカル変数を定義できます。そして、JSXライクな式を使って、変数をコンポーネントのHTMLテンプレートに注入できます。
:::note[動的vsリアクティブ]この方法により、フロントマターで計算された動的な値をテンプレートに含めることができます。しかし、一度含められたこれらの値はリアクティブではなく、変化することはありません。Astroコンポーネントは、レンダリングの際に一度だけ実行されるテンプレートです。
以下でAstroとJSXの違いに関する例をいくつか紹介します。:::
変数
ローカル変数は、波括弧の構文を使ってHTMLに追加できます。
---
const name = "Astro";
---
<div>
<h1>Hello {name}!</h1> <!-- <h1>Hello Astro!</h1> を出力 -->
</div>動的属性
ローカル変数を波括弧で囲むことで、HTML要素とコンポーネントの両方に属性値を渡せます。
---
const name = "Astro";
---
<h1 class={name}>属性を式で指定できます</h1>
<MyComponent templateLiteralNameAttribute={`私の名前は${name}です`} />:::cautionHTML属性は文字列に変換されるため、関数やオブジェクトをHTML要素に渡すことはできません。たとえば、Astroコンポーネント内のHTML要素にイベントハンドラを割り当てることはできません。
---
function handleClick () {
console.log("ボタンがクリックされました!");
}
---
<!-- ❌ これは動作しません! ❌ -->
<button onClick={handleClick}>クリックしても何も起こりません!</button>代わりに、通常のJavaScriptと同じように、クライアントサイドスクリプトを使用してイベントハンドラを追加します。
<button id="button">クリックしてください</button>
<script>
function handleClick () {
console.log("ボタンがクリックされました!");
}
document.getElementById("button").addEventListener("click", handleClick);
</script>:::
動的HTML
ローカル変数をJSXのような関数で使用して、動的に生成されるHTML要素を作成できます。
---
const items = ["犬", "猫", "カモノハシ"];
---
<ul>
{items.map((item) => (
<li>{item}</li>
))}
</ul>Astroは、JSXと同様に論理演算子と三項演算子を使用して、HTMLを条件に応じて表示できます。
---
const visible = true;
---
{visible && <p>表示!</p>}
{visible ? <p>表示!</p> : <p>あるいはこちらを表示!</p>}動的タグ
HTMLタグ名を変数に割り当てるか、コンポーネントのインポートを再割り当てすることで、動的タグも使用できます。
---
import MyComponent from "./MyComponent.astro";
const Element = 'div'
const Component = MyComponent;
---
<Element>Hello!</Element> <!-- <div>Hello!</div> としてレンダリングされます -->
<Component /> <!-- <MyComponent /> としてレンダリングされます -->動的タグを使用する場合は、以下の点に注意してください。
変数名は大文字で始める必要があります。 たとえば、
elementではなくElementを使用します。そうしないと、Astroは変数名をそのままHTMLタグとしてレンダリングしようとします。ハイドレーションディレクティブは使えません。
client:*ハイドレーションディレクティブを使用する場合、Astroはバンドルする対象のコンポーネントを知る必要がありますが、動的タグパターンではこれが機能しなくなるためです。define:vars ディレクティブ はサポートされていません。 もし、子要素を追加の要素(例えば
<div>)でラップすることができない場合は、style={``--myVar:${value}``}を手動で要素に追加することができます。
フラグメント
Astroでは <> </> 表記を使用でき、組み込みの <Fragment /> コンポーネントを提供しています。このコンポーネントは、HTML文字列を注入するために set:* ディレクティブ を追加する際に、ラッパー要素を避けるのに役立ちます。
次の例では、<Fragment /> コンポーネントを使用して段落テキストをレンダリングします。
---
const htmlString = '<p>生のHTMLコンテンツ</p>';
---
<Fragment set:html={htmlString} />AstroとJSXの違い
Astroコンポーネントの構文はHTMLのスーパーセットです。HTMLやJSXを書いたことのある人にとって親しみやすいように設計されてはいますが、.astroファイルとJSXの間にはいくつかの重要な違いがあります。
属性
Astroでは、JSXで使用されているcamelCaseではなく、すべてのHTML属性に標準のkebab-case形式を使用します。これは、Reactではサポートされていないclassでも同様です。
<div className="box" dataValue="3" />
<div class="box" data-value="3" />複数の要素
Astroコンポーネントテンプレートは複数の要素をレンダリングできます。その際、JavaScriptやJSXとは異なり、全体を単一の<div>や<>で囲む必要はありません。
---
// 複数の要素を含むテンプレート
---
<p>全体をコンテナ要素で囲む必要はありません。</p>
<p>Astroではテンプレート内に複数のルート要素を置けます。</p>コメント
Astroでは、標準のHTMLコメントまたはJavaScriptスタイルのコメントを使用できます。
---
---
<!-- .astroファイルでは、HTMLのコメント構文が使えます -->
{/* JSのコメント構文も有効です */}:::cautionHTMLスタイルのコメントはブラウザのDOMに含まれますが、JSのコメントはスキップされます。TODOメッセージやその他の開発専用の説明を残したい場合は、JavaScriptスタイルのコメントを使用することをお勧めします。:::
コンポーネントユーティリティ
Astro.slots
Astro.slots には、Astroコンポーネントのスロットの子要素を変更するためのユーティリティ関数が含まれています。
Astro.slots.has()
Type: (slotName: string) => boolean
Astro.slots.has() を使用して、特定のスロット名のコンテンツが存在するかどうかを確認できます。これは、スロットのコンテンツをラップしたいが、スロットが使用されている場合にのみラッパー要素をレンダリングしたい場合に便利です。
---
---
<slot />
{Astro.slots.has('more') && (
<aside>
<h2>More</h2>
<slot name="more" />
</aside>
)}Astro.slots.render()
Type: (slotName: string, args?: any[]) => Promise<string>
Astro.slots.render() を使用して、スロットのコンテンツをHTML文字列として非同期にレンダリングできます。
---
const html = await Astro.slots.render('default');
---
<Fragment set:html={html} />:::noteこれは高度なユースケースです!ほとんどの場合、<slot /> 要素を使用してスロットのコンテンツをレンダリングする方が簡単です。:::
Astro.slots.render() は、2番目のオプションの引数として、任意の子関数に転送されるパラメータの配列を受け取ります。これは、カスタムユーティリティコンポーネントに役立ちます。
たとえば、この <Shout /> コンポーネントは、message propを大文字に変換し、デフォルトスロットに渡します。
---
const message = Astro.props.message.toUpperCase();
let html = '';
if (Astro.slots.has('default')) {
html = await Astro.slots.render('default', [message]);
}
---
<Fragment set:html={html} /><Shout /> の子として渡されたコールバック関数は、すべて大文字の message パラメータを受け取ります。
---
import Shout from "../components/Shout.astro";
---
<Shout message="slots!">
{(message) => <div>{message}</div>}
</Shout>
<!-- <div>SLOTS!</div> としてレンダリングされます -->コールバック関数は、slot 属性を持つHTML要素タグでラップすることで、名前付きスロットに渡すことができます。この要素はコールバックを名前付きスロットに転送するためにのみ使用され、ページにはレンダリングされません。
<Shout message="slots!">
<fragment slot="message">
{(message) => <div>{message}</div>}
</fragment>
</Shout>ラッパータグには、標準のHTML要素またはコンポーネントとして解釈されない小文字のタグ(例:<Fragment /> の代わりに <fragment>)を使用します。HTMLの <slot> 要素はAstroのスロットとして解釈されるため、使用しないでください。
Astro.self
Astro.self を使用すると、Astroコンポーネントを再帰的に呼び出すことができます。この動作により、コンポーネントテンプレート内で <Astro.self> を使用して、Astroコンポーネントを自身の中からレンダリングできます。これは、大規模なデータストアやネストされたデータ構造を反復処理するのに役立ちます。
---
// NestedList.astro
const { items } = Astro.props;
---
<ul class="nested-list">
{items.map((item) => (
<li>
<!-- ネストされたデータ構造がある場合は、`<Astro.self>` をレンダリングします -->
<!-- そして、再帰呼び出しでpropを渡すことができます -->
{Array.isArray(item) ? (
<Astro.self items={item} />
) : (
item
)}
</li>
))}
</ul>このコンポーネントは次のように使用できます。
---
import NestedList from './NestedList.astro';
---
<NestedList items={['A', ['B', 'C'], 'D']} />そして、次のようなHTMLをレンダリングします。
<ul class="nested-list">
<li>A</li>
<li>
<ul class="nested-list">
<li>B</li>
<li>C</li>
</ul>
</li>
<li>D</li>
</ul>
Reference