🥵

Astroの概要と基本的な文法をちょっと理解する

2023/02/07に公開
1

概要

最近よく聞くAstroというJavaScriptフレームワークについての記事です。
話題のAstroの概要と、これまでのJavaScriptフレームワークの違いについて簡単に調べてみました。
また、公式ドキュメントのチュートリアルをやりながら、Astroの基本的な文法、記述方法を学習したので、備忘録がてら記事にしてみました。

この記事でわかること

  • Astroの概要
  • Astroのメリット
  • Astroと従来JavaScriptフレームワークの違い
  • Astroの基礎文法

Astroとは

まず、Astroとは、コンテンツにフォーカスした高速なWebサイトを構築するためのWebフレームワークで、静的サイトジェネレーターの一つです。
JavaScriptを使ってアプリケーションを開発する際に生成されたHTMLをビルド時に削除することで、高速でコンテンツ重視のWEBサイトを作成することを目的としています。
また、Astroは、SPAではなくMPAを採用しており、クライアント側の負荷軽減や初期レンダリングの高速化を実現しています。

https://astro.build/

マルチページアプリケーション(MPA)とは

マルチページアプリケーション(MPA) とは、複数のHTMLページで構成されるWebサイトのことです。
MPAは、サーバーサイドで生成されるHTMLをブラウザに送信し、ブラウザ上で表示されます。
それに対して、シングルページアプリケーション(SPA)は、一度に全てのコンテンツを読み込んで、JavaScriptで動的に表示を制御します。
MPAは、オンデマンドでページを読み込むため、初期表示が早く、クライアント側の負荷を軽減することができます。
また、検索エンジンにとっても可読性が高く、SEO対策にも適しています。
ただし、MPAは、各ページ間の遷移には時間がかかり、パフォーマンスが劣ることがありるため、SPAとMPAは一長一短と言えます。

MPAのルーティング

MPAでは、サーバーへのリクエストごとに、どのHTMLで応答するかを決定するため、ルーティングロジックはサーバーに格納されます。
SPAでは、ルーターがブラウザでローカルに動作し、サーバーにアクセスすることなく新しいページをレンダリングするためにクライアント側でルーティングを行います。
SPAにおけるルーティングのメリットとして、ナビゲーションでより高速なトランジション(遷移アニメーション)を提供できるという点が挙げられます。
これに対して、Astroでは、HotwireのTurboのようなツールを活用し、ブラウザのナビゲーションも制御してクライアントルーティングを模倣しています。
HTMLは依然としてサーバーでレンダリングされますが、TurboによってSPAのクライアントルーティングと同様に、ページ間のシームレスな遷移を表示できるようになりました。

Astroのメリット

サーバファースト

Astroは、クライアントサイドのレンダリングよりもサーバーサイドのレンダリングを可能な限り活用します。
このアプローチは、Next.js、SvelteKit、Nuxt、Remixなど、他のモダンなJavaScriptウェブフレームワークとは対照的で、これらのフレームワークでは、ウェブサイト全体のクライアントサイドレンダリングが必要で、サーバーサイドレンダリングは主にパフォーマンス上の懸念へ対処するために行われます。
SPAモデルには様々な利点がありまが、その代償として、さらなる複雑さとパフォーマンスがトレードオフの関係性になっており、ファーストロードのパフォーマンスが不可欠なコンテンツ重視のWebサイトではあまり意味を成しません。

簡単に使える

Astroが簡単に使える理由は以下の2つです。

  • 他のライブラリを使用することが可能

    React、Preact、Svelte、Vue、Solid、Litなどはすべてサポートされており、それを使ってAstroプロジェクトで新しいUIコンポーネントを作成できます。
    よって、各人が使い慣れたライブラリでの実装が可能になり学習コストを削減することができます。

  • サーバサイドレンダリングに特化した設計

    ReactやNext.jsなどの従来のSPAでは、ブラウザ上でのレンダリングに加え、サーバサイドレンダリングの実装も進んでおり、アプリケーション開発が複雑化してきています。
    それに対して、Astroがブラウザ上ではなく、サーバ上でレンダリングするように設計されているため、フロントエンドに複雑な実装を持たせる必要がなくなり、理解しやすいコードを書くことができます。

