Astro の気になった部分を試してみた

こんにちは。フロントエンドを担当しています mozato です。

先日 Twitter を徘徊していたところAstro というものを見つけ、気になった部分があったので試してみました。

Astro とは

https://astro.build/

Snowpack が先日発表した静的サイトジェネレータ (公式ではビルダー) です。

Astro が気になった理由は下記の Tweet の内容です。

興味深い⚡️が並んでいます。

特に気になったのは、基本的には JavaScript が使用されず必要な時に読み込んでくれるという内容です。

現在まだ early beta ですが、早速試してみました。 試した時のバージョンは 0.13.2 となります。

環境作成

「🔧 Quick Start」を参考に作成。

https://github.com/snowpackjs/astro#-quick-start

手順通りに進めると、テンプレート選択をする箇所があるのですが、今回は「Starter Kit (Generic)」を使用しました。

試すその前に

まずは「💧 Partial Hydration」に目を通しておきましょう。

https://github.com/snowpackjs/astro#-partial-hydration

なにか動きがあるコンポーネントを使いたい場合は :load , :visible , :idleコンポーネントの名前につけると良さそうです。

それぞれ JavaScript が読み込まれるタイミングが

  • :load はページ表示時
  • :visibleIntersectionObserver を使用し viewport 内にコンポーネントが入った時
  • :idlerequestIdleCallback() を使用しブラウザがアイドル状態の時

ということになりそうです。

:idle は試すの大変なので... :load:visible を試していきます。

試してみる

挙動を確認するための単純なカウンターコンポーネントを作成します。

src/components/Counter.jsx を作成し、内容を下記の様にします。(style は見やすさのため)

import { h } from 'preact';
import { useState } from 'preact/hooks';

export function Counter() {
  const [count, setCount] = useState(0);
  const increment = () => setCount((i) => i + 1);
  const decrement = () => setCount((i) => i - 1);

  return (
    <div
      style="
        display: flex;
        justify-content: center;
        padding: 1rem;
        border: 1px solid #ccc;
        font-size: 2rem;"
    >
      <button
        style="font-size: inherit;"
        onClick={decrement}
      >
        -
      </button>
      <p style="margin: 0 1rem; font-weight: 700;">
        {count}
      </p>
      <button
        style="font-size: inherit;"
        onClick={increment}
      >
        +
      </button>
    </div>
  );
}

作成したカウンターコンポーネントを表示させるために、src/pages/index.astro を下記の様に変更します。

---
import { Counter } from '../components/Counter.jsx';
---

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Astro</title>

    <link rel="icon" type="image/svg+xml" href="/favicon.svg">

    <link rel="stylesheet" href="/style/global.css">
    <link rel="stylesheet" href="/style/home.css">

    <style>
        header {
            display: flex;
            flex-direction: column;
            gap: 1em;
            max-width: min(100%, 68ch);
        }
    </style>
</head>
<body>
    <main>
        <header>
            <div>
                <img width="60" height="80" src="/assets/logo.svg" alt="Astro logo">
                <h1>Welcome to <a href="https://astro.build/">Astro</a></h1>
            </div>
        </header>

        <section>
            <h2 style="text-align: center;">
                <code>&lt;Counter /&gt;</code>
            </h2>

            <Counter />
        </section>
    </main>
</body>
</html>

ここまでできたら npm start を実行し、http://127.0.0.1:3000/ をブラウザで表示してみましょう。

カウンターコンポーネントを表示

先ほど作成したカウンターコンポーネントが表示されています。

試しにプラスボタンやマイナスボタンを押してみましょう。

...カウンターコンポーネントでインクリメントやデクリメントの処理を書いていたのですが、動作したでしょうか?

されないはずです!Astro はデフォルトでは HTML のみ出力してくれるからです。

デベロッパーツールの「Network」タブを選択して JavaScript がどうなっているか見てみると、

カウンターコンポーネントを表示した時のデベロッパーツールのネットワークタブでJSを選択

見事に何も読み込みされていません。

カウンターを動かしたい!

このままだと折角作成したカウンターコンポーネント君が、数を刻むこと無くその役目を終えてしまうので動作する様に変更してみましょう。

:load

まずは :load を試してみます。

:load をつけるとページ表示時に JavaScript を読み込みんでくれるはずです。

src/pages/index.astro<Counter /> と書いている箇所を下記の様にします。

<Counter:load />

それでは変更を保存しページを再読み込みしつつ、プラスボタンやマイナスボタンを押してみましょう。

カウンターコンポーネントに:loadをつけてボタンをクリック

きちんとカウンターとして動きました👏

デベロッパーツールの「Network」タブを選択して JavaScript がどうなっているか見てみると、

カウンターコンポーネントに:loadをつけた時のデベロッパーツールのネットワークタブのJSの様子

ページが表示されたタイミングで色々と JavaScript が読み込まれているのがわかります。

:visible

次は個人的に一番気になっていた :visible を試してみます。

:visible をつけるとviewport 内にコンポーネントが入った時に JavaScript が読み込まれるはずです。

src/pages/index.astro で先ほど <Counter:load /> としたところを <Counter:visible /> とします。

<Counter:visible />

まずは最初から viewport 内に表示されている場合を試してみます。 変更を保存したらページを再読み込みしてみましょう。

カウンターコンポーネントに:visibleをつけた時のデベロッパーツールのネットワークタブのJSの様子

:load の時と同じですが、最初から viewport 内に表示されているのですぐに JavaScript が読み込まれています。

それでは viewport の外に行ってもらうために margin などをつけて試してみます。

<div style="margin-top: 200vh;">
  <Counter:visible />
</div>

さてどうなるか...

カウンターコンポーネントに:visibleをつけてviewport に入った時のデベロッパーツールのネットワークタブのJSの様子

わー👏

画面内に入った時に JavaScript が読み込まれていますね!

loading="lazy" みたいに気軽!

「最初に2個 JavaScript があるじゃないか!」

  • hmr-client.js
  • visible.js

この 2つが最初に読み込まれていますが、visible.js は :visible を実現するために必要なもので、hmr-client.js はビルドすると読み込まれません。

必要なものなので許してください...

まとめ

気になった Astro の動作を簡単ですが試してみました。

必要なければ JavaScript を出力しないのは最高だと思っていたのですが、必要な時に JavaScript を読み込みしてくれるのはすごく面白い機能ですよね。

特にサイトのパフォーマンスを気にするような場面で活躍してくれるのではと思っています。

また最初から

  • React
  • Vue.js
  • Svelte
  • Preact

を使えるのも便利です。

全てを一気に使用しているサンプルが公式にある のですが、これが簡単に出来ちゃうのが本当に面白いなーと思います。

自分がブログなり LP を作る仕事を受けたりしたら、Astro を是非使ってみたいと思いました。

参考

snowpackjs / astro