ブログの静的生成フレームワークをGatsby.jsに移行してみた

こんにちはnasustです。

Hugoで静的生成していたブログをGatsbyに移行しました。 Gatsbyに移行した理由は以下の通りです。

  • Hugoで提供されていない機能を拡張するのは面倒

    • HugoはGo言語でコンパイルされたアプリなので動的に機能を拡張することが出来ません。APIサーバーを立ち上げる必要がありました。
  • 半年ぶりの記事更新なので、なんとなく新しい事をしたかった。
  • Gatsbyを選んだのは世界的にシェア数が多いから

Webページを久々に更新する場合、なんとなくリニューアルしたい病に掛かるですよね。今回はデザインを変更せずに静的生成アプリだけ変更しました。その事を記事として書きます。

Gatsbyとは

今回使用したGatsbyは、Reactをベースとした静的サイトジェネレーターです。 静的サイトジェネレーターとは、 コンテンツをテンプレートを用いてWebサイトを生成するヘッドレスCMSです。 先ほど挙げたHugoも、その1つです。 Markdownファイルを元にブログのようなWebサイトをも作成できます。

静的サイトジェネレーターのメリット

メリットは以下の通りです。

  • htmlや画像などのファイルだけなので、サーバーやアプリの実行環境の必要がないです。
  • コストパフォーマンスに優れていて、ハイパフォーマンスのスピードでコンテンツを配信できます。
  • 実行環境が必要ないので脆弱性を回避できます。
  • CDNでスケールしやすいです。

Gatsbyの特徴

Gatsbyの特徴であるReactは、UI構築の為のJavaScript製のフレームワークで世界的に人気があります。Single Page Applicationの開発で主に使用されています。プラグインも対応しており機能を拡張できます

GatsbyでUIを作成する時に便利なのが、 Reactで使用しているJSXというJavaScriptにhtmlを書けるような拡張構文を使用できることです。 Hugoのようなhtmlベースのテンプレートの場合で制御構文を書くと視認性が悪くなりやすいです。JSXの方が分かりやすいです。

HugoのテンプレートとJSXを比較して見ると分かると思います。

Hugoの例:

{{ if $var }}
    <p></p>
{{ else }}
    <p></p>
{{ end }}

JSXの例:

if(var){
    return <p></p>
}else{
    return <p></p>
}

個人的にはJSXの方が分かりやすいです。まあ初めてJSXを見たときは構文が気持ち悪いと思いましたが。

そして以下のようにJSXでUIコンポーネントを作成できるので再利用しやすいです。

const JsxComponent = ({ data , location , pageContext }) => {
    return <p>JSX</p>
}

export default JsxComponent

JsxComponentは以下のように使用できます。

import JsxComponent from "../jsx-component"

const IndexPage = ({ data , location , pageContext }) => {
    return <JsxComponent />
}

<p>JSX</p>とレンダリングされます。

このJSXを使用してコンポーネントベースでUIを構築していくと、 構造が分かりやすくhtmlを書くことができます。

実際に組み合わせた例を挙げます。 ブログの記事部分です。

const Entry = ({ data , location , pageContext }) => {
    〜〜省略〜〜

    return (
        <Container>
            <Breadcrumbs data={data} location={location} />
            <Article>
                <Title>{node.frontmatter.title}</Title>
                <Time datetime={dateTime}>{dateFormat}</Time>
                <FutureImageContainer>
                    {futureImage}
                </FutureImageContainer>
                <EntryDocument node={node} />
                <EntrySns data={data} location={location} />
            </Article>
            <EntryPageNextPrev data={data} location={location} pageContext={pageContext} />
            <Breadcrumbs data={data} location={location} />
        </Container>
    )
}

BreadcrumbsArticleはコンポーネントです。 なんとなくUIの構造が分かりやすいですよね。

このようにGatsbyはReactとJSXの組み合わせの高い生産性でWebサイトを構築できます。

動的にも対応している

Gatsbyの静的生成という事で動的に対応していないように思えますが、対応しています。

