VueやJSXは好きだけどSPAは要らない、そんなあなたに Astro
「EJS, Nunjucks, PugなどでWeb制作をやってたけど、もっとモダンな開発がしたい・・・」
「ReactやVueに手を出してみたけど、別にアプリケーションが作りたいんじゃなくて静的なWebページが作りたいんだ・・・」
「Next, Gatsby, Nuxtみたいな静的サイトビルダーを使ったけど、結局SPAの挙動するやん・・・」
「SPAは別にいらんねん・・・」
いずれかに当てはまる方、いませんか?(全部私が思ってたこと)
そんなあなたに Astro
です。
Astro
ではどんな書き方ができるの?
Astroにはいろんな特徴がありますが、この記事では「どんな書き方ができるのか」を中心に紹介します。
Astroでは .astro
という独自のファイル形式を使用します。
早速ですが、Astroでどんな書き方ができて、どんな出力が得られるのか見てみましょう。
こういう書き方↓ができて・・・
---
// フロントマッター内にJS/TSが書ける!(ビルド時に実行される)
const list = ['aaa', 'bbb', 'ccc'];
---
<!-- Reactのように(ほぼ)JSXで書ける! -->
<ul>
{list.map((item) => <li>{item}</li>)}
</ul>
<style lang="scss">
// VueのようなScoped CSSが書ける!
li {
// Sassなどのメタ言語も使える!
&:first-child {
color: red;
}
}
</style>
<script>
// ブラウザで実行されるJS/TSも書ける!
console.log('in browser');
</script>
---
// コンポーネントをimportして使う
import Component from '../components/Component.astro';
---
<!DOCTYPE html>
<html lang="ja">
<head>
<title>Sample</title>
</head>
<body>
<Component />
</body>
</html>
ビルドすると、こういう出力↓が得られる・・・!
<!DOCTYPE html>
<html lang="ja">
<head>
<title>Sample</title>
<link rel="stylesheet" href="/assets/index.1a99575d.css" />
<script type="module">
console.log('in browser');
</script>
</head>
<body>
<ul class="astro-QIHFUJBN">
<li class="astro-QIHFUJBN">aaa</li>
<li class="astro-QIHFUJBN">bbb</li>
<li class="astro-QIHFUJBN">ccc</li>
</ul>
</body>
</html>
li:where(.astro-QIHFUJBN):first-child {
color: red
}
※見やすさのために整形しています。
記法はReactやVueのいいとこ取り! ✨
出力後はSPAではなく完全に静的なHTML ✨
最高 ✨ 天才 ✨ 超イケメン ✨
もうちょい詳しく
それでは、もう少し詳しく .astro
コンポーネントの機能を見ていきましょう。
テンプレートエンジンとして
HTML中心のテンプレートを、JS (TS) を使用して出力することができます。EJSにも近いイメージです。
フロントマッター内に書かれたスクリプトは、ビルド時に(つまりNodeで)のみ実行されます。
そして、フロントマッター内で宣言した変数はHTMLテンプレート内で使用できます。
AstroのHTMLテンプレートは「JSXに似た式 (JSX-link Expressions)」と説明されている通り、ほぼJSXだけどJSXじゃない形式です。
たとえばJSXでは className
を使用しますが、Astroでは普通に class
を使用します。
よりHTMLに近くていいですね。
---
const name = "test";
console.log(name); // このconsole.logはターミナルに表示される
---
<div>変数の文字列を表示することが可能:{name}</div>
<div class={name}>属性の値に入れることも可能</div>
↓出力
<div>変数の文字列を表示することが可能:test</div>
<div class="test">属性の値に入れることも可能</div>
出力後は、完全に静的なHTMLになります。
変数部分がブラウザ上で動的に変わることはありません。
画像などのimport
画像などの静的アセットファイルをimportすると、そのファイルに対するパスが文字列として取得できます。
importされたファイルはビルド時に出力(コピー)されます。
デフォルトではハッシュがついたファイル名に変更されます。
---
import imageSample from "./sample.jpg";
---
<img src={imageSample} alt="サンプル画像" />
↓出力
<img src="/assets/sample.fda7c236.png" alt="">
もしファイル名を間違えていたらビルド時にimportしようとした段階でエラーが発生するので、リンク切れの心配不要になって幸せになれます。
コンポーネントProps
コンポーネントにPropsを渡すことができます。
---
const { name } = Astro.props;
---
<p>{name}</p>
---
import Component from "./Component.astro";
---
<Component name="なまえ" />
TypeScriptを使用する場合、型 Props
をexportすることで型を定義できます。
Astro.props
の型は、Props
の情報を元に解釈されます。
(これまでは as Props
が必要でしたが、最近のアップデートで自動で解釈されるようになっています。)
---
export type Props = {
name: string;
disabled?: boolean;
};
const { name, disabled = false } = Astro.props;
---
スロット (Slots)
コンポーネントの中に入れる要素を渡すことができます。
Reactでいうとchildren、Vueのslotにあたる機能です。
Vueのslotと同じく、複数持つことができます。
<div class="wrapper">
<main>
<slot />
</main>
<footer>
<slot name="footer" />
</footer>
</div>
---
import Wrapper from '../components/Wrapper.astro';
---
<Wrapper>
<p>メインコンテンツ</p>
<nav slot="footer">フッター内に入る</nav>
</Wrapper>
スコープ付きCSS (Scoped CSS)
Vueの <style scoped>
のような機能です。
ランダムなクラス名が追加されることで、そのコンポーネント内のみにスタイルが当てられます。
<p>テキスト</p>
<style>
p {
color: red;
}
</style>
↓出力
<p class="astro-QIHFUJBN">テキスト</p>
p:where(.astro-QIHFUJBN) {
color: red
}
ブラウザで実行するスクリプト
scriptタグに書いたスクリプトは、ブラウザ上で実行されるscriptタグとして出力されます。
TypeScriptもサポートされています。
(以前はscriptタグ内にTypeScript記述ができなかったのですが、できるようになってました!)
<button data-trigger>Click!</button>
<script>
document.querySelector<HTMLButtonElement>('[data-trigger]')?.addEventListener('click', () => {
console.log('Clicked!');
});
</script>
宣言的UIはできないの?
.astro
コンポーネントではできない。
なんたってHTMLテンプレートの出力はビルド時のみに実行され、完全に静的なHTMLになるから。
.astro
コンポーネントはテンプレートエンジンと思ったほうがイメージとしては合ってます。
ですが、 .astro
ファイルでは ReactやVue, Svelte, Solid, ... といった他のフレームワークを使用できます。
宣言的UIをしたい場合は、そういった他のフレームワークと組み合わせるといいでしょう。
---
import ReactComponent from './ReactComponent.jsx';
---
<ReactComponent client:load />
外部フレームワークのブラウザ上でのスクリプトは、client:*
ディレクティブを設定することで実行されます。
逆に、ReactやVueのUIフレームワークを使いつつ、ブラウザ上のスクリプト実行を消し去りたいなんて場合は client:*
ディレクティブを付けなければOKです。
さいごに
その他詳しくは公式ドキュメントの Astroコンポーネント
のページを参照ください。
(日本語です)
それでは、よきAstroライフを🚀
Astroは…いいぞ。
余談
コードブロックを記述する際、
```astro
```
ではZennでシンタックスハイライトがつかなかったため、 jsx
か html
に設定しました。
そのため、シンタックスハイライトが完全ではないです。
Discussion