Next.jsでCMSにMDX形式で登録したコンテンツを利用する ―next-mdx-remoteの使い方―

Jamstackな構成では、CMSにどのような形式でコンテンツを登録するかが関心になって来るかと思う。

Markdownを採用する場合は、remark-reactやremark-htmlを使ってMarkdownをHTMLに変換できる。

ただ、実際にはMarkdownだと機能的に足りない部分が出て来そうで、MDXという、MarkdownにJSX(Reactコンポーネント)を埋め込める形式を利用すると捗る。

しかしこのMDX、CMSのような外部コンテンツとして登録する場合はどう使えば良いんだ?と思ったら、Next.jsの場合、next-mdx-remoteという便利なライブラリがあった。

next-mdx-remoteの使い方

例として、Next.jsのImageコンポーネントを利用してみる。

# プロフィール画像

<Image src="/images/profile.jpg" width="100" height="100" />

CMSには上のようなMDX形式で保存して、

Reactコンポーネントでこのように変換して利用できる。renderToStringhydrateメソッドの第2引数にMDXで利用しているReactコンポーネントを渡すといい感じに変換してくれるようだ。

2021/05/25追記

Version 3ではrenderToStringhydrateメソッドではなくserializeメソッドを利用する方式に変わっている。

JavaScriptで要素をフェードイン・フェードアウトで切り替える ―async/awaitでrequestAnimationFrameを使う―

妻の歌人としての個人サイトのトップページで、短歌をアニメーションで切り替える処理をJavaScriptで書いている。

jQueryには上のような専用メソッドがあるフェードイン・フェードアウト処理を自分でググりつつ書いてみたところ、試行錯誤できるポイントがあったのでまとめておきたい。

setTimeoutを使った当初実装

まずjQueryの実装は高度に共通化されていて読み解くのがなかなか大変そうだったので、ググって実装方法を探してみた。

このStack Overflowの回答が分かりやすかったので、参考にして実装してみた。

それがこれ。ループでsetTimeoutを使って100ミリ秒ごとに少しずつopacity(不透明度)を変化させている。

分かりやすい処理でちゃんとフェードイン・フェードアウトしたので満足しつつ、念の為他のサイトの実装例も色々と見てみたらもっと良い実装方法がありそうだった。

requestAnimationFrameを使ってより滑らかなアニメーションを実現

このサイトの実装例は明らかに私の当初実装より洗練されているように見えた。requestAnimationFrameというメソッドを使うと良いらしい。

このコールバックの回数は、たいてい毎秒 60 回ですが、一般的に多くのブラウザーでは W3C の勧告に従って、ディスプレイのリフレッシュレートに合わせて行われます。

ただし、setTimeout や setInterval は、ブラウザー側で再描画の準備が整っているか否かにかかわらず、必ず実行されてしまいます。また、ブラウザーのタブが非表示 (バックグラウンド) の場合でも常に実行し続けます。

一方で、requestAnimationFrame はブラウザーの負荷に合わせては 60 FPS 以内で再描画の準備が整ったタイミングで実行され、また、ブラウザーのタブが非表示 (バックグラウンド) にある場合は、発火頻度が自動で低下します。これにより、メモリーの消費を抑えることができます。

こういった理由で requestAnimationFrame はタイマーメソッドより、アニメーションの表現に向いていると言えます。一方で、正確な FPS を制御することはできません。

いい感じにブラウザのフレームごとにタイムスタンプを引数にしてコールバック関数を実行してくれるらしい。

このrequestAnimationFrameを使って上のように実装し直してみた。当初の100ミリ秒ごとの不透明度変更と比べて明らかにアニメーションが滑らかになっている。第2引数にフェードイン/フェードアウト持続時間durationを指定できるようになったのも分かりやすい。

async/awaitの利用

参考サイトと違うのはasync/awaitを使っている点で、

コールバック関数の引数にタイムスタンプが渡されることに注目して、1フレーム進んだ後にPromiseがタイムスタンプを引数にresolveして、const timeStampに1フレーム進んだタイムスタンプが代入される仕組みになっている。

