blog.keisatoh.net

Astroでブログをつくった

たのしい\(^o^)/

昨年末にコロナウイルスに感染し、名古屋のホテルで療養していた。体調が良くなってきたころ、ホテルの小さな机の上でAstroを触り始め、楽しくなってブログを作った。

ソースコード:https://github.com/keisatoh/blog.keisatoh.net

もともとはてなブログやWordPressでブログを書いていたけど、適当に放っておいたらproライセンスが切れたり、ドメインが失行していたりして、なにかを書く場所を失っていた。自作ブログはもともと憧れがある。僕がSSGという単語を知る前から、lifehacking.jpの「ブログをWordPressからHugoに移行しました」を読んで、静的で軽く、Markdownで執筆できるスタイルに魅力を感じていた。

思えばそこが今回のモチベーションの遠い起点で、次点で愛読しているr7kamuraさんのブログだと思う。当時の自分には難しかったが、いつの間にかだいたい理解できるようになっていて感慨深い。今回は得意なJavaScriptを使おう。

使用技術

Astro

フレームワークはAstroを採用した。使ってみたかった。

Astroは、コンテンツにフォーカスした高速なWebサイトを構築するためのオールインワンWebフレームワークです。 はじめに 🚀 Astroドキュメント

Astroは2021年に登場したフレームワークで、ビルド時に可能な限りJavaScriptを排除した、高速なSSGに強みがあるとされている。また、ReactやVue、Svelteといった他のフレームワークを同じページで混在できたり、先日発表されたばかりの最新バージョンではページまたはエンドポイントごとにSSGとSSRを混在させられたりと、びっくりがたくさん。

コンテンツ重視をうたっているだけあって、ビルトインでMarkdownまわりのあれこれやページネーションがサポートされていて、ブログにはもってこいである。実際に開発体験はすごくよくて、たのしかった。

たとえば記事一覧ページ(pages/post/下のmdファイルを一覧で表示する)を実現するのに、以下のAstroコンポーネントのみで済む:

---
const posts = await Astro.glob('../pages/post/*.md');
---

<div>
{posts.map((post) => (
  <article>
    <h1>{post.frontmatter.title}</h1>
    <a href={post.url}>Read more</a>
  </article>
))}
</div>

Astroコンポーネントは、---に囲まれたコンポーネントスクリプトと、その下のコンポーネントテンプレートによって構成される。スクリプト部はTypeScriptが書けるし、テンプレート部はJSXのような記法が使える(プレーンなHTMLを書くこともできる)。 特有のくせがなく、それでいて欲しいメソッドは充実していて(ここでいうAstro.glob)、ドキュメントを読めばだいたい全部わかるのがうれしい。

Tailwind CSS

CSSはTailwind CSSで書いた。最近は、学校の課題や個人でなにかを作るとき、ほぼ毎度Tailwindを使用している。デザインを試行錯誤しながらスタイリングするときにも、ストレスが少ないのがいい(今回デザインカンプは作っていない)。

記事本文のスタイリングには@tailwindcss/typographyを採用した。

Markdownファイルのフロントマターにはlayoutプロパティがあり、Layoutコンポーネントが指定できる。そして、指定したLayoutコンポーネントの<slot />の箇所にレンダリングされる。

<article class="prose">
  <slot />
</article>

@tailwindcss/typographyを使うと、proseクラスを振るだけで、読みやすい文章になるようよしなにスタイリングしてくれる。カスタマイズすることもできるけど、現状はほぼデフォルトのまま使用している。

Cloudflare Pages + Imgur

デプロイ先にはCloudflare Pagesを選んだ。昨年、NuxtでSSGしたサイトをデプロイした時にパフォーマンスが良かったのと、当時比較検討した結果、無料プランでできることの幅がもっとも広かったため。たとえば、商用利用ができる。無料(Hobby)アカウントのポリシーによって「非営利の個人使用のみ」と定められているVercelと比べて、Amazonなどのアフィリエイトリンクを貼りやすいと思う。また、このサイトのドメインがCloudflareで取得・管理されているので一元化できる利点もある。

画像はリポジトリ内ではなく外部に置きたかったので、Imgurへアップロードすることにした。アップロードすると同時にEXIFを消してくれたり、WebP画像も生成してくれるので、手軽に運用できそう。アップロード先の選択肢にはGyazoも上がったが、前述の機能が無料で使えるImgurに軍配が上がった。

(ちょっとだけ)工夫したところ

ちょっとだけ工夫した点として、dekプロパティの存在がある。

まず、The Vergeにみられるような、タイトル下の短い文章をつけたいと思った。これはdekと呼ばれるらしい。たしかに、WIREDやCNETのソースを覗くと、それらしき名前のクラスが振られている。

というわけで、mdファイルのフロントマターにdekプロパティを用意して、それを記事ページと記事一覧ページ(トップページ)のタイトル下に表示した。日記を書くときなんかにいちいち考えるのは面倒なので、dekがないときの一覧ページには本文冒頭から抜粋することにした。

つまり、トップページにはdekか本文冒頭が表示される。以下、抜粋するための関数:

const getExcerpt = (html: string, excerptLength: number) => {
  const removeHTMLTags = (html) => {
    return html
      .replace(/\r?\n/g, ' ')
      .replace(/<("[^"]*"|'[^']*'|[^'">])*>/g, '');
  };
  const plainText = removeHTMLTags(html);

  const excerpt = plainText.slice(0, excerptLength);
  // 抜粋する文字数より本文が長い場合、末尾に3点リーダーをつける
  return excerptLength < plainText.length ? excerpt + '…' : excerpt;
};

この関数によって返される本文冒頭は、前述のユースケースだけでなく、descriptionプロパティ未入力時のmetaタグの中にも利用している。

dekとの論理演算を除けば、はてなブログやnoteなどの多くのブログサービスにみられるポピュラーな実装だと思う。さきほどの関数は、まさにはてなブログっぽさを意識している。

さいごに

気に入ったのでしばらくの間Astroを触っていくし、Astroの話をよくすると思います。

← トップに戻る