MPA (複数の HTML ファイルを軸として構成する Web アプリケーション) 開発用フレームワークとして人気がある Astro 。その勉強のため、Bootstrap の公式サンプルを題材にしてみました。
プロジェクトを作成-> % npm create astro@latest -- --template framework-react
astro Launch sequence initiated.
dir Where should we create your new project?
./pricing-page
◼ tmpl Using framework-react as project template
ts Do you plan to write TypeScript?
Yes
use How strict should TypeScript be?
Strict
deps Install dependencies?
Yes
git Initialize a new git repository?
Yes
✔ Project initialized!
■ Template copied
■ TypeScript customized
■ Dependencies installed
■ Git initialized
next Liftoff confirmed. Explore your project!
Enter your project directory using cd ./pricing-page
Run npm run dev to start the dev server. CTRL+C to stop.
Add frameworks like react or tailwind using astro add.
Stuck? Join us at https://astro.build/chat
╭─────╮ Houston:
│ ◠ ◡ ◠ Good luck out there, astronaut! ?
╰─────╯
Astro 公式のテンプレ (npm create astro@latest
) を利用して、プロジェクトをサクッと作成します。細かなコンポーネント作成は、Reactの力を借りることを想定しています。
Bootstrap を読み込むには、<head>
タグ内に<link href="(CDNのURL)/bootstrap.min.css" rel="stylesheet">
と入れる手の他に、npm install bootstrap
としてからCSSを読み込む方法があります。
例えば、index.astro
を次のように記述した場合、「import
」部分で Bootstrap を読み込み、「---
」以下に記述した HTML 部分でそれを取り込むことになります。
---
import "bootstrap/dist/css/bootstrap.min.css";
---
<html lang="ja" data-bs-theme="dark">
<head>
<meta charset="utf-8">
<title>Pricing example</title>
</head>
<body>
<div class="container py-3">
<header>
<div class="p-3 pb-md-4 mx-auto text-center">
<h1 class="display-4 fw-normal text-body-emphasis">Pricing</h1>
</div>
</header>
</div>
</body>
</html>
コンポーネントでパーツを分割する
前述したように、Astro は MPA なので、1ページにベタッと情報を書いても普通に表示されます。
// src/pages/index.astro
---
import "bootstrap/dist/css/bootstrap.min.css";
---
<html lang="ja" data-bs-theme="dark">
<head>
<meta charset="utf-8">
<title>Pricing example</title>
</head>
<body>
<div class="container py-3">
<header>
<!-- ここをまとめたい -->
<div class="p-3 pb-md-4 mx-auto text-center">
<h1 class="display-4 fw-normal text-body-emphasis">Pricing</h1>
<p class="fs-5 text-body-secondary">Quickly build an effective pricing table for your potential customers with this Bootstrap example. It’s built with default Bootstrap components and utilities with little customization.</p>
</div>
</header>
</div>
</body>
</html>
しかし、せっかくならコンポーネントを活用するべき……ということで、src/components
ディレクトリにPricingHeader.astro
を作成し、パーツを切り出してそちらに置いておくことにします。
// src/components/PricingHeader.astro
---
---
<header>
<div class="p-3 pb-md-4 mx-auto text-center">
<h1 class="display-4 fw-normal text-body-emphasis">Pricing</h1>
<p class="fs-5 text-body-secondary">
Quickly build an effective pricing table for your potential customers with
this Bootstrap example. It’s built with default Bootstrap components and
utilities with little customization.
</p>
</div>
</header>
// src/pages/index.astro
---
import "bootstrap/dist/css/bootstrap.min.css";
import PricingHeader from "../components/PricingHeader.astro";
---
<html lang="ja" data-bs-theme="dark">
<head>
<meta charset="utf-8">
<title>Pricing example</title>
</head>
<body>
<div class="container py-3">
<PricingHeader />
</div>
</body>
</html>
また、変数を使用してコンポーネントに値を渡せますので、次のように記述を変更できます。React (などが使う JSX) とは、ところどころ文法が違いますので、混同しないように注意して下さい。
// src/components/PricingHeader.astro
---
interface Props {
title: string;
message: string;
}
const { title, message } = Astro.props;
---
<header>
<div class="p-3 pb-md-4 mx-auto text-center">
<h1 class="display-4 fw-normal text-body-emphasis">{title}</h1>
<p class="fs-5 text-body-secondary">{message}</p>
</div>
</header>
レイアウトでテンプレート部分を共通化する
pages
ディレクトリ以下に置く.astro
ファイルは、今まで見てきたように<html>
から始まるのが基本です。しかしそれだと、pages
ディレクトリ以下に複数の.astro
ファイルを置くと、Webサイト全体で共通になるような要素 (<head>
タグ内の記述など) を愚直にコピペすることになってしまいます。
そこで、src/layouts
ディレクトリにDarkLayout.astro
を作成し、共通部分を切り出してそちらに置いておくことにします。
// src/layouts/DarkLayout.astro
---
import "bootstrap/dist/css/bootstrap.min.css";
interface Props {
title: string;
}
const { title } = Astro.props;
---
<html lang="ja" data-bs-theme="dark">
<head>
<meta charset="utf-8">
<title>{title}</title>
</head>
<body>
<slot />
</body>
</html>
すると、Layout 部分を他ファイルから参照できますので、index.astro
が次のようにコンパクトになります。
// src/pages/index.astro
---
import PricingHeader from "../components/PricingHeader.astro";
import DarkLayout from "../layouts/DarkLayout.astro";
const pageTitle = 'Pricing example';
const headerTitle = 'Pricing';
const headerMessage = `Quickly build an effective pricing table for your \
potential customers with this Bootstrap example. It’s built with default \
Bootstrap components and utilities with little customization.`;
---
<DarkLayout title={pageTitle}>
<div class="container py-3">
<PricingHeader title={headerTitle} message={headerMessage} />
</div>
</DarkLayout>
変数を利用してコンポーネントを一括で作成する
「コンポーネントでパーツを分割する」段落の応用編です。.astro
ファイルを使う場合はこんな感じ。
// src/components/PricingCard.astro
---
interface Props {
title: string;
price: number;
features: string[];
button: string;
}
const { title, price, features, button }: Props = Astro.props;
---
<div class="col">
<div class="card mb-4 rounded-3 shadow-sm">
<div class="card-header py-3">
<h4 class="my-0 fw-normal">{title}</h4>
</div>
<div class="card-body">
<h1 class="card-title pricing-card-title">
${price}<small class="text-body-secondary fw-light">/mo</small>
</h1>
<ul class="list-unstyled mt-3 mb-4">
{
features.map((feature) => (
<li>{feature}</li>
))
}
</ul>
<button type="button" class="w-100 btn btn-lg btn-outline-primary"
>{button}</button
>
</div>
</div>
</div>
// src/pages/index.astro
---
import PricingCard from "../components/PricingCard.astro";
import PricingHeader from "../components/PricingHeader.astro";
import DarkLayout from "../layouts/DarkLayout.astro";
const pageTitle = 'Pricing example';
const headerTitle = 'Pricing';
const headerMessage = `Quickly build an effective pricing table for your \
potential customers with this Bootstrap example. It’s built with default \
Bootstrap components and utilities with little customization.`;
const cards = [
// 中略
];
---
<DarkLayout title={pageTitle}>
<div class="container py-3">
<PricingHeader title={headerTitle} message={headerMessage} />
<main>
<div class="row row-cols-1 row-cols-md-3 mb-3 text-center">
{
cards.map((card) => (
<PricingCard {...card} />
))
}
</div>
</main>
</div>
</DarkLayout>
一方、React を使って.tsx
で書く場合は、次のようになります。書き方はだいぶ似ていますが、class
指定の書き方など、ところどころ異なることに気をつけましょう。
// src/components/PricingCard.tsx
interface Props {
title: string;
price: number;
features: string[];
button: string;
outlined: boolean;
}
const PricingCard = ({ title, price, features, button, outlined }: Props) => (
<div className="col">
<div className="card mb-4 rounded-3 shadow-sm">
<div className="card-header py-3">
<h4 className="my-0 fw-normal">{title}</h4>
</div>
<div className="card-body">
<h1 className="card-title pricing-card-title">
${price}<small className="text-body-secondary fw-light">/mo</small>
</h1>
<ul className="list-unstyled mt-3 mb-4">
{
features.map((feature, index) => (
<li key={index}>{feature}</li>
))
}
</ul>
<button type="button" className={
`w-100 btn btn-lg ${
outlined ? 'btn-outline-primary' : 'btn-primary'
}`
}
>{button}</button
>
</div>
</div>
</div>
);
export default PricingCard;
// src/pages/index.astro
---
import PricingCard from "../components/PricingCard2.tsx";
import PricingHeader from "../components/PricingHeader.astro";
import DarkLayout from "../layouts/DarkLayout.astro";
const pageTitle = 'Pricing example';
const headerTitle = 'Pricing';
const headerMessage = `Quickly build an effective pricing table for your \
potential customers with this Bootstrap example. It’s built with default \
Bootstrap components and utilities with little customization.`;
const cards = [
// 中略
];
---
<DarkLayout title={pageTitle}>
<div class="container py-3">
<PricingHeader title={headerTitle} message={headerMessage} />
<main>
<div class="row row-cols-1 row-cols-md-3 mb-3 text-center">
{
cards.map((card) => (
<PricingCard {...card} />
))
}
</div>
</main>
</div>
</DarkLayout>
まとめ
Bootstrap の公式サンプルを題材にして、Astro が持つ便利機能 (コンポーネント、レイアウト、変数など) を使ってみました。上記の説明だけだと「React 要らないのでは?」となってしまいますが、static なページ作りが得意な Astro の中に、dynamic なコンポーネント作りが得意な React とをシームレスに連携できる……というのが重要だったりします。
そのため次回は、React 要素を使って、Astro で作られた Web サイト内の一部分を動的に書き換える例を紹介してみます。
- 閲覧数 294
コメントを追加