充実した機能と柔軟性

コンポーネント構文、ファイルベースのルーティング、アセットハンドリング、ビルドプロセス、バンドル、最適化、データフェッチなど、Astroにはさまざまな機能が搭載されています。Astroのコア機能だけで、優れたウェブサイトを構築できます。
さらにコントロールが必要な場合は、 React、Svelte、Vue、MDXなど、100以上のインテグレーションを使用してAstroを拡張できます。お気に入りのCMSを接続したり、お気に入りのホストにデプロイするのも、コマンド1つでできます。
また、同じページで異なるフレームワークを混在もでき、柔軟性の高いコードを書くことができます。

Astro Islands

Astro Islands(別名:コンポーネントアイランド)は、AstroのWebアーキテクチャーのパターンです。
アイランドとはHTMLの静的なページ上にあるインタラクティブなUIコンポーネントを指し、それらのインタラクティブなコンポーネント(アイランド)は常に独立して読み込まれ、要求に応じて実行することが可能です。
Astroでは、サポートされているUIフレームワーク(React、Svelte、Vueなど)を使って、ブラウザ上でアイランドをレンダリングできます。

astro island

Astroの基礎文法&サンプル

公式チュートリアルで学習した内容のうち、基本的かつ個人的によく使いそうだと思った内容とそのサンプルをまとめました。

1, Astroプロジェクトの構築

まず、以下のコマンドをターミナルで入力し、Astroプロジェクトを初期化します。
尚、今回はnpmで作成します。

npm create astro@latest

上記コマンドを実行すると、セットアップウィザードが起動するので下記のように続けていきます。

  1. create-astroインストールするためにyを押下
  2. Where would you like to create your new project?というプロンプトが表示されたら、フォルダ名を入力して、プロジェクト用の新しいディレクトリを作成(例:./tutorial)。
  1. スターターテンプレートの短いリストが表示されるので、Empty Projectテンプレートを選択
  2. プロンプトがWould you like to install dependencies?と尋ねたら、yを入力
  3. プロンプトが「新しいgitリポジトリを初期化しますか」と尋ねたら、yを入力
  4. 最後にTypeScriptのオプションは好みに応じて選択して下さい

上記でプロジェクトの環境構築は完了です。

2, Astroのルーティング

静的ルーティング

src/pagesディレクトリにある、Astroコンポーネント(.astro)とMarkdownファイル(.md)は自動的にウェブサイトのページとなります。各ページのルーティングは、src/pagesディレクトリ内のパスとファイル名に対応しています。

下記が、ルーティング例です。

src/pages/index.astro        -> mysite.com/
src/pages/about.astro        -> mysite.com/about
src/pages/about/index.astro  -> mysite.com/about
src/pages/about/me.astro     -> mysite.com/about/me
src/pages/posts/1.md         -> mysite.com/posts/1

以下、静的ルーティングのサンプルです。

https://stackblitz.com/edit/github-rrciyn?embed=1&file=src/pages/index.astro

動的ルーティング

Astro ページファイルは、ファイル名で動的なルートパラメータを指定して、動的ルーティングを行うことができます。
動的ルーティングはgetStaticPaths関数をを定義して実装することができます。
getStaticPaths関数は、params(URLのパス)と、オプションで任意のprops(これらのページに渡したいデータ)を含むオブジェクトのリストを返す必要があります。

手順は以下の通りです。

  1. src/pages/[dog].astro という新規ファイルを作成する

  2. getStaticPaths関数を定義し、返り値としてparamsを設定する

    以下のように定義することで、/dogs/clifford/dogs/rover/dogs/spotの3つのページが生成され、それぞれ対応する犬の名前が表示されます。

    [dog].astro
    ---
    export function getStaticPaths() {
      return  [
        {params: {dog: 'clifford'}},
        {params: {dog: 'rover'}},
        {params: {dog: 'spot'}},
      ];
    }
    
    const  { dog }  = Astro.params;
    ---
    

