Astro+SvelteでつくるWebサイト 第1回 環境構築と開発サーバーの確認

小規模なコーポレートサイトを作るという想定で、AstroとSvelteを使ってWebサイトを作成していきます。まずは環境構築と開発サーバーの確認をします。

発行

著者 渡辺 由 フロントエンド・エンジニア
Astro+SvelteでつくるWebサイト シリーズの記事一覧

はじめに

「会社のホームページを作りたいです。ページ数は5ページくらいで、お知らせは担当者が更新できるようにしてください」と言われたらどのように作りますか? よくありそうなコーポレートサイトの案件ですが、もちろん選択肢はたくさんあり、これが正解! というものが決まっているわけではありません。

正解が決まっているわけではないのですが、2024年2月現在、筆者なら、フレームワークには、まずAstroを選択します。会社サイトであれば1日に何度も更新するとか、ユーザーの入力によって動的に表示を切り替えたいということはあまりないと思うので、Astroで静的にHTMLをビルドできればパフォーマンスや運用コストの面でメリットがあると考えます。

Jamstack

こういった、事前にデータを埋め込んだ静的HTMLを生成することで、配信するWebアプリケーションと、コンテンツを管理を完全に分離したアーキテクチャをJamstackといいます。もっと詳しく知りたい方は、ピクセルグリッドの会社サイトにある、「Jamstackとは?」のページをご覧ください。

ブラウザ上で動くJavaScript

サイトがHTMLとCSSのみで構成されるのであればAstroだけで実現できますが、実際にはブラウザでJavaScriptを実行したい場面もあるでしょう。そのために、Astroは任意のUIライブラリと組み合わせて使えるようになっています。ここでは、Svelteを採用します。

Svelteの選択理由は、ビルド後のコードサイズが小さい点と記法がピュアなJavaScriptに近い点です。コーポレートサイトであれば、それほど複雑なスクリプティングは必要ないと思われるので、選択しました。とはいえ、AstroであればJavaScriptフレームワークの選択肢が多く、将来的にSvelteから他のものに切り替えたり、共存させることも可能です。

この先、AstroコンポーネントにもSvelteコンポーネントにもJavaScriptを書いていきますが、AstroコンポーネントのJavaScriptはビルド時に実行され、SvelteコンポーネントのJavaScriptはブラウザで実行されます。サイトを作りながら、その違いを確認していきましょう。

JavaScriptのランタイム

さらに言えば、AstroコンポーネントのJavaScriptはNode.js上で、SvelteコンポーネントのJavaScriptはブラウザ上のV8エンジンなどの上で実行されています。

この連載では、AstroとSvelteをベースに架空の会社サイトを作り、公開・運用までできるようにしていきます。選択肢を増やすという意味でも、Astro+Svelteでどんなことが実現できるのか、できないことは何かを具体的にイメージする助けになれば幸いです。

この連載で取り上げる内容

CodeGridにはすでにAstroを扱った記事がありますが、この連載では、より実践的な内容を扱います。

  • SvelteやLinterを含めた環境構築
  • 内容が固定されたページを作る
  • 更新可能なお知らせページを作る
  • お知らせ記事に既読表示をつける
  • サイト内検索機能を追加する
  • GitHubにpushすると自動的に公開・更新されるようにする
  • パフォーマンスや運用コストのこと
  • SSRやホスティングサービスなどのさらなる選択肢

AstroやSvelteについて、詳しくは

このシリーズはAstroやSvelteを触ったことがない方でも読み進めていただけますが、それぞれの基本的な設計思想や機能については詳しくは触れませんので、そのあたりは以下の記事をご参照ください。

この連載に沿ってひととおりのサイト構築をやってみたのちに、上記の記事や、その他のAstroの機能についての記事を参照していただくのもおすすめです。

作るもの

架空の会社「CodeGrid社」のコーポレートサイトです。以下のようなページがあります。

  • トップページ
  • 会社概要
  • 製品紹介
  • 採用情報
  • アクセス
  • お知らせ
    • 一覧ページ
    • 個別の詳細ページ

お知らせページには適宜記事が増えていく想定ですので、一覧ページと詳細ページを作ります。他は単一のページです。

ChatGPTに作ってもらった架空のコンテンツ

コンテンツはChatGPTに頼んで作ってもらいました。今回は架空のサイトのまま終わるので、本番サイトそっくりなコンテンツであるのは良いのですが、仮のコンテンツとして使う場合には「ダミーだ」と気づかずに本番公開してしまうリスクもあるかもしれませんね。

環境構築

ではさっそく制作を始めましょう。まずはAstroをインストールします。Astroのサイトでも紹介されているとおり、npm initコマンドを使って、対話しながらAstro入りのpackage.jsonを作成します。

npm init以外の方法

手動でpackage.jsonを用意して、ファイルにプロジェクトの名前や使用するパッケージを書き込んでいくこともできますし、過去にAstroで作ったプロジェクトをコピーしてきてそれをベースに……というのもやりがちです。ただ、Astroはバージョンアップも比較的早く、そのたびに変更になる点もあるので、毎回npm initするのが良いと筆者は考えています。

