Hero img
GatsbyからNextjsに移行した理由

ブログをReactのフレームワークNextjsに移行しました。

Gatsby5.0が出た時に周りのプラグインの挙動がおかしかったので勉強を兼ねてNextjsに移行してみたらNextjsの方が利便性を感じた。


目次

  • Gatsbyから移行した理由
  • Nextjsを触ってみて、
  • mdx
  • mdのパース
  • タグフィルタリング機能
  • mdxブログのテンプレート
  • getStaticPropsとgetStaticPaths
  • GatsbyとNextjsを使ってみて
  • Gatsbyのメリット
  • Nextjsのメリット
  • まとめ

Gatsbyから移行した理由

Gatsbyのver 5が出て、バージョンを上げたら、node,reactのバージョンアップが必要になり、それに伴い、警告メッセージがたくさん出てきて気持ち悪く、また、必要なnodeのバージョンが高かったので、ビルドできない環境であったため、前々から気になってたNextjsに触れることを決めました。
今までNextjsを触らなかったのは、サーバー側も触れる、SSR,SSGとか、とにかく色々できる。つまり機能が多いので、学習コストが高そう、プラグインとかも無いので、全部自前で用意する必要があると思っていたからです。

Nextjsを触ってみて、

GatsbyからNextjsに移行する時、今までmdxで記事を書いていたので、これを解決しなければならない、またSEO関連もPluginで終わらせていたので、この辺もどうにかしないといけない、デフォでssrって、別にSSGでいいんだけど、他に画像の最適化もしないといけないのでとにかく時間がかかってしまった。

mdx

mdx関係は、Nextjsのmd関係を検索するとまぁまぁ、ライブラリーが出てくるのだが、新規では無く、今までGatsbyで使用した物の移行となるので、既存のライブラリーを使用すると、Gatsbyでカスタマイズしていた書き方が出来なく、解決方法も無かった、エラーばっかりでライブラリーを使うことを諦めました。
そして、最終的には、rehype-○○と,remark-○○のライブラリーを使用し、カスタマイズができるようにしました。
これがかなり大変で、pタグの中にdivが入ってしまっている現象が発生し、正常には画面が表示されるが、エラーメッセージがかなり表示されたり、目次を作ったり、タグ、フィルター機能、とにかくmdx関係で苦戦しました。

mdのパース


  const result = unified()
    .use(remarkParse)
    .use(removeCustomElementParagraph)
    .use(remarkGfm)//他の打消~~とかも使えるようになる
    .use(plugin)
    .use(remarkRehype, {
      allowDangerousHtml: true, // trueにしておくことで、自分でカスタマイスしたタグをそのまま吐き出してくれるようになります。
      handlers: {
        message: handler,
      }
    })
    .use(rehypeSlug)//headerにidを付けれる
    .use(rehypeCodeTitles, { customClassName: "code-title" })
    .use(rehypePrism, { plugins: ['line-numbers', 'copy-to-clipboard'] })
    .use(collectIds)// p in divで構造エラーが出るのでこの関係を外す
    .use(rehypeStringify, {
      allowDangerousHtml: true
    })//mdから最後に変換する必要がある
    .processSync(md);


  return { result: result, headers: ids }

上記のような感じで,mdxファイルのパース処理をしています。(一部自作)
A to Bみたいに簡単に変換してくれるのだろうと思っていたが、間に色々処理をしないといけない事を始めて知った。特にremarkGfmは必ず入れておかなければ...

このmdxは今でもよくわかっていない、自作ファンクションを作る時に理解?したメモがある参考までに

memo.txt
markdownの変更にはmd -> mdast -> hast -> html
 remarkParse   md -> mdast
 remark-rehype mdast -> hast
 rehypeParse html -> hast
 rehypeReact hast -> react
 stringify hast -> html
 remarkRehype mdast -> hast?
 rehypeStringify hast -> html

タグフィルタリング機能

タグで日付ソート、フィルタリングするために。
当初フィルタリングするために、ssrなので、タグをサーバーに一度投げて、その後、mdファイルの全て読み込みタグをさらに読み込みソートして、フィルタリングをして、クライアント側に返す処理になっていましたが、毎回、全てmdファイルを読み込み、タグを取集して日付でソートし、フィルタリングするというかなり無駄な処理をしていました。(だって検索してもそんなサンプルしかなかったし、) nextjsでssr何だからそれが正解なのかなぁと思っていましたが、納得ができず、ビルド前にmdファイルを全て読み込み、タグのjsonファイルと、記事用のmdファイル日付ソートしてjsonファイルを保存する方法を取りました。ついでにサイトマップも作成しちゃう。

ビルド前に実行されるjs
const fs = require('fs');
const matter = require('gray-matter')
const Writefile = async () => {
  const postf = fs.readdirSync("./blogposts").filter((postPath) => /\.md?$/.test(postPath));;
  fs.writeFile('postlist.txt', JSON.stringify(postf), function (err) {
    if (err) throw err;
    console.log('JSONファイルが作成されました!');
  });
  let xml = `<?xml version="1.0" encoding="UTF-8"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:news="http://www.google.com/schemas/sitemap-news/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns:video="http://www.google.com/schemas/sitemap-video/1.1"><url><loc>https://yumeno.me</loc><changefreq>daily</changefreq><priority>0.7</priority></url>`;
  const posts = postf.map((slug) => {
    const content = fs.readFileSync("./blogposts/" + `${slug}`);
    const { data } = matter(content);

    let tmp =   slug.replace(/\.md?$/, '')
    xml +=`<url><loc>https://yumeno.me/${tmp}</loc><changefreq>daily</changefreq><priority>0.7</priority></url>`
      return {data};
  });

  xml +=`</urlset>`
  fs.writeFile('./public/sitemap/sitemap-0.xml' ,xml , ((err)=>{
    if(err) throw err;
    console.log('created-sitemap');
  }))
  const allTags = posts.reduce((a, post) => {
    post.data.tag?.map((tag) => a.add(tag));
    return a;
  }, new Set([]));

  //sort tags
  const allTagssorted = [...allTags].sort((a, b) => a.localeCompare(b));
  allTagssorted.unshift('all');

  fs.writeFile('tags.txt', JSON.stringify(allTagssorted), function (err) {
    if (err) throw err;
    console.log('JSONファイルが作成されました!');
  });
}
Writefile();

