Closed16

Astroの基本環境構築メモ

鮪缶の鮪缶の

はじめに

執筆者のこと

  • Astro初学者
  • 非エンジニア

目標

  • 幅広い用途で使い回しが可能な基本環境を構築する。
  • Githubにアップロードしておき雛形として活用する。
  • プロジェクト制作時に環境構築作業をスムーズにする。

開発環境

  • Node.js
  • pnpm
  • VSCode(拡張機能「Astro」をインストール)
  • Windows10

仕様

README:仕様一覧と導入目的を記録。

リンター・フォーマッター

対応次第Biome一本に切り替えていく予定。

  • Prettier:HTML
  • Stylelint:CSS/SCSS
  • Biome:JS/TS/JSON/JSXなど

gitignore

  • gitignore.ioで作成
  • astro/node.js/windows/VisualStudioCode

命名規則

  • ファイル名:ケバブケース
  • 変数名:キャメルケース
  • import宣言名:パスカルケース
鮪缶の鮪缶の

構築手順

Astroをインストール

pnpm create astro@latest

対話式を以下のように選択。

# Astroプロジェクトのディレクトリ名を指定
Where should we create your new project?
-> ./ディレクトリ名

# テンプレートの指定、Emptyはテンプレート無し
How would you like to start your new project?
-> Empty

# TypeScriptとの利用
Do you plan to write TypeScript?
-> Yes

# TypeScriptの構文ルールを厳格にするか、Strictは厳格
How strict should TypeScript be?
-> Strict

# 依存関係のインストール
Install dependencies?
-> Yes

# Gitの初期化
Initialize a new git repository?
->Yes

GitHub

  • 生成された.gitignoreにgitignore.ioで生成したコードを追加する。
  • リモートリポジトリを作成。
  • リモートリポジトリを追加。
git remote add origin [リモートリポジトリ名][リモートリポジトリURL]
鮪缶の鮪缶の

リンター・フォーマッター

インストール

StylelintとBiomeをインストールする。

pnpm install -d stylelint @biomejs/biome @latest

Stylelintのプラグイン

  • stylelint-config-recess-order:プロパティの並び替え。
  • stylelint-scss:SCSS用の設定。
  • stylelint-config-standard-scss:SCSSでのルール設定。
pnpm install -d stylelint-config-recess-order stylelint-scss stylelint-config-standard-scss

Prettier

少しつまずいた箇所。
公式ドキュメントからは「VSCodeの拡張機能内に内包されているため別途インストールは不要」と読み取れる。
しかし、実際はpnpmでパッケージをインストールする必要があるらしい。
PrettierとAstro用プラグインをインストール。

pnpm install prettier prettier-plugin-astro

設定ファイルを作成

  • .stylelintrc.cjs
  • biome.json
  • .prettierrc.cjs

Prettierで.astroを処理させる記述

.prettierrc.cjs
module.exports = {
  plugins: [require.resolve('prettier-plugin-astro')],
  overrides: [
    {
      files: '*.astro',
      options: {
        parser: 'astro',
      },
    },
  ],
};

npmスクリプト

  • pnpmを使用
  • SCSSと.astroに対応させる
    上記を意識して記述。
    今後Biomeに一本化していく時はここの書き換えを忘れないようにすること。
package.json
{
    "scripts": {
        "format": "pnpm run _typecheck && pnpm run _lint-fix && pnpm run _prettier-fix",
        "_lint": "biome check --ext .ts,.js,.json --ignore-path .gitignore . && stylelint \"**/*.{scss,css,astro}\"",
        "_lint-fix": "biome check --apply --ext .ts,.js,/json --ignore-path .gitignore . && stylelint --fix \"**/*.{scss,css,astro}\"",
        "_prettier": "prettier --check \"**/*.{html,scss,ts,js,astro}\" --ignore-path .prettierignore",
        "_prettier-fix": "prettier --write \"**/*.{html,scss,ts,js,astro}\" --ignore-path .prettierignore"
    }
}
鮪缶の鮪缶の

この時点のディレクトリ構成。

/astro
├─ .astro
├─ node_modules
├─ public
├─ src
│  └─ pages
│     └─ index.astro
├─ .gitignore
├─ .prettierignore
├─ .prettierrc.cjs
├─ .stylelintignore
├─ .stylelintrc.cjs
├─ astro.config.mjs
├─ biome.json
├─ package.json
├─ package.lock.yaml
├─ README.md
└─ tsconfig.json
鮪缶の鮪缶の

