Astroコンポーネント
Astroコンポーネントは、あらゆるAstroプロジェクトの基本的な構成要素です。クライアントサイドのランタイムを持たない、HTMLのみのテンプレートコンポーネントです。
HTMLをご存知の方なら、すでに最初のAstroコンポーネントを書くのに十分な知識を持っています。
Astroコンポーネントの構文は、HTMLのスーパーセットです。この構文は、HTMLやJSXを書いたことのある人なら誰でも親しみやすいように設計されています。また、コンポーネントとJavaScript式を含むためのサポートも追加されています。Astroコンポーネントは、ファイル拡張子が.astro
なので、すぐ見分けられます。
Astroコンポーネントは非常に柔軟です。多くの場合、Astroコンポーネントは、ヘッダーやプロフィールカードのような、ページ上で再利用可能なUIを含むことになります。また、Astroコンポーネントには、SEO対策を容易にする共通の<meta>
タグのコレクションのような、小さなHTMLのスニペットが含まれることもあります。Astroコンポーネントは、ページ全体のレイアウトを含められます。
Astroコンポーネントについて知っておくべきもっとも重要なことは、ビルド中にHTMLへ変換されることです。コンポーネントの内部でJavaScriptコードを実行しても、すべて事前に実行され、ユーザーに送られる最終ページからは取り除かれます。その結果、デフォルトでは、追加されるJavaScriptの痕跡のない、より高速なサイトが実現します。
コンポーネント構造
Section titled コンポーネント構造Astroコンポーネントは、コンポーネントスクリプトとコンポーネントテンプレートという2つの主要な部分で構成されています。それぞれのパーツは異なる仕事を行いますが、この2つを組み合わせることで、使いやすさと、どんなものにも対応できる表現力を兼ね備えたフレームワークを提供することを目指しています。
---
// コンポーネントスクリプト (JavaScript)
---
<!-- コンポーネントテンプレート (HTML + JS Expressions) -->
コンポーネントを他のコンポーネントの内部で使用し、より高度なUIを構築できます。たとえば、Button
コンポーネントを使用して、ButtonGroup
コンポーネントを作成すると、次のようになります。
---
// 例: ButtonGroup.astro
import Button from './Button.astro';
---
<div>
<Button title="Button 1" />
<Button title="Button 2" />
<Button title="Button 3" />
</div>
コンポーネントスクリプト
Section titled コンポーネントスクリプトAstroでは、Astroコンポーネント内のコンポーネントスクリプトを識別するためにコードフェンス(---
)を使用します。Markdownを書いたことがある方なら、すでにフロントマターという同様の概念に馴染みがあるかもしれません。Astroのコンポーネントスクリプトの考え方は、この概念から直接着想を得ています。
コンポーネントスクリプトを使用して、テンプレートをレンダリングするために必要なあらゆるJavaScriptコードを記述できます。
- 他のAstroコンポーネントのインポート
- 他のフレームワークコンポーネント(Reactなど)のインポート
- データ(JSONファイルなど)のインポート
- APIやデータベースからコンテンツを取得するコード
- テンプレートで参照する変数の作成
---
import SomeAstroComponent from '../components/SomeAstroComponent.astro';
import SomeReactComponent from '../components/SomeReactComponent.jsx';
import someData from '../data/pokemon.json';
// 渡されたコンポーネントのprops(`<X title="Hello, World" />`など)にアクセスする。
const {title} = Astro.props;
// 外部データを取得する(プライベートAPIやデータベースからでも可)
const data = await fetch('SOME_SECRET_API_URL/users').then(r => r.json());
---
<!-- テンプレートはここに書きます -->
コードフェンスは、その中に書かれたJavaScriptを「囲い込む」ことを保証するために設計されています。コードがフロントエンドのアプリケーションに漏れたり、ユーザーの手に渡ったりしません。高コストなコードや機密性の高いコード(プライベートなデータベースの呼び出しなど)が、ユーザーのブラウザに届くことを心配せずに、安全にコードを書けます。
コンポーネントテンプレート
Section titled コンポーネントテンプレートコンポーネントスクリプトの下には、コンポーネントテンプレートがあります。コンポーネントテンプレートは、コンポーネントの出力するHTMLを決定します。
ここにプレーンなHTMLを書けば、そのコンポーネントはAstroのページでインポートされて使用される際にそのHTMLをレンダリングします。
ただし、Astroのコンポーネントテンプレート構文は、JavaScript式、インポートしたコンポーネント、特別なAstroディレクティブ (EN)もサポートしています。コンポーネントスクリプトで(ページ構築時に)定義されたデータと値をコンポーネントテンプレートで使用することで、動的に作成されたHTMLを生成できます。
---
// コンポーネントスクリプトはここに書きます
import ReactPokemonComponent from '../components/ReactPokemonComponent.jsx';
const myFavoritePokemon = [/* ... */];
---
<!-- HTMLコメントに対応しています -->
<h1>Hello, world!</h1>
<!-- propsやコンポーネントスクリプトの変数を使用します -->
<p>好きなポケモンは: {Astro.props.title}</p>
<!-- `client:`ディレクティブの付いたコンポーネントはハイドレートされます -->
<ReactPokemonComponent client:visible />
<!-- JSXと同じように、HTMLとJavaScriptの式を混ぜられます -->
<ul>
{myFavoritePokemon.map((data) => <li>{data.name}</li>)}
</ul>
<!-- テンプレートディレクティブを使って、複数の文字列やオブジェクトからクラス名を作成できます -->
<p class:list={["add", "dynamic", {classNames: true}]} />
JSXに似た式
Section titled JSXに似た式Astroコンポーネントのフロントマターのコンポーネントスクリプト内で、ローカルJavaScript変数を定義できます。また、JSXに似た式を使用して、これらの変数をコンポーネントのHTMLテンプレートに挿入できます。
ローカル変数は、中括弧({}
)で囲んで使うことで、HTMLに追加できます。
---
const name = "Astro";
---
<div>
<h1>Hello {name}!</h1> <!-- 出力: <h1>Hello Astro!</h1> -->
</div>
動的な属性
Section titled 動的な属性ローカル変数は、中括弧で囲んで、HTML要素やコンポーネントに属性の値を渡せます。
---
const name = "Astro";
---
<h1 class={name}>属性式がサポートされています</h1>
<MyComponent templateLiteralNameAttribute={`MyNameIs${name}`} />
動的なHTML
Section titled 動的な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>}
動的なタグ
Section titled 動的なタグHTMLタグ名、またはインポートしたコンポーネントを変数に設定することで動的なタグが利用できます。
---
import MyComponent from "./MyComponent.astro";
const Element = 'div'
const Component = MyComponent;
---
<Element>こんにちは!</Element> <!-- <div>こんにちは!</div> としてレンダリングされます -->
<Component /> <!-- <MyComponent />としてレンダリングされます -->
動的なタグを使用する場合:
-
変数名をキャピタライズする 例えば、
element
ではなくてElement
を利用します。そうでなければ、Astroは変数名をリテラルなHTMLタグとしてレンダリングします。 -
ハイドレーションのディレクティブはサポートしません.
client:*
ハイドレーションディレクティブを利用する場合、Astroは本番用にバンドルするコンポーネントを知る必要がありますが、動的なタグパターンではこれが動作しません。
フラグメントと複数要素
Section titled フラグメントと複数要素Astroコンポーネントテンプレートは複数の要素をレンダリングできますが、JavaScriptやJSXとは異なり、その際に全体を1つの<div>
や<>
で囲む必要はありません。
---
// 複数の要素を含むテンプレート
---
<p>各要素を1つのコンテナ要素で包む必要はありません。</p>
<p>Astroはテンプレート内の複数のルート要素をサポートします。</p>
しかし、式を使用して複数の要素を動的に作成する場合は、JavaScriptやJSXと同様に、これらの要素をフラグメントで囲む必要があります。Astroでは、<Fragment> </Fragment>
または省略形の<> </>
のいずれかを使用できます。
---
const items = ["犬", "猫", "カモノハシ"];
---
<ul>
{items.map((item) => (
<>
<li>赤い{item}</li>
<li>青い{item}</li>
<li>緑の{item}</li>
</>
))}
</ul>
また、以下の例のように、set:*
ディレクティブ (EN)を追加する際に、ラッパー要素を避けるためにフラグメントが役に立つことかもしれません。
---
const htmlString = '<p>Raw HTML content</p>';
---
<Fragment set:html={htmlString} />
AstroとJSXの違い
Section titled 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では標準的なHTMLのコメントかJavaScript形式のコメントを使用できます。
---
---
<!-- .astroファイルではHTMLコメント構文が有効です -->
{/* JSコメント構文も有効です */}
コンポーネントのprops
Section titled コンポーネントのpropsAstroコンポーネントは、propsを定義し、受け取れます。propsは、HTMLをレンダリングするためにコンポーネントテンプレートで利用できます。propsは、フロントマタースクリプトのグローバルな Astro.props
で利用できます。
以下は、greeting
とname
のpropsを受け取るコンポーネントの例です。受け取るpropsは、グローバルな Astro.props
オブジェクトから分割代入されることに注意してください。
---
// 使い方: <GreetingHeadline greeting="Howdy" name="Partner" />
const { greeting, name } = Astro.props;
---
<h2>{greeting}、{name}!</h2>
このコンポーネントをインポートして、他のAstroコンポーネント、レイアウト、ページでレンダリングする場合、属性としてこれらのpropsを渡せます。
---
import GreetingHeadline from './GreetingHeadline.astro';
const name = "Astro"
---
<h1>グリーティングカード</h1>
<GreetingHeadline greeting="やぁ" name={name} />
<p>素敵な一日をお過ごしください!</p>
TypeScriptのProps
型のインターフェイスでpropsを定義できます。Astroはフロントマター内のProps
インターフェイスを自動的に検出し、型の警告やエラーを出します。propsは、Astro.props
から分割代入する際に、デフォルト値を与えることもできます。
---
interface Props {
name: string;
greeting?: string;
}
const { greeting = "こんにちは", name } = Astro.props;
---
<h2>{greeting}, {name}!</h2>
コンポーネントのpropsは、何も提供されない場合に使用するデフォルト値を指定できます。
---
const { greeting = "こんにちは", name = "宇宙飛行士" } = Astro.props;
---
<h2>{greeting}、{name}!</h2>
<slot />
要素は外部HTMLコンテンツのプレースホルダーで、他のファイルからコンポーネントテンプレートに子要素を注入(はめ込む=スロット)できます。
デフォルトでは、コンポーネントに渡されたすべての子要素は、その<slot />
内でレンダリングされます。
---
import Header from './Header.astro';
import Logo from './Logo.astro';
import Footer from './Footer.astro';
const { title } = Astro.props
---
<div id="content-wrapper">
<Header />
<Logo />
<h1>{title}</h1>
<slot /> <!-- 子要素はここに入ります -->
<Footer />
</div>
---
import Wrapper from '../components/Wrapper.astro';
---
<Wrapper title="Fred's Page">
<h2>フレッドについて</h2>
<p>ここでは、フレッドについて紹介します。</p>
</Wrapper>
このパターンはAstroレイアウトコンポーネントの基本です。HTMLコンテンツのページ全体を<Layout></Layout>
タグで囲んでレイアウトコンポーネントに送り、共通のページ要素の中にレンダリングさせられます。
名前付きスロット
Section titled 名前付きスロットAstroコンポーネントは、名前付きスロットも使えます。これを利用すると、対応するスロット名を持つHTML要素のみをスロットの場所に渡せます。
---
import Header from './Header.astro';
import Logo from './Logo.astro';
import Footer from './Footer.astro';
const { title } = Astro.props
---
<div id="content-wrapper">
<Header />
<slot name="after-header"/> <!-- `slot="after-header"` 属性を持つ子要素はここに入ります。 -->
<Logo />
<h1>{title}</h1>
<slot /> <!-- `slot`属性をもたない子要素、`slot="default"`属性を持つ子要素はここに入ります。 -->
<Footer />
<slot name="after-footer"/> <!-- `slot="after-footer"` 属性を持つ子要素はここに入ります。 -->
</div>
---
import Wrapper from '../components/Wrapper.astro';
---
<Wrapper title="フレッドのページ">
<img src="https://my.photo/fred.jpg" slot="after-header">
<h2>フレッドについて</h2>
<p>ここでは、フレッドについて紹介します。</p>
<p slot="after-footer">Copyright 2022</p>
</Wrapper>
子要素の slot="my-slot"
属性を使用して、コンポーネント内の <slot name="my-slot" />
にマッチするプレースホルダに渡します。
スロットのフォールバックコンテンツ
Section titled スロットのフォールバックコンテンツスロットは、フォールバックコンテンツをレンダリングすることもできます。スロットに渡される子要素がない場合、 <slot />
要素はそれ自身のプレースホルダーの子要素をレンダリングします。
---
import Header from './Header.astro';
import Logo from './Logo.astro';
import Footer from './Footer.astro';
const { title } = Astro.props
---
<div id="content-wrapper">
<Header />
<Logo />
<h1>{title}</h1>
<slot>
<p>これは、スロットに渡された子要素がない場合の代替コンテンツです。</p>
</slot>
<Footer />
</div>
CSSスタイル
Section titled CSSスタイルCSSの <style>
タグも、コンポーネントテンプレートの内部でサポートされています。
これらのタグはコンポーネントのスタイル設定に使えます。すべてのスタイルルールはそのコンポーネントに自動的にスコープが作られ、大規模なアプリでのCSSのコンフリクトを防げます。
---
// コンポーネントスクリプトはここに書きます
---
<style>
/* コンポーネントでスコープが作られ、ページ上の他のh1要素には影響しません */
h1 { color: red }
</style>
<h1>Hello, world!</h1>
📚 スタイルの適用に関する詳細は、スタイリングガイドを参照してください。
クライアントサイドスクリプト
Section titled クライアントサイドスクリプトAstroコンポーネントでは、標準的なHTMLの<script>
タグを使用してクライアントサイドにインタラクティビティを追加することができます。
スクリプトは、イベントリスナーの追加、アナリティクスデータの送信、アニメーションの再生など、JavaScriptがWeb上で実行できるあらゆることに使用できます。
<button data-confetti-button>お祝いする</button>
<script>
// npmモジュールをインポートする。
import confetti from 'canvas-confetti';
// ページ内のコンポーネントDOMを見つける。
const buttons = document.querySelectorAll('[data-confetti-button]');
// ボタンがクリックされたときに紙吹雪(confetti)を発生させるイベントリスナーを追加します。
buttons.forEach((button) => {
button.addEventListener('click', () => confetti());
});
</script>
Astroはデフォルトで<script>
タグを処理・バンドルし、npmモジュールのインポートやTypeScriptの記述などに対応しています。
📚 詳細についてはスクリプトのガイドを参照してください。
HTMLコンポーネント
Section titled HTMLコンポーネントAstroは.html
ファイルをコンポーネントとしてインポートして使用したり、これらのファイルをページとしてsrc/pages
のサブディレクトリに設置することをサポートしています。フレームワークなしで構築された既存のサイトからコードを再利用したい場合や、コンポーネントに動的な機能が確実に入らないようにしたい場合はHTMLコンポーネントを利用するといいでしょう。
HTMLコンポーネントは有効なHTMLしか含むことができず、そのためAstroコンポーネントの主要機能が制限されます。
- フロントマターやサーバーサイドのインポート、動的な記法をサポートしません
- すべての
<script>
タグはバンドルされずに残され、is:inline
を持つ場合と同じように扱われます。 public/
フォルダにあるアセットのみを参照できます。
次のステップ
Section titled 次のステップ📚 Astroの組み込みコンポーネント (EN)について学びます。
📚 AstroプロジェクトでのJavaScriptフレームワークコンポーネントの使用方法について学びます。