その他の参考記事

Next.js + microCMS + VercelでJamstackなブログ付き個人サイトを作る

妻の歌人としてのブログ付き個人サイトをリリースしたので、技術的な観点をまとめておきたい。

Jamstack

これがサイトの構成図で*1、最近流行りのJamstackというアーキテクチャで作ってみた。

Jamstackは、静的サイトジェネレーターを用いてCMS等で管理するコンテンツをビルド時にすべて取得して、ユーザーアクセス前に用意しておいたHTML、CSSJavaScriptCDN経由で配信することで、画面遷移が非常に速い優れたパフォーマンスのサイト構築を可能にする。

CMS更新時のWebhook通知で自動デプロイする仕組みを入れることで、開発者がソースコードを触ることなく、サイト編集者で完結したサイト更新を行うことができるため、ビジネスから趣味の活動まで、静的サイト開発・運用の手段として有力な選択肢になるだろう。

  • 静的サイトジェネレーター
    • Next.js, Nuxt.js, Gatsby.js, Hugo, Jekyll等
  • ホスティングサービス
    • Netlify, Vercel, Firebase等
  • ヘッドレスCMS

Jamstackな構成をサポートする静的サイトジェネレーター、ホスティングサービス、ヘッドレスCMSには様々なサービスが覇を競っている状態で、ググると色々な構成例が出て来るので、サイトの目的や好みに応じてサービスの組み合わせを選ぶと良いだろう。私はNext.js(React)を使ってみたかったのでNext.jsとVercelを採用し、ITに詳しくない妻がサイトを更新するので、CMSには日本製で非開発者にもUIが分かりやすいと評判のmicroCMSを採用した。

Jamstackのメリット

実際にサイト開発、運用を経験して感じたJamstackのメリットについて書きたい。

お手軽ハイパフォーマンス

普通にNext.jsで開発してVercelにデプロイするだけでGoogleのPageSpeed Insightsで99のスコアが出るのはすごい。実際にサイトを触ってみてもページ遷移が非常に速いことが分かるだろう。

非開発者による更新も比較的容易

サイトコンテンツはすべてCMS上でMDX(Reactコンポーネントが使えるMarkdown)形式で管理している。microCMSにはリッチエディタ機能もあるけれど、仕事でリッチエディタを使っていて、想定外のHTMLタグが大量に混入して苦労することが多かったため、敢えてテキストエリアにMDX形式で入力する方式を採用した。

サイト更新者の妻はITにあまり強くないけれど、microCMSの管理画面を見ながら各APIのコンテンツ構成と、私が作成した初期コンテンツを元にMarkdownの説明を簡単にしたら、microCMS上でサクサクコンテンツ追加できるようになったので、noteやはてなブログのリッチエディタほど直感的に操作できないけれど、非開発者によるコンテンツ更新も比較的容易に行える感触だ。ちなみに妻にMarkdownエディタを紹介したところ面倒臭く感じたようで、microCMSの管理画面のフォーム上で直接更新して、デプロイ結果を見て実際の表示を確認していた*2。趣味のサイトであれば取り敢えずデプロイしてみる雑な運用もアリかなと思う。

開発体験が良好

Next.jsはReactベースのライブラリで、TypeScriptの導入も容易だ。TypeScriptの型チェックの恩恵を受けつつReactでキッチリとコンポーネント化できるフロントエンド開発は、普段仕事でBackbone.jsベースの複雑なSPAを触っていることと比較しても快適で、コーディングのモチベーションを終始高く保てた。Node.jsのライブラリ群も非常に充実している。

Next.jsは今回初めて触ったけれど、簡単なブログサイトを作ってデプロイまで行う公式チュートリアルが非常に易しく書かれているので、学習コストは低く感じた。

Reactの公式ドキュメントも日本語訳があり、分かりやすく丁寧に書かれていて助かる。

サイト制作手順