Astroインテグレーション

どういった制作場面でも使うであろうものをざっくり最小限に。

@astrojs/mdx

https://docs.astro.build/ja/guides/integrations-guide/mdx/
MDX形式への対応。
MDXはMarkdownをコンポーネントとして扱うためのフォーマットのこと。

pnpm astro add mdx

@astrojs/rss

https://docs.astro.build/ja/guides/rss/
RSSフィードを自動生成(rss.xml.ts)。

pnpm add @astrojs/rss

astro-compress(@playform/compress)

https://github.com/PlayForm/Compress#readme
ファイル圧縮。

pnpx astro add @playform/compress

@astrojs/sitemap

https://docs.astro.build/en/guides/integrations-guide/sitemap/
サイトマップを自動的に生成。

pnpm astro add sitemap

@astrojs/partytown

https://docs.astro.build/en/guides/integrations-guide/partytown/
Google AnalyticsGAの最適化。

pnpm astro add partytown

astro-seo

https://github.com/jonasmerlin/astro-seo#readme
SEO対策。

pnpm install astro-seo
鮪缶の鮪缶の

その他インストール

Sass

必須その1。

pnpm install -D  sass

Vite

必須その2。

pnpm install -D  vite

astro-google-fonts-optimizer

https://github.com/sebholstein/astro-google-fonts-optimizer
Google Fontsの最適化処理。

pnpm install astro-google-fonts-optimizer

Sharp

pnpmを使用しているため必要。

pnpm install -D sharp

remark-heading-id

Markdownの見出しIDを設定。

pnpm install -D remark-heading-id
鮪缶の鮪缶の

/src内にディレクトリを作成

Astroコンポーネントや各種データを用途ごとに格納できるようにする。
GitHubには空のディレクトリを反映させられない。雛形として完成させる時点で空の場合はダミーデータを配置しておくか。

src直下

  • components:レイアウト以外のAstroコンポーネント
  • assets:画像やスタイルシート
  • content:コンテンツコレクション
  • data:動的に読み込む用のJSONファイル

components直下

現場のプロから学ぶ CSSコーディングバイブルのCSS設計に馴染みがあるため、基本的にAstroコンポーネントもこれを参考にして管理する。

  • layouts:レイアウト用Astroコンポーネント
  • elements:最小単位のパーツコンポーネント
  • components:複数要素で構成されるパーツコンポーネント

assets直下

  • img:画像ファイル
  • styles:scssファイル

現時点での/src内ディレクトリ構成

/src
├─ assets
│  ├─ images
│  └─ styles
├─ components
│  ├─ components
│  ├─ elements
│  └─ layouts
├─ content
├─ data
└─ pages
   ├─ posts
   └─ index.astro
鮪缶の鮪缶の

設定ファイル

追加作成

ビルド時に作成されない設定ファイルを追加する。

  • rss.xml.ts:src/pages/に作成。RSSフィードの設定
  • config.ts:src/content/に作成。コンテンツコレクションの設定
  • robots.txt:src/pages/に作成。robots.txtを動的に作成する

設定を記述

ts.config

  • src内で頻繁に使用するディレクトリのパスエイリアスを設定
  • scripts:{build}:エラー時はビルドしないように設定
tsconfig.json
{
    "extends": "astro/tsconfigs/strict",
+         "scripts": {
+           "build": "astro check && astro build"
+     },
    // パスエイリアス
    "compilerOptions": {
        "baseUrl": ".",
+         "paths": {
+            "@src/*": ["src/*"],
+             "@components/*": ["src/components/components/*"],
+             "@layouts/*": ["src/components/layouts/*"],
+             "@elements/*": ["src/components/elements/*"],
+             "@styles/*": ["src/assets/styles/*"],
+             "@images/*": ["src/assets/images/*"],
+             "@data/*": ["src/data/*"]
        }
    }
}

