node v9 の静的サイトをAstro でリプレースする
はじめに
ポート株式会社 サービス開発部 Advent Calendar 2023 13日目の記事です。
フロント開発に携わる🩷ピンク髪💅の eicho.chin です。こちらは最近実装した Astro のサイトの話を紹介できればと思います!🫣
Astro選ぶ理由
- 旬のフレームワーク、圧倒的人気
- cloudflare へのデプロイだけではなく、色んな方法でデプロイ可能
- 静的サイトであれば、SSG/SSR 選び放題
- Vite
- あらゆる面で CWVs に適応するフレームワーク🐵
今回のリプレースの目的/満たしたい希望
- node のバージョンをなるべく最新のものを使用したい。
- 将来開発のコストも削減できれば幸い。
- 既存の計測やクローリングに影響がないように実装します。
調査編🤨
sass/scss 導入使用
既存のcss を全部再利用♻️のため、利用可能かどうかの確認します。
Styles & CSS 🚀 Astro Documentation
yarn add -D sass
その他の設定は特に不要です。
src/stylesheets/application.scss
にて、既存のscssファイル は 全部一つのファイルにまとめてる endpoint。
Astro コンポーネントは全部 scope であり、利用したい コンポーネントで import "../stylesheets/application.scss";
するので問題なく、class のスタイルが適用されます。
利用案
scope で利用するのがセオリなんですが、既存のCSSを移行が目的のため、global で利用したいですが、以下の書き方でしたら、ビルド後は CSS 全部無くなります。
<!doctype html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
...some code
<style>
@import url("@/stylesheets/application.scss");
</style>
🐤🐤🐤 image 🐤🐤🐤
とても困りました。
<Image /> (astro:assets)
、CLS 最高に対応する Image 作り込みによる困る点
Astro 3 以降
Astro 3 以降 <Image /> (astro:assets)
が CLS のために、色々良しなに画像がいじられます。
- 圧縮されます。
- ファイル配信形式もわかる、
png/jpg => webp
デフォルトに変換されます。 -
densities
で画素レベルで画像のサイズをコントロールできます。
↑良しなに画像に最適化のため、png の画像がぼやくように見えます。
解決方法1 (うまくいかなかった):
---
import { Image } from 'astro:assets';
---
<Image src={imageBtnFaceBook} width="33" height="33" alt="facebookでシェアする" densities={[1.5, 2]} quality="100" format="png" />
- densities: 拡大の最小から最大を設定、デカすぎる設定されると無視されます。
- quality:
low
、mid
、max
などまたは1
~100
設定可能。この設定で画像のサイズも変わります。 - format: デフォルト webp ですが、何も変更したくない場合設定が必要です。
上記で一旦解決しそうに見えますが、実際はぼやくことは変わってません。
真・解決策:
Image component に任せます。
<Image src={imageTxtMv1} alt="イメージ alt" />
- 充実に推奨の書き方で記述をします。
- height、width も imgObject の取得される値を使用されます。
- CSSで画像サイズをコントロールします。
[slug].astro
で動的にルートされる場合、画像のpath に思うように 🦑 ない
astro v3.3.2, では <Image>
コンポーネントで動的な path は利用不可。
<Image>
の src は必ず Module Importing import imgUrl from "dir/image.png"
ファイル Object を渡す必要があります。
失敗例:
[year].astro 動的画面ごとに dir/2023/txt_2023.png
のようなパスとファイル名の画像を表示したい。
---
import { Image } from 'astro:assets';
export function getStaticPaths() {
return [
{ params: { year: '2023' } },
];
}
const { year } = Astro.params;
// 成功
import imageUrl2023 from "../images/txt_2023.png";
// 失敗
import imageUrl from `../images/txt_${year}.png`;
background: url("/path")
で 利用したい場合、public/
配下で配置するしかないぽい
CSS これ以外で利用したい場合、他の ライブラリーを使用するしかない。
例:Astro ImageTools Documentation
GA4 計測の調査
gtag.js を使用して、アプリ + ウェブ プロパティの Google アナリティクス タグをサイトに追加する | Google for Developers
CVWs のチェックの時、点数下がらないようなソリューション Partytown
Partytown解決案:
メリット:CVWs のチェックの時、google タグで点数下がることがないように
本番のみ計測にしたいため、Vite の環境判別を使用します。
---
const isProduction = import.meta.env.PROD;
---
{isProduction && <slot />}
使う箇所:
...some code
...
<ProductionOnly>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-id"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){ dataLayer.push(arguments); }
gtag('js', new Date());
gtag('config', 'UA-id');
</script>
</ProductionOnly>
実装編😆
Astroのコレクションを使う形
公式から引用:
予約されているプロジェクトディレクトリsrc/contentの中にあるトップレベルのディレクトリは、1つのコンテンツコレクションを表わします。src/contentディレクトリの中に入れられるのは、コンテンツコレクションだけです。このディレクトリは他のものには使えません。
- コレクションディレクトリ内に Markdownや MDX、YAML、JSON のようなデータフォーマットを使用できます。
- ファイル名の前にアンダースコア (_) を付けると、ビルドから除外されます。
JSON ファイルを使用したコンテンツ定義
zod で schema定義
AstroはZodを使ってコンテンツスキーマを動かしています。Zodを利用すると、Astroはコレクション内のすべてのファイルのフロントマターを検証し、プロジェクト内からコンテンツをクエリする際に自動的にTypeScriptの型を提供できます。
デフォルトで zod が組み込まれています。詳しくは:Zodによるデータ型の定義
import { z, defineCollection } from 'astro:content'
const dataCollection = defineCollection({
type: 'data',
schema: z.object({
content: z.object({
about: z.object({
title: z.string(),
lead: z.string(),
currentSituation: z.string(),
contentStatus: z.string(),
isItWork: z.string(),
})
});
npm run dev
の後、.astro/types.d.ts ファイルがビルドされます。
実際開発時に、型が流れてくるイメージです。
JSONを使用する場合
JSON の場合、type: 'data'
を設定する必要があります。詳しくはこちら:コレクションスキーマの定義
const dataCollection = defineCollection({
type: 'data', // data の指定により、JSON 定義を使用できます
schema: z.object({...}),
})
export const collections = {
data: dataCollection // データコレクションのname は data になります。
}
getStaticPaths()
、getCollection()
で画面を生成する
静的サイトビルド pages/[example].astro
ページを作成します。
ページがファイル名で動的パラメータを使用する場合、そのコンポーネントはgetStaticPaths()関数をエクスポートする必要があります。
この関数はAstroが静的サイトビルダーであるために必要です。つまり、サイト全体が事前に構築されます。
- getStaticPaths() でエントリーごと画面を生成する。
- getCollection() でエントリーデータを取得。
---
export async function getStaticPaths () {
const entries = await getCollection('data') // コレクションで定義したname でコレクションを指定します。
return entries.map(entry => ({
params: { id: entry.id }, // params は必須、path になります。
props: { entry } // オブション
}))
}
const { entry } = Astro.props
---
<!--テンプレート-->
<Component entry={entry} />
getStaticPathsで retrun された params や props は このように Astro.props
から取り出すことが可能です。
実際取得したいデータもまた子コンポーネントに渡すことが可能です。
イメージ図:
画像は ファイル名 のみ JSON ファイルに保存、使用の際は、module import して表示を行う。
-
エントリーJSON定義の際は以下のように fileNameのみになります。
{ ...some code "data": { "name": "コンテンツネーム", "contentId": "10001", "logo": "logo_10001" // ファイル名 } }
-
ファイル名を使用してダイナミックインポートで モジュールを<Image>に使用
<Image src={await import(`../images/${entryData.data.logo}.png`).then((data) => data.default)} alt="テスト内容" />
今回画像も全部pngのため上記の書き方で問題ないですが、些か優雅さは足りないのです。🥲
困った編🧐
Amplify で Astroをデプロイしたい場合、困難に遭遇
Amplify デフォルトイメージの Node バージョン最新は 18.13.0 でした。
Astro 3.2.2 は最低 18.14.1 が要求されてます。僅か 1 パッチパージョンで、失敗してしまいます。🥲
解決方法
Amazon ECR Public Gallery - Docker/library/node
Amplify で構築イメージを上記のpublic イメージから 使いたい node バージョンのイメージを指定する形で解決可能になります。
末尾スラッシュ問題
他の方も同じような問題 :
再現手順:
-
スラッシュなしのurl を踏む
https://feature-10000.d39edkp5r8k421po.amplifyappfake.com/fake -
https://<app_url>/<build_#>/<path_I_was_on>
の ように<build_#>
がpath に追加されてしまう。
暫定対応
- ビルド設定を変えます
こちらの設定で、この変更が可能になります!buildformat
ビルド設定 | 画像 |
---|---|
format: 'directory' | |
format: 'file' |
- サイト内遷移リンクを
/
なしに戻る - Amplify でリダイレクト設定
- (正規 URLを設定)
終わりに
初めて Astro を触りました。CWVs などの対応が完備してて、とても嬉しいです。
Discussion