【React/Next.js】Markdownの記事を表示するサイトの開発
Section: Technology

きっかけ

以前のサイトはRust言語でできたYewというWASMに変換するフロントエンドのフレームワークを用いていました。

しかし以下の欠点あります。

  • あまり周辺ライブラリが揃っていない
  • wasmファイルのサイズが大きかったり、初期ロードの時間があったりして、(キャッシュがない状態では)サイト全体が表示されるまで10秒以上かかる

SEO対策のためにプリレンダーをしていたが、docker-composeでwebpackを動作させると、pkgフォルダーを読み込めないというエラーが出るようになりました。entryのファイルはwasmが出力された後に実行してほしいが、entryが先に実行されるようになったのが原因です。

少し格闘したが、欠点を思い出してここで時間を費やしても現状維持でしかなく、その時間があれば爆速のサイトを別の技術で開発した方がいいじゃないかと思い、開発する事に至りました。

要件

まず要件として

  • 各記事はMarkdownファイルから自動生成
  • 動的な部分も欲しいため、SSRを行う
  • 以前のサイトとほぼ変わらないデザインにする

を挙げました。

基本的に有名なライブラリやフレームワークを用いれば実現できそうなサイトです。

技術選定

私のフロントエンドフレームワークの知識は

  • Vueをバイトで使用
  • ReactAngularの名前は知っている

という状態なので、使用する技術の選定から始めることにしました。

きっかけにあるようにwasmを用いるようなマイナーな技術では、自分で実装しないといけないことをしみじみと感じました。

そのため以下の順でフロントエンドフレームワーク、選定することにしました。

  1. 一番有名なフレームワーク
  2. 今後数年も発展していきそうか

一番有名なフレームワーク

個人的に一番有名であることのメリットとして、ライブラリやベストプラクティスが発達しており、メンテナンスもしっかりされていると所だと思います。

更に私はPHPのLaravelをよく触っており、その恩地を受けてきて、有名である重要性を感じてきました。

どれが有名か調べてみると、Reactがダウンロード数や検索数がここ近年圧倒的に多いことがわかりました。

またSSRを使用するうえでは、Next.js一択しかないと思いそちらも使用することにしました。

今後数年も発展していきそうか

発展しなければすぐサイトをリニューアルしなければならなくなるので、こちらも念のため考えることにしました。

フレームワークが危うくなる原因として、他の言語のものや他の技術が発展して圧倒的に開発しやすくなり、皆がそちらを使いだすことだと思っています。

個人的にWebAssemblyGraphQLが将来的に発展しそうな技術です。

WebAssembly

WebAssemblyは速度とか速くなったり、色々なプラットフォームでも使えたりして、とても推しています。

しかしJavaScriptでも十分速いし、置き換えるためのコストがとても高く、広く使われるのは先かまたは使う人が少なくメンテナンスする人も減り廃れていく可能性があります。

GraphQL

GraphQLではApolloというプラットフォームがあり、GraphQLに最適化されています。

しかしNext.jsでも問題なく使用できそうなので、わざわざそちらを学んで使う必要はないと思う人が多そうと感じました。

以上からReact/Next.jsを使うことにしました。

学習

TypeScript

まずHandbookを読んで概要をつかむことにしました。

型アノテーションとかがRustと似ていたりして、馴染みがありました。

Unionの変数の扱い方やinterfaceとtypeの二つあることは、馴染みがありませんでした。

総合的には、型の安全性と記述量が増えないようにすることに関して、バランスがよくとれている言語だと思いました。

Next.js

Next.jsの公式サイトStart Learningで学ぶと良いという情報を頂いたので、次に進めていきました。

Markdownの記事からHTMLに変換して表示させるサイトを作っていく内容でした。

また基本的な処理だけでなく、SEOについての説明とかもあり、とてもぴったりな内容でした。

サイトのリニューアル

先程の学習を活かして、現在のサイトを置き換えていきました。

見た目

html!が書いてある見た目の部分を移植するところから始めました。

HTML

HTMLの部分は

  • classアトリビュートをclassName
  • ルート部分をLink
  • imgタグをImage
  • svgのアトリビュートをキャメルケース
  • 処理の必要な部分を消去 のみ変更しました。

以下のようにYewでもReactでも内部処理の扱い方は変わらないので、ほとんど問題なく移行できました。

{ "Hello World!" }

CSS

デザインはTailwind CSSを使っていたので、以下のように入れました。

npm install tailwindcss postcss-preset-env postcss-flexbugs-fixes

特に問題はなかったのですが、Tailwindのバージョンが3.0.0になっててびっくりしました。

postcss.config.jstailwind.config.jsはそれぞれ以下のようになりました。

module.exports = {
    plugins: [
      'tailwindcss',
      'postcss-flexbugs-fixes',
      [
        'postcss-preset-env',
        {
          autoprefixer: {
            flexbox: 'no-2009'
          },
          stage: 3,
          features: {
            'custom-properties': false
          }
        }
      ]
    ]
}
module.exports = {
  purge: [
    './pages/**/*.tsx',
    './components/**/*.tsx'
  ],
  // ...
}

処理

MarkdownからHTML

unifiedを使って変換を行いました。

これによりコードのハイライトなど様々なことができるようになりました。

import {unified} from 'unified'
import remarkParse from 'remark-parse'
import remarkRehype from 'remark-rehype'
import rehypeSlug from 'rehype-slug'
import rehypeHighlight from 'rehype-highlight'
import rehypeStringify from 'rehype-stringify'
//...
const processedContent = await unified()
    .use(remarkParse)
    .use(remarkRehype)
    .use(rehypeSlug)
    .use(rehypeHighlight)
    .use(rehypeStringify)
    .process(matterResult.content)

Meta情報の取得とエラー

gray-matterにより、以下のようにmarkdownファイルの上部に記入した内容を取得することができます。

---
title: たいとる!!!
published_time: 2021-12-14T12:00:00+09:00
modified_time: 2021-12-14T12:00:00+09:00
---
const matterResult = matter(fileContents)

しかし以下のようなエラーがでてくることがあります。

Server Error
Error: Error serializing `data.published_time` returned from `getStaticProps` in "/".
Reason: `object` ("[object Date]") cannot be serialized as JSON. Please only return JSON serializable data types.

対応していないDateTime型のデータをそのまま読み込もうとしているため、エラーが起こっています。

そのためpublished_time.toISOString()というようにstring型に直して対処しました。

デプロイ

速度

これまで最初の表示まで10秒程度かかっていたので、とても爆速になりました。

React/Next.jsで開発してみて

検索やsvgをリアルタイムでアニメーション処理していた部分は実装できていないが、主な部分は移行できました。

Vueだとフレームワークを使っているという感じが強かったが、React/Next.jsは言語をそのまま書いているように感じました。

まだ状態管理部分にあまり触れられていないので、結局どちらの方が使いやすいか調べていきたいです。