astro.config

  • デフォルト値を使用しない場合箇所のみ記述する
  • 公式ドキュメントを確認したところひとまず必須なのは最終的にデプロイされるURLを指定するsite:か
  • prefetchはスマートフォンユーザーへの配慮や閲覧環境による格差を避ける観点から、デフォルトのfalseを使用
  • インテグレーション関係の記述は自動セットアップを利用したので自動で追加されている
  • グローバルCSSの設定は後述
astro.config.mjs
// インテグレーションのインポート時に自動で追加
import mdx from '@astrojs/mdx';
import partytown from '@astrojs/partytown';
import sitemap from '@astrojs/sitemap';
import { defineConfig } from 'astro/config';
import remarkHeadingId from 'remark-heading-id';

// https://astro.build/config
export default defineConfig({
    //  最終的な出力URLを記述
+    site: 'https://example.jp',
    // インテグレーションのインポート時に自動で追加
    integrations: [
        sitemap(),
        mdx(),
        partytown({
            // Adds dataLayer.push as a forwarding-event.
            config: {
                forward: ['dataLayer.push'],
            },
        }),
    ],
    vite: {
    },
    // JSやCSSなどのアセットが出力されるディレクトリを指定
+    build: {
+        assets: '_assets',
+    },
    // インテグレーションのインポート時に自動で追加
    markdown: {
        remarkPlugins: [remarkHeadingId],
    },
});

rss.xml.ts、config.ts

制作物に応じて記述内容が変わるので今回は手を加えない。
記述する際は公式ドキュメントを参照。

robots.txt.ts

公式ドキュメントを参照して記述。
astro.config.mjsのsiteから値を取得する。

robots.txt.ts
import type { APIRoute } from 'astro';

const getRobotsTxt = (sitemapURL: URL) => `
User-agent: *
Allow: /

Sitemap: ${sitemapURL.href}
`;

export const GET: APIRoute = ({ site }) => {
    const sitemapURL = new URL('sitemap-index.xml', site);
    return new Response(getRobotsTxt(sitemapURL));
};
鮪缶の鮪缶の

汎用的なastroコンポーネント

ファイルを作成

  • 404.astro:404エラーページ
  • レイアウトコンポーネント:src/components/layoutsに格納
    • head.astro:<head>内の要素を記述
    • header.astro:サイトのヘッダー部分
    • footer.astro:サイトのフッター部分
    • base-layout.astro:上記の3つを読み込みコンテンツを挿入する<slot />を置いた全体の基本レイアウト

汎用的な記述

base-layout.astroに記述

一部の<head>は公式ドキュメントに準じてこちらに記述している。

base-layout.astro
---
// head.astroをインポート
import Head from '@layouts/head.astro';

// header.astroをインポート
import Header from '@layouts/header.astro';

// footer.astroをインポート
import Footer from '@layouts/footer.astro';
---
<html lang="ja">
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />

        <!-- head.astroを挿入 -->
        <Head />
    </head>

    <body>
        <!-- header.astroを挿入 -->
        <Header />

        <!-- ここに子要素=コンテンツが挿入される -->
        <slot />

        <!--  footer.astroを挿入 -->
        <Footer />
    </body>
</html>

head.astroに記述

インテグレーションの設定が関係するものは別途後述。

base-layout.astro
<!-- キーワード。基本は4つまで -->
<meta name="keywords" content="キーワードを記述" />

<!-- 電話番号自動認識機能をオフ -->
<meta name="format-detection" content="telephone=no" />
<meta name="format-detection" content="email=no" />

<!-- ファビコン設定 -->
<link rel="”icon”" href="“/favicon.ico”" />
鮪缶の鮪缶の

汎用的なCSS

CSSは制作物に応じて独自の記述をしていく。
汎用的な記述はあらかじめ雛形に組み込み使い回せるようにしておく。
CSSのスコープがAstroコンポーネントの長所の一つ。一方で、全ページに共通する仕様はグローバルに設定しておくことで統一の漏れを回避できる。共通化とパーツ化の使い分けは意識していく。

作成するSCSSファイル

  • reset.scss:リセットCSS。thenewcssresetを採用
  • global.scss:全ページの共通設定
  • variable.scss:全ページの共通変数を定義
  • mixin.scss:全ページの共通mixin(デフォルトの記述はなし)

astro.configの設定