const SectionMenuItem = ({ menu, initShow, data, location, pageContext }) => {
    const childMenuRender = (menu, show) => {
        if (menu.item.child.length > 0) {
            ~~省略~~
            if (show) {
                return (<ChildMenuList >{childMenusElements}</ChildMenuList>)
            } else {
                return (<ChildMenuList className="hidden" >{childMenusElements}</ChildMenuList>)
            }

        } else {
            return ""
        }
    }

    const [showState, setShowState] = useState(initShow)

    const onClick = () => {
        if (showState) {
            setShowState(false)
        } else {
            setShowState(true)
        }
    }

    const code= showState ? "e313" : "e5cc"

    return (
        <Item>
            <Parent onClick={onClick} as="div">
                <ParentIcon>
                    <ThemeProvider theme={{ color: "#bbb", fontSize: fontSize.rem24px, top: "-1px" }}>
                        <MaterialIcons code={code} />
                    </ThemeProvider>
                </ParentIcon>
                <ParentTitle>
                    {menu.item.name}
                </ParentTitle>
            </Parent>
            { childMenuRender(menu, showState)}
        </Item>
    )
}

クリックするとメニューが展開されるコードです。 静的生成してもonClickハンドラーは動作してhtmlを書き換えてくれます。

Flutter入門 / 解説の左側のメニューが実装されたものです。

Gatsbyへの移行で作業したこと

  • Atomic Designの追求
  • CSSをstyled-componentsで書くようにした
  • 機能が足りない為、自作プライグインを実装する必要があった

Atomic Designの追求

HugoのテンプレートをAtomic Designで書いていたつもりでしたが、あまり追求出来なかったので移行する際に整理しました。

ReactのコンポーネントとAtomic Designは相性が良いので、かなりUIパーツの再利用性が高くなりました。

Atomic Designの解説は下記のサイトにあります。

CSSをstyled-componentsで書くようにした

Hugoの時はSCSSとBEMで書いていました。 BEMは素晴らしいですが、命名規則が理由でクラス名が長くなりがちです。 それが個人的には不満でした。

styled-componentsでScoped CSSを書けるので短い名前でも重複しないようになりました。styled-componentsとは、CSS in JSのライブラリです。

styled-componentsはコンポーネントの中に書けるので便利です。 下記のコードはブログの左上のプロフィールのコンポーネントです。

import React from "react"
import styled from 'styled-components'

import AvatarImage from "../atom/images/avatar-image"
import TwitterButton from "../atom/twitter-button"

import { fontSize } from "../../utils/site-theme"
import { SideWidget } from "../styles/side-widget"

const Profile = () => {
    return (
        <ProfileWidget>
            <AvatarStyledImage loading="eager" durationFadeIn={100} >
                <AvatarImage />  
            </AvatarStyledImage>
            <Name>
                nasust / hideki mori
            </Name>
            <TwitterButton />
            <Message>
                UIが好きで、
                フロントエンド、バックエンドを開発しているソフトウェアエンジニアです。
            </Message>
        </ProfileWidget>
    )
}

export default Profile;

const ProfileWidget = styled(SideWidget)`
    font-size: ${fontSize.rem12px};
`

const AvatarStyledImage = styled.div`
    width: 100px;
    height: 100px;
    margin-left: auto;
    margin-right: auto;
    border-radius: 100px;
    overflow: hidden;
`

const Name = styled.div`
    padding-top: 1rem;
    padding-bottom: 1rem;
    text-align: center;
    font-weight: bold;
`

const Message = styled.div`
    padding-top: 1rem;
`

自作プラグイン

gatsby-remark-link-cardというブログカードを表示するプラグインもあったのですが、OGタグが無いサイトなどには使用出来なかったので自作しました。

非公開の自作のプラグインの作成方法はCreating a Local Plugin | Gatsbyで解説されています。 Markdownの拡張方法はCreating a Remark Transformer Plugin | Gatsbyで解説されています。

移行して良かった点

  • GatsbyのReactとJSXのおかげでAtomic Designをさらに追求できた。
  • styled-componentsのおかげBEMのような長いクラス名を付ける必要が無くなった
  • Webpの対応が楽になった。Hugoでは生成後にバッチでWebpを作成する必要があったが、Gatsbyはデフォルトで対応している
  • プリフェッチで次のページの読み込みが速い

Hugoより生成の速度が遅いですが、機能拡張やテンプレートの構造などはGatsbyの方が良いですね。

iOS、Android、Web、APIサーバーなどのフロントエンド・バックエンドを開発するソフトウェアエンジニアです。 UI/UXが好きです。かっこいいUIやWebデザインを眺めるのが趣味です。 このブログはソフトウェア開発関係の内容を記事にしています。
web service:
GitHubQiitaTwitterはてなブログ
handle name:
nasust
real name:
hideki mori
job:
ソフトウェアエンジニア
develop:
target: ios, android, web page, single page application, api server, system service, cli tool, linux embedded device

lang: c/c++, go, swift, objective-c, java, kotlin, typescript, dart, javascript, ruby, python, php

tool: vscode, xcode, android studio, photoshop, vim, docker