mdxブログのテンプレート

カオス状態ですね...

[slug].js

import * as fs from 'fs'
import * as path from 'path'
import matter from 'gray-matter';
import { unified } from 'unified';
import rehypeReact from 'rehype-react'
import MDXComponents from '../component/MDXComponets'
import React from 'react';
import rehypeParse from 'rehype-parse';
import { MarkdownToHtml  } from '../lib/transpiler';
import { postFileNames , MdxPath} from '../component/utils/mdx-file-loader';


const processor  = unified()
    .use(rehypeParse, { fragment: true, })
    .use(rehypeReact, { createElement: React.createElement, components:MDXComponents})
import BlogpostTemplate from '../component/Blogtemplate1';
export default function Page({ ...props }) {
  return (
    <BlogpostTemplate frontmatter ={props.frontmatter} headers = {props.headers} slug={props.slug} relative={props.relativepost} >
            {processor.processSync(props.html).result}
    </BlogpostTemplate>
  )
}

export const getStaticProps = async (context) =>{
  const slug = context.params.slug;
  const filePath = path.join(process.cwd(), 'blogposts', `${slug}.md`);
  const fileContent = fs.readFileSync(filePath, 'utf-8');
  const { data, content } = matter(fileContent);

  const posts = JSON.parse(postFileNames).map((slug) => {
    const contentmdx = fs.readFileSync(path.join(MdxPath, `${slug}`));
    const { data } = matter(contentmdx);
    return {
      frontmatter: data,
      slug: slug.replace(/\.md?$/, ''),
    };
  }).filter(post=> data.tag.some(tag => post.frontmatter.tag.includes(tag)))
  .sort((a,b)=>{a.frontmatter.date < b.frontmatter.date ?  1 :-1;});

    let newArray = []
    while (newArray.length < 6 && posts.length > 0) {
        const rand = Math.floor(Math.random() * posts.length)
        newArray.push(posts[rand])
        posts.splice(rand, 1)
    }
  const datas  = await MarkdownToHtml(content);
  const htmldata = datas.result.toString();
  const headersid = datas.headers;
  return {
    props: { html: htmldata , frontmatter : data , headers : headersid , slug : slug  ,  relativepost: newArray }
  }
}

export async function getStaticPaths() {
  const postsPaths = JSON.parse(postFileNames).map((slug) => ({
    params: {
      slug: slug.replace(/\.md?$/, '')
    },
  }));
  return {
    paths: postsPaths,
    fallback: false,
  };
}

getStaticPropsとgetStaticPaths

mdxブログのテンプレートでは、getStaticPathsでmdファイルのページを作成しています。
記事の中身はgetStaticPropsを使って、mdファイルを読み込み、ついでに関連する記事を6を渡し、自作ファンクションMarkdownToHtmlでmdからhtmlにしています。
せっかくhtmlにしたのにさらに{processor.processSync(props.html).result}でhastに変換しています。
html->hastは無駄なのでは?と思っていますが、解決出来なかった。なんかPrismが、ページリロードした時に反映されないので諦めました。

GatsbyとNextjsを使ってみて

GatsbyとNextjsを使ってみて

Gatsbyのメリット

  • 1.SSGでどこでもデプロイができる。
  • 2.画像の最適化が自動でされる。
  • 3.機能はプラグインで何とかなるの開発不要

短時間でお金かけずにするのであればGatsbyが良い。Nextjsはnodejsが入っているサーバーが必要になるがGatsbyはnodeサーバーが不要。静的ビルドすれば良いとかは言わないで Pluginも充実しているので簡単にGatasbyでサイト構築ができる。難点なのは、バージョンが上がった場合プラグインとかが使えなくなる可能性が大いにある。buildの時間がかかるのは事実だが、ローカルのPCでビルドしてアップロードすることはまず無く,サーバー上で勝手にビルドが実行されるので、実質待たされる時間はほとんどない。一番問題となるのはフォルダー構成が分かりにくいのと、GraphQLを覚え無いといけない部分。

Nextjsのメリット

  • 1.ビルドが早い。
  • 2.サーバーサイドも自由にいじれる。
  • 3.自由度が高いので汎用性が高い。

汎用性を重視するのであればNextjsがいいと思う。サーバーサイドで処理する事が頻繁に発生する。Gatsbyもできるとか言わないで そして、Nextjs14から、フォルダー構成が分かりやすくなっている。next/imageだけはちょっとどうにかしてほしいが、cloudinaryを使うことにしてからあまり気にしなくなった。

まとめ

ブログするならwordpressを使った方がいいし、高速な静的サイトを作るなら、Gatsbyが良いと良いと思う。
なんでもする必要があるならNextjsを使う方がいいと思う。結局は適材適所であるが、迷ったらとりあえずNextjsを使えば、だいたいどんな状況になっても対処できるのでNextjsを使うことをおすすめする。

関連記事

コメント

コメントを書く

質問や、間違いがありましたら、お気軽にどうぞ

※お名前とコメントの内容を入力してください。
※全てこのブログ上に公表されます。
※許可なく管理者によって修正・削除する場合がございます。 詳しくはプライバシーポリシーを参照ください