これから同様の構成で静的サイトを作ってみようという方への参考と自分用のメモのため、サイトリリースまでの手順を書いておきたい。工数的にはほぼ年末年始休暇中の作業でリリースまで持ち込むことができた。

  1. Next.jsのチュートリアルサイトを作成する
  2. GitHubチュートリアルサイトに名前を付けてリポジトリ作成する
  3. TypeScriptを導入する
  4. ESLintJavaScript Standard Style)を導入する
  5. CSSライブラリのBulmaを導入する
    • BootstrapをJSなしでCSSのみにしたような印象のライブラリ
    • 便利なスタイルが色々と用意されていて助かるが、 Bootstrapと同様に影響範囲が広くなるので、仕事で使うには注意が必要だと思った
  6. サイト構成・デザインを妻と相談しつつ決定する
    • ハンバーガメニューではなくフッターで4つのメニューを切り替える
    • トップページで短歌をフェードイン・フェードアウトで切り替えて表示する
    • 白背景に黒字をベースとし、アクセントカラーに緑を採用する
    • その他サイトタイトルやdescriptionなどを決定した
  7. ローカルにコンテンツを用意しつつ、各ページを作成する
  8. ローカルからmicroCMSにコンテンツを移動し、APIでコンテンツを取得する処理を書く
  9. CMSでMDX形式のコンテンツを管理する仕組みを導入する
  10. トップページで短歌がフェードイン・フェードアウトで切り替わる処理を追加する
  11. faviconとOGP画像を設定する
  12. VercelにGitHubリポジトリを連携してデプロイする
    • 環境変数(microCMSのAPIキー)の設定もGUIで簡単に行えた
    • GitHubにpush時の自動デプロイ機能がデフォルトで付いていて驚いた
  13. Vercel管理画面で独自ドメインを取得する
    • 分かりやすいUIでサクッと購入できた*3
    • 購入後自動で独自ドメインにアクセス可能になり、TLSSSL)の設定まで自動で始まって、約1時間後にTLS設定が完了した
  14. Vercel管理画面でDeploy用URLを取得し、microCMSのWebhook通知先に設定して、CMS更新時の自動デプロイを可能にする
    • Vercelのデプロイ用URLがエラーとなり機能しなかったが、Vercelのサポートに英語で報告したらすぐにバグ修正してくれた
  15. VercelからSlackにデプロイ通知する
    • プライベートでSlackを使っていないため、今回の実運用では採用していないが、仕事で使うならあった方が良いだろう
  16. サイト編集者にCMSの更新方法を案内する

*1:Slackのデプロイ通知については可能なことを確認したのみで、実際の運用は行っていない(プライベートでSlackを利用していないため)。図はDraw.ioを使って描いた。

*2:現状では30秒ほどでデプロイ完了する。

*3:お名前.comとは雲泥の差だ…。

妻の歌人公式サイトをリリースしました!

妻の歌人としての個人サイトを開発してリリースした。

開発の背景

悪友

悪友

  • 作者:榊原紘
  • 発売日: 2020/08/05
  • メディア: 単行本(ソフトカバー)

妻は去年、笹井宏之賞大賞受賞の特典で第一歌集『悪友』を出版して絶賛売り出し中なのと、最近のnoteのゴタゴタでnote退会したいみたいな話があったので、私のNext.jsの勉強がてらnoteみたいなブログ機能付きの公式サイト作ってみたら面白いんじゃない?ということで年末年始休暇にサクッと作ってみた。

トップページで短歌がアニメーションで切り替わるのと、Next.jsのSSG*1故のサクサク動くレスポンスがアピールポイント。

歌人で個人サイトを持っている人は少なく、持っていたとしてもワードプレスのテーマで作っている方が多いようなので、フルカスタマイズ可能なスクラッチ開発のサイトを持っているのは差別化になるかと思った。

やっぱりWebはコツコツ作ってるときが一番楽しい。HTMLは自由だし、最新のJSライブラリ群は恐ろしく快適な開発体験を提供してくれる。

2021/01/18追記

サイトの情報をソースにして、妻のWikipedia記事をどなたかが書いてくれた。

技術的な観点の記事も書いた。