preprocessorOptionsに上記のファイルを設定することでSCSSがグローバルに適用され、.astroファイルに自動的に読み込まれる。
「.astroファイルに」なので.scssファイル同士では読み込みが発生しない。scss内で他のscssからmixinや変数を使用したい場合は@useで読み込みをする。(SCSSの記法としては通常通りの挙動)

astro.config.mjs
export default defineConfig({
vite: {
+       css: {
+           preprocessorOptions: {
+               scss: {
+                   additionalData: `
+					@use "@styles/reset.scss" as *;
+					@use "@styles/global.scss" as *;
+					@use "@styles/variable.scss" as *;
+					@use "@styles/mixin.scss" as *;
+					`,
                },
            },
        },
    },
鮪缶の鮪缶の

astro-seo

設定方法

<head>内に<seo />を設置。この要素の属性値にSEOに関する設定を記述していく。
各属性と値に関してはインテグレーションのGitHubページで詳細に解説されている。
https://github.com/jonasmerlin/astro-seo

最終的なコード

head.astro
---
import { SEO } from 'astro-seo';

// プロパティの型定義
export interface Props {
    title?: string;
    description?: string;
    noindex?: boolean;
    toppage?: boolean;
}

const props = Astro.props;

// 変数の定義
// title用
const BASE_TITLE = 'サイト名';
const title = props.title ? `${props.title} | ${BASE_TITLE}` : BASE_TITLE;

// description用
const BASE_DESCRIPTION = 'サイト全体の説明文';
const description = props.description ?? BASE_DESCRIPTION;

// noindex/nofollowの設定用
const noindex = props.noindex ?? false;

// openGraph.basic.typeの設定用
const toppage = props.toppage ? 'website' : 'article';
---

<SEO
    title={title}
    description={description}
    noindex={noindex}
    nofollow={noindex}
    openGraph={{
        basic: {
            title,
            type: toppage,
            image: '/opengraph.png',
        },
    }}
    twitter={{ creator: '@username' }}
    extend={{
        meta: [{
            name: 'twitter:card', content: 'summary_large_image' }],
    }}
/>

親コンポーネントから受け取る値

各ページごとに異なる値を設定するのが望ましいもの。
親コンポーネントで読み込む際に適切な値を渡す。

  • title:個別のページタイトル。渡されない場合はサイト名を使用
  • description:個別の説明文。渡されない場合はサイト全体の説明文を使用
  • noindex:検索対象外にするときのみtureを渡す。例外時のみ記述で標準では省略
  • toppage:サイトのトップページの場合のみtrueを渡す。それ以外のページでは記述しない

親コンポーネントで使用する際の記述方法

上記の値を受け取るため、子コンポーネント使用時は次のように記述する。
すべての値を受け取る場合の例

base-layout.astro
---
import Head from '@layouts/head.astro';
const { title, description, noindex, toppage } = Astro.props;
---
 <Head
    title={title}
    description={description}
    noindex={noindex}
    toppage={toppage} />

さらにbase-layout.astroを読み込む親コンポーネント(各ページ用のastroファイル)での記述。

pagetitle.astro
---
import Layout from '@layouts/base-layout.astro';
const { title, description, noindex, toppage } = Astro.props;
---
<Layout
    title="個別ページタイトル"
    description="個別説明文"
    noindex="true or 記述しない"
    toppage="true or 記述しない">

記述しない属性値

設定可能な属性値のうち今回は記述を見送ったもの。

  • SEO用途以外にも必要な内容:seo要素から切り離し独立して記述する方針。

    • charset="UTF-8"
    • ファビコン設定
  • デフォルト値を使用するので記述しないもの

    • canonical…正規URL。デフォルトは/Astro.url.href
    • openGraph.basic.url…ページのURL。デフォルトは/Astro.request.url.href
  • その他

    • languageAlternates……代替言語

保留

  • SEO上必須か確認してから記述を決める

  • openGraph.optiona

    • description
    • siteName
  • SNS

    • Facebookの設定は未対応か?
    • Xはtwitter.で書いていくのかextend.metaで書いていくのか確認

SEOとは関係ないが躓いたところ

openGraph={{}}内、つまりオブジェクトリテラル内で変数の値を参照しようとした際。{変数名}としてしまっており意図しない読み込みがされてしまっていた。
オブジェクトリテラル内で変数を使用するときに中括弧 {} は不要。

鮪缶の鮪缶の

@astrojs/sitemap

設定方法

デプロイ先のURLを設定しておく。(既に記述済み)
sitemap()関数にoption項目を記述していくことで更に高度な設定が可能。(今回は行わない)

astro.config.mjs
export default defineConfig({
    //  最終的な出力URLを記述
    site: 'https://example.jp',
    // インテグレーションのインポート時に自動で追加
    integrations: [
       sitemap(
        // オプションを記述
        ),
   ],

クローラー対策

  • <head> 内
  • robots.txt ファイル
    にサイトマップへのリンクを記述しておくことで、クローラーがサイトマップを見つけやすくなる。
    (ts:robots.txt.tsは既に記述済み)
head.astro
---
---
+ <link rel="sitemap" href="/sitemap-index.xml" />
robots.txt.ts
import type { APIRoute } from 'astro';

const getRobotsTxt = (sitemapURL: URL) => `
User-agent: *
Allow: /

Sitemap: ${sitemapURL.href}
`;

export const GET: APIRoute = ({ site }) => {
  const sitemapURL = new URL('sitemap-index.xml', site);
  return new Response(getRobotsTxt(sitemapURL));
};

生成されるファイル

サイト構築時、下記のファイルが出力ディレクトリに生成される。

  • sitemap-0.xml:サイト上のページがリストアップされる。規模が大きいサイトの場合は-1、-2と追加の枝番ファイルが生成される
  • sitemap-index.xml:上記の枝番ファイル全てへのリンクが記述された統合ファイル
鮪缶の鮪缶の

astro-google-fonts-optimizer

<head>内に下記の要素を記述する。
url属性の値用にGoogleFontからスタイルシートのURLを取得してくる。

head.astro
<GoogleFontsOptimizer
    url=""
/>
鮪缶の鮪缶の

@astrojs/partytown

<head>内にGoogle Analyticsトラッキングコードを埋込む。
実際にGoogle Analyticsから取得し「GA_MEASUREMENT_ID」が自身のトラッキングIDになっているものを使用すること。

head.astro
<script type="text/partytown" src="https://www.googletagmanager.com/gtag/js?id=GA_MEASUREMENT_ID"
></script>
<script type="text/partytown">
    window.dataLayer = window.dataLayer || [];
    function gtag() {
        dataLayer.push(arguments);
    }
    gtag('js', new Date());
    gtag('config', 'GA_MEASUREMENT_ID');
</script>
鮪缶の鮪缶の

最終的なディレクトリ構造

/astro
├─ public
│  ├─ assets
│  │  └─ images
│  └─ favicon.svg
├─ src
│  ├─ assets
│  │  ├─ images
│  │  └─ styles
│  │     ├─ global.scss
│  │     ├─ mixin.scss
│  │     ├─ reset.scss
│  │     └─ variable.scss
│  ├─ components
│  │  ├─ components
│  │  ├─ elements
│  │  └─ layouts
│  │     ├─ base-layout.astro
│  │     ├─ footer.astro
│  │     ├─ head.astro
│  │     └─ header.astro
│  ├─ content
│  │  └─ cinfig.ts
│  ├─ data
│  └─ pages
│     ├─ posts
│     ├─ 404.astro
│     ├─ index.astro
│     ├─ robots.txt.ts
│     └─ ree.xml.ts
├─ .gitignore
├─ .prettierignore
├─ .prettierrc.cjs
├─ .stylelintignore
├─ .stylelintrc.cjs
├─ astro.config.mjs
├─ biome.json
├─ package.json
├─ package.lock.json
├─ README.md
└─ tsconfig.json
鮪缶の鮪缶の

終わりに

ここで現時点での基本環境の構築は終了。
公式ドキュメントを読めばおおよその把握はできるものの、実践的なテクニックはZennの投稿や技術ブログに大いに学ばせていただいた。本当にありがとうございます。
SEOやサイトマップ対策は動的な生成を導入することで運用コストの軽減になると思う。
今後も実際の制作を重ね更なる知識を身に着ける中で環境のアップデートを行っていきたい。その時はまた備忘も兼ねスクラップにまとめていく。
今回の作業中にも4→5のメジャーアップデートを含め数回のアップデートが行われていた。Astroのこれからの展開にも期待している。

このスクラップは14日前にクローズされました