はじめてのAstro
Astroを触ってみたのでメモ
実施環境
項目 | 詳細 |
---|---|
PC | MacBook Pro(14 インチ、2021)Apple M1 Pro |
OS | MacOS Ventura 13.6 |
Node.js | v16.14.2 |
Astroとは
What is Astro?
Astro is an all-in-one web framework for building fast, content-focused websites.
Key Features
- Component Islands: A new web architecture for building faster websites.
- Server-first API design: Move expensive hydration off of your users’ devices.
- Zero JS, by default: No JavaScript runtime overhead to slow you down.
- Edge-ready: Deploy anywhere, even a global edge runtime like Deno or Cloudflare.
- Customizable: Tailwind, MDX, and 100+ other integrations to choose from.
- UI-agnostic: Supports React, Preact, Svelte, Vue, Solid, Lit and more.
なんかいろいろ早そうなフロントエンドフレームワークという印象。
↑Astroについて更に詳しい解説をもとに意訳
- 柔軟性や多機能を犠牲にして?、速度とかシンプルさを優先しているっぽい。コンテンツに集中できるフレームワークとのこと(具体的にどう違うんだろ)。
- SPAじゃなくMPA
- デフォルトで Zero JS らしく、早い
- React, Preact, Svelte, Vue, Solid, Lit, and several othersがサポートされている
Astro Islands
Astro Islandsは、まずなるべくjsを削ぎ落とす(デフォルトではjsをゼロにする)。そしてページの中にjsが必要な部分があったら、その部分だけ独立した島(Islands)のように扱ってレンダリングする。そうすることで島以外はただのHTMLなので高速だし、島と島は並列にロードできるし、タイミング等も指定できる。とのこと。
パス
- src/pages配下の
.astro
ファイル、.md
ファイルがそのままURLになる -
[パス名].astro
で動的なパス生成-
getStaticPaths
のreturnでパス名を返す
-
---
import BaseLayout from '../../layouts/BaseLayout.astro';
export async function getStaticPaths() {
return [
{ params: { tag: "astro" } },
{ params: { tag: "successes" } },
{ params: { tag: "community" } },
...
];
}
const { tag } = Astro.params;
---
<BaseLayout pageTitle={tag}>
<p>Posts tagged with {tag}</p>
</BaseLayout>
.astroファイルの書き方基礎
---
// frontmatter領域
import '../styles/global.css';
import Navigation from '../components/Navigation.astro';
const pageTitle = "About Me";
const titleColor = "navy";
const fontWeight = "bold";
---
<html lang="en">
<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>{pageTitle}</title>
<style define:vars={{ titleColor, fontWeight }}>
h1 {
color: purple;
font-size: 4rem;
color: var(--titleColor);
font-weight: var(--fontWeight);
}
</style>
</head>
<body>
<Navigation />
<h1>{pageTitle}</h1>
<script>
document.querySelector(".hamburger").addEventListener("click", () => {
document.querySelector(".nav-links").classList.toggle("expanded");
});
</script>
</body>
</html>
- 上下3点ハイフン
---
で区切られたfrontmatter領域と、HTMLの領域に分かれる- frontmatter領域では変数が定義でき、
{変数}
でHTML領域で使える→svelteが近い? -
{}
内は式がかける{finished && <p>I finished this tutorial!</p>}
- frontmatter領域では変数が定義でき、
- CSSはstyleタグ内に書く
-
<style define:vars={{ titleColor, fontWeight }}>
のようにすると、style内で変数が使えるcolor: var(--titleColor);
- グローバルCSSはimportして適用
import '../styles/global.css';
-
- インタラクションを加えるためにフロントに送るjsは
<style>
タグ内に記載する
---
const { platform, username } = Astro.props;
---
<a href={`https://www.${platform}.com/${username}`}>{platform}</a>
- コンポーネントはimportして、
<コンポーネント名 />
で使える - コンポーネントでは
const { platform, username } = Astro.props;
の形でPropsを定義できる - 渡す時は
<Social platform="twitter" username="astrodotbuild" />
---
import BaseLayout from '../layouts/BaseLayout.astro'
const allPosts = await Astro.glob('../pages/posts/*.md');
const pageTitle = "My Astro Learning Blog";
---
<BaseLayout pageTitle={pageTitle}>
<p>This is where I will post about my journey learning Astro.</p>
<ul>
{allPosts.map((post) => <li><a href={post.url}>{post.frontmatter.title}</a></li>)}
</ul>
</BaseLayout>
-
const allPosts = await Astro.glob('../pages/posts/*.md');
で'../pages/posts/*.md'
の情報が配列で受け取れる
.mdファイルの書き方基礎
---
const { frontmatter } = Astro.props;
---
<h1>{frontmatter.title}</h1>
<p>Written by {frontmatter.author}</p>
<slot />
---
layout: ../../layouts/MarkdownPostLayout.astro
title: My Second Blog Post
author: Astro Learner
description: "After learning some Astro, I couldn't stop!"
image:
url: "https://docs.astro.build/assets/arc.webp"
alt: "Thumbnail of Astro arcs."
pubDate: 2022-07-08
tags: ["astro", "blogging", "learning in public", "successes"]
---
After a successful first week learning Astro, I decided to try some more. I wrote and imported a small component from memory!
- layoutは
.astro
ファイルに記述でき、const { frontmatter } = Astro.props;
でfrontmatter情報にアクセスできる- それを
.md
ファイルでlayout: ../../layouts/MarkdownPostLayout.astro
とすると適用できる
- それを
islands
---
import BaseLayout from '../layouts/BaseLayout.astro';
import Greeting from '../components/Greeting';
const pageTitle = "Home Page";
---
<BaseLayout pageTitle={pageTitle}>
<h2>My awesome blog subtitle</h2>
<Greeting messages={["Hi", "Hello", "Howdy", "Hey there"]} /> // クライアント側で動かないコード
<Greeting client:load messages={["Hej", "Hallo", "Hola", "Habari"]} /> // こっちは動く
</BaseLayout>
クライアント側でインタラクションするためには、client:load
等を付ける必要がある。client:visible
だと、そのコンポーネントが見えた時にjsをロードする。
ブランチがmasterだったのでmainに変更。でもデフォルトブランチの設定がbranchesからGeneralに移動しててちょっと詰まった
npmの依存関係壊れてた。。
npm ERR! code ERESOLVE
npm ERR! ERESOLVE unable to resolve dependency tree
npm ERR!
npm ERR! While resolving: @example/docs@0.0.1
npm ERR! Found: react@18.0.0
npm ERR! node_modules/react
npm ERR! react@"^18.0.0" from the root project
npm ERR!
npm ERR! Could not resolve dependency:
npm ERR! peer react@">= 16.8.0 < 18.0.0" from @docsearch/react@3.0.0
npm ERR! node_modules/@docsearch/react
npm ERR! @docsearch/react@"^3.0.0" from the root project
npm ERR!
npm ERR! Fix the upstream dependency conflict, or retry
npm ERR! this command with --force or --legacy-peer-deps
npm ERR! to accept an incorrect (and potentially broken) dependency resolution.
npm ERR!
npm ERR!
npm ERR! For a full report see:
え、yarnだといけた…動作結構違うのか…大丈夫なのか…笑
Astro.props
使ってるページでcannot find name 'Astro'
でてる…
いや、行けてるとこもあるな、なんで?HeadSEO.astro
だけいけてる
うわ、SP対応してないな…ドキュメントだからPCメインだろうってこと…?対応しないと
フォント変えたい
Source Han Code JPにしたいけどフォントいまいちよくわからない。
- フォント
- ビットマップ:ラスタ画像的な
- スケーラブル:ベクタ画像的な
- ストローク:文字を線で表現
- アウトライン:文字を輪郭線で表現、多分今はほとんどこれ
- TrueType:歴史が長く、安価
.ttf
- ttfを複数詰め込んだのが
.ttc
- OpenType:TrueType発展版、PostScriptを包括、高機能&高価
- TrueType形式:
.otf
/.ttf
- PostScript形式:
.otf
- フォントコレクション:
.otc
/.ttc
- TrueType形式:
- WOFF:「Web Open Font Format」の略。
- OpenType又はTrueType形式のフォントを圧縮
- 軽いのでWebフォントに適している
- WOFF2は改良版
- TrueType:歴史が長く、安価
厳密にはTrueType = .ttf、OpenType = .otfってわけでも無いらしいが、一旦ざっくりで。
PCとかで使うならttc系、ウェブならWOFF2がいいってことかな?WOFF2の対応状況も問題なさそう。
ttcをwoff2に変換して使う感じかな
オンラインコンバータ良さげなのがなかったので、これをダウンロードして使用。
30.2MB(.ttc)が9KB(.woff2)になった。違いすぎん…?笑
フォントが当たってない。Whatsfontでは当たってることになってるが、Whatsfont結構適当やな…たぶんCSS見に行ってるだけか。
Failed to decode downloaded font: http://localhost:4321/fonts/SourceHanCodeJP.woff2
introduction:1 OTS parsing error: invalid sfntVersion: -2147378942
これ?→変化なし
ttcからではなくotfから変換したファイルで試したらエラーは消えたが適用はされず。
よくわからんけどFira Codeのwoff2は適用されたので、SourceHanCodeJPのファイル、または変換が良くなかった…?
一旦これで進める。
yarn upgrade --latest astro
-> error Cannot read properties of undefined (reading 'postcss')
yarn upgrade --latest @astrojs/tailwind
-> error Cannot find package 'tailwindcss' imported from /node_modules/@astrojs/tailwind/dist/index.js
yarn add --dev tailwindcss
-> `yarn dev`できた!
yarn upgrade --latest
別のエラー
error Invalid URL
File:
/src/components/HeadSEO.astro:13:27
Code:
12 | const imageSrc = content?.image?.src ?? OPEN_GRAPH.image.src;
> 13 | const canonicalImageSrc = new URL(imageSrc, Astro.site);
| ^
14 | const imageAlt = content?.image?.alt ?? OPEN_GRAPH.image.alt;
15 | ---
[markdown] [/src/pages/core/colors.md] Astro now supports MDX! Support for components in ".md" (or alternative extensions like ".markdown") files using the "setup" frontmatter is no longer enabled by default. Migrate this file to MDX.
このテンプレいろいろ古すぎるな…まあAstro v1だったもんな…笑
→この辺か?
svgの色変え
マークダウン内でjsを使う
mdxを使えるようにする。
---
title: General Introduction
description: general intro
layout: ../../layouts/MainLayout.astro
---
import { SITE } from "../../config";
{SITE.portfolio} // これでimportした値が使える
## 筆者について
都内でフロントエンドエンジニアとして勤務しています。詳細は<a href={SITE.portfolio}>ポートフォリオサイト</a>を作っていますので、そちらを参照いただけると嬉しいです。
マークダウンのリンクの書き方([テキスト](リンク)
)だとうまくいかなかったが、aタグにすればいけた。
ga4入れる
chatgptできいてみる。プロパティがサイト単位、ストリームはウェブとかスマホとかそういう単位の様子。
Google Analytics 4(GA4)では、「ストリーム」は「プロパティ」の一部です。
プロパティ(Property): プロパティは、ウェブプロパティやアプリなどのデジタルプロパティ全体を指します。これは、データを収集、分析する単位となります。一般的に、1つのプロパティが1つのウェブサイトやアプリに対応します。
ストリーム(Stream): ストリームはプロパティ内の特定のデータソースを表します。例えば、ウェブプロパティ内には複数のウェブサイトがある場合、それぞれのウェブサイトに対応するストリームを作成します。これにより、異なるデータソースをプロパティ内で区別できます。
簡単に言えば、プロパティは全体のデジタルプロパティを指し、ストリームはそのプロパティ内の特定のデータソースを表します。
別にPartyTownいらんくない…?と思ったので一旦なしでやってみる。
headのすぐ下に下記追加。gtag()が引数がないのに引数入れてるエラーが出るのでgtag(...args: any[])
とした。
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=[GA4のID]"
></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(...args: any[]) {
dataLayer.push(arguments);
}
gtag("js", new Date());
gtag("config", "[GA4のID]");
</script>
dataLayerがないエラーもでたので、下記を作成。
declare global {
interface Window {
dataLayer:any;
}
}
export {};
rss feed
- 記事の表示方法
- 手動入力(link, title, pubDateが必須)
- content collections
- glob imports
イベントハンドラ
prettier
mermaid入れたい
mermaidは3つのパートで構成されている
- Deployment
- Syntax
- Configuration
mermaidを導入する方法
- Using the Mermaid Live Editor at mermaid.live. -> 多分オンラインエディタで画像作る感じなので却下
- Using mermaid plugins with programs you are familiar with. -> なさそう?Gatsbyはあるんだ…
- Calling the Mermaid JavaScript API. -> 可能性ありそう
- Deploying Mermaid as a dependency. -> 可能性ありそう
layoutでmermaidをimportして、preで使う方法はなぜか失敗した
// Layout.astro
<script type="module">
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs';
mermaid.initialize({ startOnLoad: true });
</script>
// hoge.mdx
<pre class="mermaid">
graph TD
A[Client] --> B[Load Balancer]
B --> C[Server01]
B --> D[Server02]
</pre>
astroファイル内であればうまくいったので、mdxの生成プロセスの問題で何か起こってる?mdxをHTMLに変換するところとかどのタイミングでどうやってるんやろか。そこはどうやったら調べられるんや…
innerHTMLが効かない
<script type="module">
import mermaid from './mermaid.esm.mjs';
mermaid.initialize({ startOnLoad: false });
// Example of using the render function
const drawDiagram = async function () {
element = document.querySelector('#graphDiv');
const graphDefinition = 'graph TB\na-->b';
const { svg } = await mermaid.render('graphDiv', graphDefinition);
element.innerHTML = svg;
};
await drawDiagram();
</script>
svgは生成できてそうなのに、element.innerHTMLがうまく行かない。危ないからAstroで禁止してる…?set:htmlというのはあったが、クライアントサイドで生成したものは無理そう?サーバサイドで生成すればいいのだが、mermaid.renderがdocumentを参照してる…?エラーでとまるdocument is not defined
APIの種類
- mermaid.run
- By default, mermaid.run will be called when the document is ready, rendering all elements with class="mermaid".
- mermaid.initialize({startOnLoad: false}) will prevent mermaid.run from being called automatically after load.
- クラス名変えるくらいしかできない?(そんな感じの名前ではないが…)
- mermaid.render
- graphDefinitionのテキストからsvgを生成する関数っぽい
reactのdangerouslySetInnerHTMLでいけないか?
yarn astro add react
yarn add -D @types/react
// MermaidDiagram.tsx
import mermaid from "../../../node_modules/mermaid/dist/mermaid";
import { useEffect, useState } from "react";
type MermaidDiagramProps = {
graphDefinition: string;
};
const MermaidDiagram: React.FC<MermaidDiagramProps> = ({ graphDefinition }) => {
const [svgDiagram, setSvgDiagram] = useState<string>("");
useEffect(() => {
mermaid.initialize({
startOnLoad: false,
});
mermaid.render("graphDiv", graphDefinition).then((svg) => {
setSvgDiagram(String(svg));
});
}, [graphDefinition]);
return (
<>
<div>{graphDefinition}</div>
<div>{svgDiagram}</div>
</>
);
};
export default MermaidDiagram;
// hoge.mdx
<MermaidDiagram2 client:only="react" graphDefinition="graph TB\na-->b" />
→ error document is not defined
むむむ。
useEffect + client:onlyの組み合わせでも出てしまう。。
うーん…というかmdxはたぶんサーバでビルドするから(それもどうやって確かめるのか…)、クライアントサイドでのsvg生成と相性がわるいのか…?普通の.astroファイルならいけるもんなあ…
というかmermaidがdocumentを参照するのやめてほしいな…無理か。
mdxのパースとかレンダリングを関数で明示的にやる…?それともその処理になにか別の処理を挟ませることができるのか。
if (typeof window !== "undefined") {
で分岐しても error document is not defined
出るもんな…仕組みがわからん過ぎてわからん。
お
Configure the plugin:ってどこで何するねんと思ったが、astro.config.mjsをいじる感じかな
全くうまくいってないな…そもそもconfigの書き方が違う気がする
Markdown -> HTML の変換に使われる事が多い、Remark / Rehype について
remark-mermaidjs入れたらいけたぞ…なんでや…
npm install remark-mermaidjs
npx playwright install --with-deps chromium
...
import remarkMermaid from "remark-mermaidjs";
// https://astro.build/config
export default defineConfig({
...
markdown: {
remarkPlugins: [remarkMermaid],
},
});
```mermaid
flowchart LR
Start --> Stop
\```
でもplaywrightの依存関係package.jsonに反映されてないけど本番環境でもいけるの…?
オプションってどうやって渡すんや…
まとめ
使いやすそう!特に個人開発の静的ページとかはめちゃ良さそうな印象を受けました。公式でも複雑なことするならNext.js使ってねとのことだが、サクッと作りたいときにはかなり良さそう。
- 前提知識が少ない
- ほぼ素のHTML/CSS/jsでいける
- MPAでシンプル
- UIライブラリ(ReactとかVueとか)が簡単に、部分的に使える
- テンプレが多く、サクッと構築したいとき便利
- 日本語ドキュメントがある
- ちゃんと比べたわけじゃないけど、ビルドとかいろいろ早そうな体感はあった
- jsをデフォでゼロにしているわけだし、描画も早いはず
- まだちゃんと比較したわけじゃないけど、超個人的にはGatsbyよりAstroの方がいい印象。GatsbyはプラグインまわりとかGraphQLまわりとかやっぱり少し学習コストがある気がしたが、AstroはよりWebスタンダードに近くてシンプルに素早く構築するには有利に感じた。
Next.jsの複雑化や独自化に対する揺り戻しが起きつつあるなかで、その一つの解なのかも?もっとちゃんとAstroの概念を勉強したい