*1:Static Site Generation。ユーザーがサイトにアクセスする前のビルド時にあらかじめコンテンツをすべて用意しておく手法。また、VercelではデフォルトでVercel Edge NetworkというCDNがサポートされている。

公立中学動物園論争に見る「ダブスタ指摘」論法のくだらなさ

中学受験について、匿名でないと書けないことを伝えたい

まぁこんなウダウダ書かなくても公立中出身者ならあの動物園に通わなくていいってので十分良さはわかるよ。

2020/12/08 22:32

公立中学を「動物園」と表現したブコメに日頃ポリコレや反差別にうるさい「はてなリベラル」が多くスターを付けているのはダブスタだという論争が話題になっている。

この論争には、私が以前からネットで多用される詭弁的論法の最たるものだと認識している「ダブスタ指摘」論法の問題点が表れていると感じたので、その観点に絞って書きたい。

問題点を指摘する増田を書く

論争が起きているのははてな匿名ダイアリー(通称増田)なので、私も久しぶりに上の増田を書いてみた。

他者の表現の自由を侵害しようと言うのであれば、その表現がどのように表現対象の人権(自由)を侵害する差別的な表現なのか、ということを十分に社会的合意が得られるレベルで示す必要がある。「自由」と「自由」の対立の調停を図るのが本来のリベラリズムの立場のはずだ。この2つの自由のうち、表現の自由の方を極端に軽視するのが、行き過ぎたポリコレ推進派が本当に「リベラル」なのかと、自己矛盾を感じさせるポイントだろう。しかし「はてなリベラル」嫌いの人たちは、「動物園」の表現の取り締まりにまったく躊躇がないらしく、その点で彼らが嫌いなはずのダメなリベラルに良く似いる。

表現の自由基本的人権なのだから、不快な表現、攻撃的な表現というだけで取り締まろうという方向にするべきではなく、取り締まろうと言うのであれば、その表現が他者の人権(自由)をどのように制限しているかの丁寧な説明が必要なはず、というのが論旨で、「はてなリベラル」批判派の人たちが「動物園」の比喩を曖昧な根拠で取り締まろうというのは、彼らが何よりも嫌いなはずの行き過ぎたポリコレ推進派と同じ立場ですよね?という指摘だ。

「日頃は反差別、ポリコレに熱心なはずの「はてなリベラル」が公立中学を「動物園」と表現するのはダブスタだ」という批判をするとき、この批判者自身の立場は、以下のような真逆の方向が想定し得る。

  1. ダブスタであり、公立中学への表現も厳しく取り締まるべきだ
  2. ダブスタであるから、公立中学への表現が許されるように、その他の差別問題に関する表現についても寛容であるべきだ

私がこの論法を嫌いなのは、批判者が自分はこのうちのどちらの立場なのか隠して批判することが多いためだ。首尾一貫性という、どうとでも破綻を見出せるような枷を、批判相手にのみ課して自らには課さないのは卑怯だと感じる。

ダブスタ指摘論法はミラーリング、試し行動の側面を持つ

私の増田への反応を見てみよう。

「公立中学差別」って何だ?

何でこう論理的思考が弱い人が多いのか、&quot;表現の自由を制限するには、その表現によってどのような人権が制約されているかの丁寧な説明が必要&quot; これを引き出す為の批判に決まってるじゃん(自由の制限の基準含め)

2020/12/12 11:15

今までの差別問題や表現問題のときにそれらはされてましたか?

するまでもなく明らかな場合もあったと思うけど、そうでなく意見が割れてるときにそんな建設的な流れになった記憶ないけど。

検証・説明しきれない部分を「理解してるくせにしらばっくれてる」みたいなのはあったっけ。

そう言えばいいですか?

なんで自分らが批判されるときだけ違う基準を持ち出したり、違うレギュレーションで議論しようとしたりするの?君たちだけそんなに特別な存在なの?

行き過ぎたポリコレ推進派のダメな部分をそのまま演じてやり返す「ミラーリング」のつもりらしい。何だかため息が出てしまう。わざわざ自分がダメだと思っている集団の行動を真似して人を不快にさせて、自分の不快感に気づいて貰おうというのでは、まるで子供の「試し行動」だ。