以下、動的ルーティングの実装サンプルです。

/dogs/clifford/dogs/rover/dogs/spotが生成できていることが確認できます。

https://stackblitz.com/edit/github-cntfy6?embed=1&file=src/pages/index.astro

3, スタイル設定

Astro独自の<style>タグを使用すると、対象のコンポーネントにスタイルを適応することができます。
<style>タグの中にCSSを記述することで該当する要素にスタイルを適応することができます。
また、Astroでは、コードフェンス[1]内に変数を定義し、CSSで使用することができます。

具体的には下記の手順でスタイルを当てます。

  1. コードフェンス内で変数を定義する
  2. <style>タグにdefine:vars={{}}ディレクティブを適応する
  3. 該当箇所でCSSの値に変数を適応する。

以下、サンプルです。

https://stackblitz.com/edit/github-jmvwwc?embed=1&file=src/pages/index.astro

4, Componentsの作成

componentsとは、部品、成分、構成要素などの意味を持つ英単語で、webの文脈では、ボタンやヘッダーやフッダーなど、webページな必要な部品のことを指します。
各部品をcomponentsとして管理しておくことで、各ファイルで使用したい際に呼び出すことができ、コードの可読性を高め、記述量を減らすことができます。
また、astroでは、React同様にコンポーネントにpropsを渡すことができます。
propsは、コードフェンス内で Astro.propsを定義して利用します。

詳しくは、公式ドキュメントをご参照下さい。

今回は例として、Navigation componentsを作成し、ナビゲーションリンクを各ページで使い回してみます。

components作成の手順は以下のとおりです。

  1. src/components/配下にNavigation.astro を新規に作成する

  2. コンポーネントの内容とpropsを定義する

    astroの場合以下のようにpropsを定義します。

    ---
    // propsの定義
    const {nav1, nav2 , nav3}= Astro.props
    ---
    
  3. Navigation.astroをインポートして使用する

    propsを渡す際は以下のように記述します。

    ---
    // Componentのインポート
    import Navigation from "../components/Navigation.astro"
    ---
    
    <!-- componentの呼び出し -->
    <Navigation nav1='Home' nav2='about' nav3='blog'/>
    

以下、ナビゲーションリンクコンポーネントのサンプルです。

https://stackblitz.com/edit/github-5atmuj?embed=1&file=src/pages/index.astro

5, クライアントスクリプト

Astroコンポーネントは、標準的なHTMLの<script>タグを使用したクライアントサイドのインタラクティブ機能の追加をサポートしており、JavaScriptを記述する際は<script>タグを使用します。

具体的には

  • イベントリスナーの追加
  • 分析データの送信
  • アニメーションの再生
    など、ウェブ上でJavaScriptが実行可能な処理すべてを使用できます。

例として、アラートボタンを実装しました。

以下サンプルです。

https://stackblitz.com/edit/github-mvnxqf?embed=1&file=src/pages/index.astro

6, Layout

レイアウトは、ページテンプレートのような再利用可能なUI構造を提供するために使用されるコンポーネントです。
ヘッダー、ナビゲーションバー、フッターなど、ページ間で共有される共通のUI要素を提供するAstroコンポーネントは、慣習的に「レイアウト」と呼ばれます。
使用する際は <slot />を使って、個々のページのコンテンツを表示したい場所を指定します。

---
import Header from '../components/Header.astro';
import Footer from '../components/Footer.astro';
const { title } = Astro.props
---
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <BaseHead title={title}/>
  </head>
  <body>
    <nav>
      <a href="#">Home</a>
      <a href="#">Posts</a>
      <a href="#">Contact</a>
    </nav>
    <h1>{title}</h1>
    <article>
      <slot /> <!-- ここに各ページのコンテンツが入ります -->
    </article>
    <Footer />
  </body>