サイトを作りたいディレクトリで、以下のコマンドを実行します。

npm init(npm create)でAstroをインストール

npm create astro@latest

なお、AstroのWebサイトに書かれているnpm createは、npm initのエイリアスなので、まったく同義です。

すると、対話式にいろいろと聞かれますので、以下を選択します。

  • [email protected] をインストールしていいか? … yes(y)
  • dir=どこにプロジェクトを作る? … "demo-site" など
  • tmpl=どのテンプレートを使う? … "Empty" を選択
  • deps=依存関係をインストールする? … yes
  • ts=TypeScriptを使う? … no
  • git=gitリポジトリを作る? … yes

これでdemo-siteのような、指定した名前のディレクトリができているはずです。ちなみに「tmpl(どのテンプレートを使う?)」のところで"Empty"の代わりに"Include sample files (recommended)"を選んでも、それほど中身に違いはありません。

Houston 🚀

対話中に"Houston"などと出てきますが、これはAstroやAstronaut(宇宙飛行士)にちなんで、NASAの管制センターがあるヒューストンを指しているようです。「Launch」「Liftoff」などそれらしき単語も使われていて、管制官とともに宇宙に旅立つ準備をしているイメージなんですね。

開発サーバーの起動

さて、さっそくdemo-siteディレクトリに移動してみましょう。

テンプレートにEmptyを選択した場合のデフォルトのディレクトリ構成

.
├── README.md
├── astro.config.mjs
├── node_modules
├── package-lock.json
├── package.json
├── public
│   └── favicon.svg
├── src
│   ├── env.d.ts
│   └── pages
│       └── index.astro
└── tsconfig.json

それほど多くのディレクトリやファイルはありません。Webサイトの元になるコードは、srcディレクトリの中にあるので、今後は主にここで作業していきます。最後に、astro.config.mjsにAstroのビルド設定が書かれています。

ローカルでの実行

まずはデフォルトでどのようなサイトができているのか、確認してみましょう。npm run devコマンドを実行すると、Astroの開発サーバーが起動します。起動したらターミナルに表示されているとおりhttp://localhost:4321/に、ブラウザでアクセスしてみてください。「Astro」の文字が出てくれば成功です。

画面下部にツールバーが隠れており、ホバーすると出てきます。これはAstroの開発サーバーが提供している機能なので、コードには含まれているわけではなく、開発環境以外では出てきません。公式ドキュメントへのリンクや、開発時に便利なツールが提供されています。

Svelteを追加

この状態ではまだSvelteが使えません。.svelteファイルを用意しても、ビルド時にエラーになります。Svelteを使えるようにするには、必要なパッケージを追加し、Astroの設定ファイルも編集する必要がありますが、Astroにはそれらをまとめてやってくれる機能が備わっています。

ターミナルで、先ほど作ったdemo-siteディレクトリに入り、Astroが備えているaddというコマンドを使います。

Svelteを追加

cd demo-site
npx astro add svelte

すると、インストール時のようにまた対話が始まります。インストールするSvelteのバージョンや、configファイルに追加する内容の確認ですが、ここはすべてy(yes)で問題ありません。後から変更することもできます。

Svelte以外の追加

Svelteだけでなく、メジャーなJSフレームワークやツールについては、astro addで追加できるようになっています。詳しくはドキュメントを参照してください。

Svelteコンポーネントを追加する

Svelteが使えるようになったので、さっそく.svelteファイルを追加してみましょう。srcディレクトリの中にcomponentsというディレクトリを作り、その中にHello.svelteというファイルを作ります。中身は以下のようにします。

Svelteコンポーネントの追加

<script>
  import { onMount } from 'svelte';

  let currentTime = '';

  onMount(() => {
    const now = new Date();
    currentTime = now.toLocaleTimeString();
  });
</script>

<div>
  <p>Hello! This is Svelte component.</p>
  <p>{currentTime}</p>
</div>

コンポーネントのマウント時に、現在時刻を表示するだけのコンポーネントです。

Astroコンポーネントを編集する

このSvelteコンポーネントを表示したいので、Astroコンポーネントに埋め込みます。トップページの元になっている/src/pages/index.astroを編集してしまいましょう。

index.astroにSvelteコンポーネントを追加

---
import Hello from '../components/Hello.svelte'; // Svelteコンポーネントの読み込み

const currentTime = new Date().toLocaleTimeString();
---

<html lang="ja">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
    <meta name="viewport" content="width=device-width" />
    <meta name="generator" content={Astro.generator} />
    <title>Astro+Svelte demo-site</title>
  </head>
  <body>
    <div>
      <p>Hello! This is Astro component.</p>
      <p>{currentTime}</p>
    </div>
    <hr>
    <Hello client:visible /> // Svelteコンポーネントの表示
  </body>