表現の自由と差別の問題は複雑で、人によって様々な立場を取り得る。自らの立場を隠して、あるのかないのか分からない「界隈」の対立を煽るからダブスタ指摘論法が嫌いなのだと、改めて確認させられる一件だった。

GitHub CLIから任意のMarkdownテンプレートでプルリクエストを作成する

GitHubリポジトリ.github/pull_request_template.md を置くとGUIからプルリクエスト(以下PR)作成時にデフォルトでテンプレートが展開される機能があって、仕事の開発でもこの機能を各リポジトリで活用している。

ただこのテンプレート、大部分のタスクにとって不要な文言が諸事情で記載されていたり、私個人の判断で追加している定型文があったりして、毎回同じような変更をしなければいけないのがなかなかダルい。もちろん開発チーム内で議論してテンプレートの内容を変更すれば良いのだけれど、諸事情で入っている文言を外すのも、個人的な好みの文言を入れるのも、チームの合意を得るのが難しい部分だ。なので、GitHub CLIを使って手元の任意のテンプレートでPRを作成する方法を試してみた。

GitHub CLIのセットアップ

$ brew install gh
$ gh auth login

Macの場合、インストールからアカウント連携まで上の2つのコマンドで実行できる。

プルリクエストの作成

Markdownのテンプレートを作成して、PRを作成したいブランチに切り替えてから

$ gh pr create --title 'PRのタイトル' --body "`cat template.md`"

上記のコマンドでcatで出力したMarkdownファイルの内容を引数としてPRを作成できる。

これで手元で任意のテンプレートを指定してPRを作成できるようになった。

Puppeteerで記事本文中のはてなブログタグを取得する

はてなブログの新機能

はてなブログの記事にはてなブログタグを設定できる機能がリリースされた。

はてなブログタグとは

はてなブログタグ」は「はてなキーワード」からWikiのようなユーザーによる編集機能が除外されて名称変更したサービスで、はてなブログに記事を投稿すると、記事本文中の単語にはてなブログタグの個別ページへのリンクが自動で付与される仕組みになっている。

Puppeteerで記事本文中のはてなブログタグを取得する

せっかくなので、記事本文中のはてなブログタグのリンク付与状況を取得して、はてなブログタグの設定の参考にできると良いと思った。

<a class="keyword" href="http://d.hatena.ne.jp/keyword/%C8%F3%A5%E2%A5%C6">非モテ</a>

ブログ記事のソースを確認すると、はてなブログタグは上のような形式で挿入されているので、Webスクレイピングで容易に取得できそうだ。

Webスクレイピングは以前にも記事を書いたPuppeteerで行うのが簡単だ。

サクッと書いてみた。

先日バズった私の非モテ論記事で試してみる。

$ node index.js https://fuyu.hatenablog.com/entry/2020/10/05/002610
{
  '非モテ': 57,
  '小野ほりでい': 2,
  'フェミニズム': 13,
  '潜在的': 2,
  'ヘテロ': 2,
  'モリー': 1,
  '二村ヒトシ': 1,
  '森岡正博': 1,
  'ブッダ': 2,
  '古今東西': 1,
  rei: 1,
  '中村 元': 1,
  Kindle: 1,
  '本田 透': 1,
  '恋愛資本主義': 4,
  'バロメータ': 1,
  '本田透': 2,
  '電波男': 1,
  'ラク': 1,
  'ウエルベック': 4,
  'ミシェル・ウエルベック': 1,
  '服従': 3,
  'イスラーム': 5,
  '若い女': 1,
  Twitter: 3,
  'ウェルベック': 2,
  'ユートピア': 1,
  'ジェンダー': 4,
  '異性愛': 1,
  '性風俗': 1
}

やはり「非モテ」が圧倒的に多い。この記事であれば、「非モテ」と「フェミニズム」辺りをタグ設定しておけば良さそうだ。

参考記事

JavaScriptで配列の重複数をカウントする処理の参考にした。