</html>

サンプルとしてヘッダーとフッターをレイアウト化してみました。

https://stackblitz.com/edit/github-qzszu1?embed=1&file=src/pages/index.astro

7, アイランドアーキテクチャ

最後に、Astroの目玉機能?でもあるアイランドについてです。
上でも説明したように、Astroでは、サポートされているUIフレームワーク(React、Svelte、Vueなど)を使って、ブラウザ上でJavaScriptをレンダリングできます。(参考)

また、クライアントサイドでJavaScriptを実行するためにはclient:というディレクティブを設定する必要があります。
clientディレクティブが、ページが読み込まれたときにJavaScriptをクライアントに送信して再実行し、コンポーネントを対話型にするようAstroに指示するためです。
これをハイドレイトコンポーネントと呼びます。

また、clientディレクティブには、いくつかの値を設定でき、その値に応じて挙動が変わります。設定可能な値については、こちらをご確認下さい。

実装の手順は以下の通りです。

  1. Astroプロジェクトに任意のUIフレームワークを追加する

    CLIにてnpx astro add [任意のフレームワーク]を入力することで、導入が完了します。

  2. コンポーネントの作成と呼び出し

    各フレームワーク・ライブラリの記法に沿って、プログラムを記述し以下のように呼び出して下さい。

    ---
    import MyReactComponent from  '../components/MyReactComponent.jsx';
    ---
    <html>
      <body>
        <h1>Astroで直接Reactコンポーネントを使用する!</h1>
        <MyReactComponent  />
      </body>
    </html>
    
  3. clientディレクティブを設定する

    ---
    import MyReactComponent from  '../components/MyReactComponent.jsx';
    ---
    <html>
      <body>
        <h1>Astroで直接Reactコンポーネントを使用する!</h1>
    		// clientディレクティブ
        <MyReactComponent client:load />
      </body>
    </html>
    

カウントボタンの実装サンプルを作りました。
下記サンプルMyReactComponentclientディレクティブを削除するとJavaScriptが動作しなくなります。

https://stackblitz.com/edit/github-ndwexw-rrg1km?embed=1&file=src/pages/index.astro

最後に

最後に、Astro公式チュートリアルの成果物である、ブログサイトのソースコードを貼り付けておきます。今回紹介した知識 + ちょっとで作成できるので、興味ある方は是非やってみて下さい!

https://github.com/shutootaki/astro-blogtut

私はかなりの弱々*100エンジニアなのですが、Astroのコードは理解し易くて書いていて楽しかったです。
ただ、複雑性を排除したはいいものの、色々なフレームワークをちゃんぽんできてしまうので、運用方法をしっかりと定めないと、やはり複雑なコードになってしまうのではないかと思いました。
今回は触れていませんが、Astro v2.0が2023年の1月末にリリースされたので、興味がある方は御覧ください。
本記事に間違い等ありましたら、速攻修正しますので教えてくれると嬉しいです。ありがとうございました。

参考

https://astro.build/
https://docs.astro.build/en/getting-started/
https://astro.build/blog/astro-2/

脚注
  1. Astroコンポーネント内のJavaScriptを識別するためのもの、importや変数定義をする際は、コードフェンス(---)を使用します。 ↩︎

Discussion

ryoppippiryoppippi

このアプローチは、Next.js、SvelteKit、Nuxt、Remixなど、他のモダンなJavaScriptウェブフレームワークとは対照的で、これらのフレームワークでは、ウェブサイト全体のクライアントサイドレンダリングが必要で、サーバーサイドレンダリングは主にパフォーマンス上の懸念へ対処するために行われます。

Next.js、SvelteKit、Nuxtについては、SSGを選択することでクライアントサイドレンダリングが必要なくなるので、この記述は不正確です。またSSRを用いることで、Client Side JSがオフでもレンダリング自体はなされます。
またRemixはSSRがデフォルトです。オプションではないので、パフォーマンス上の懸念と対処、というわけではないと考えられます。