</html>

冒頭のimportで先ほど作ったSvelteコンポーネントを読み込み、<Hello client:visible />で表示しています。client:visibleはAstro独自の記法ですが、clientはクライアント側でのみ実行されることを意味し、visibleはページが表示されたタイミングで実行されることを意味します。

さらに、Svelteコンポーネントと同じ、現在時刻を表示するJavaScriptも追加しています。冒頭でも触れたようにAstroコンポーネントのJavaScriptはビルド時に実行され、一方のSvelteコンポーネントはクライアント側で実行されるのですが、どのような挙動になるか、確認してみましょう。

ブラウザで表示してみる

ブラウザで見てみましょう。先ほどと同様にnpm run devコマンドを実行します。http://localhost:4321/にアクセスすると以下のように表示されたでしょうか?

続いてリロードしてみてください。時間が進みますが、AstroとSvelteの2つの時刻は同じままです。不思議に思った方も、そういうことか! と思った方もいらっしゃるかもしれませんが、devコマンドで起動する開発サーバーはあくまでも開発用で、サイトをビルドした時の挙動とは異なります。

Ctrl+Cキーで開発サーバーを停止し、今度はnpm run buildを実行してみてください。これで、distディレクトリにHTMLやCSSが書き出されました。

本番サイトとして公開するのはこのdistディレクトリですので、その状態を確認してみましょう。今度はnpm run previewを実行します。これで、distディレクトリをサービスするサーバーが起動します。

再度http://localhost:4321/にアクセスすると、今度はAstroコンポーネントとSvelteコンポーネントの時間がずれているはずです。

Astroコンポーネントに記述したJavaScriptはビルド時に実行されるので、表示時刻も最後にビルドした際の時刻です。一方のSvelteコンポーネントはクライアント側で実行されるので、表示時刻は現在時刻になります。ページをリロードすれば、Svelteコンポーネントのみ時間が進みます。

ほとんどの場面ではnpm run devで開発サーバーを確認しながら進めていけるのですが、実際に、ユーザーに提供するサイト(ビルドしたサイト)とは挙動が異なる点もあるので、注意が必要です。ときどきnpm run previewで確認しながら作業すると良いでしょう。

JavaScriptの書き分け

JavaScriptの実行タイミングについてなんとなくイメージできたでしょうか。サイトを作っていく上では、ビルド時に実行されれば良いものはAstroコンポーネントに、ブラウザで実行したいものはSvelteコンポーネントに書いていきます。

今回作っていくサイトであれば、トップページやお知らせなど、ほぼすべてのコンテンツはいつ誰がアクセスしても同じものが表示されてOKなので、ビルド時に実行できれば良い=Astroコンポーネントを使います。

一方、ユーザーのアクションなどによって、表示を変えたいところにはSvelteコンポーネントを使うことになります。今回の場合だと、「お知らせ」に既読表示をつけるという機能が当てはまります。また、今回は実装しませんが、スマートフォンなどに向けたハンバーガーメニューや、ユーザーのログイン機能、フォームのバリデーション機能なども、どのように表示するべきかはビルド時には一つに決められませんので、Svelteコンポーネントの出番です。

Linterの追加

最後に、コードの品質を保つためにLinterやPrettierを追加しておきましょう。必要なnpmパッケージを追加し、コマンドが実行できるようにします。

ESLintのインストール

npm install -D eslint eslint-plugin-astro eslint-plugin-svelte @typescript-eslint/parser

Prettierのインストール

npm install --save-dev prettier prettier-plugin-astro prettier-plugin-svelte eslint-config-prettier

それぞれにconfigファイルを用意し、package.jsonにもコマンドを追加しておきます。詳しくはデモコードを参照してください。npm run lintでLinterが実行され、npm run formatでPrettierが実行されるようになっています。

LinterとPrettier

Linterは、コードの品質を保つためのツールです。コードの書き方についてのルールを定めておき、それに沿っていないコードがあればエラーを出してくれます。ここでインストールしているESLintは、とても有名なLinterのひとつです。また、Prettierは、コードのフォーマットを整えてくれるツールです。

LinterとPrettierは、それぞれの役割が異なるので、両方を使うことが多いです。下記のシリーズに詳しく書いていますので、参考にしてください(第5回にPrettierについても紹介されています)。

上記の記事では、eslint-plugin-prettierを使って、ESLintとPrettierを連携させていますが、このプラグインによる連携方法は現在は推奨されていないため、この連載では使用していません。ルールの指定や実行方法については、本記事の最後にあるデモコードで確認いただけます。gitコミット時の自動実行も設定してあります。

連携方法について、詳しくはPrettierのドキュメントを参照してください。

ここまでのまとめ

まずはサイトを作っていく土台ができました。次回は、サイト内で共通のヘッダー・フッターや、内容が固定されたページを作っていきます。

ここまでのデモのコード

ここまでのサイトのデモのコードは、以下のリンクから見ることができます。