このポートフォリオ(yamaの気ままに)の技術周りの話
※ 画像・動画等のコンテンツが上手く生成されないことがあります。その際はお手数ですが、ページの更新を複数回お願いします。
目次
はじめに
自身のポートフォリオとして簡単なホームページとブログを構築しました。 今回は制作にあたってのこだわりや苦労した点について紹介しようと思います。。。
開発期間
3週間(30時間ほど)
使用した技術
Next.js
ベースはNext.jsを採用。サーバーサイドレンダリングや静的サイト生成を簡単に実現でき、状況に応じて高速なページ表示やSEO対策が可能なため。また、ルーティングの設定が簡単でTypeSctiptにも対応しているため、非常に開発体験が良いと感じている。そして、なんと言ってもデプロイが非常に簡単である事。
Vercel
デプロイに使用。 pngを読み込んでくれないのが嫌い。
TailwindCSS
スタイリングで使用
MaterialUI
ナビゲーションバー、テキストフォーム、ページネーション
Email.js
お問い合わせ機能
NotionAPI
Notion上で作成したコンテンツを簡単に取得することができるAPI。Notionは直感的な操作性を持ち、テキストや画像などのコンテンツを自由に配置できるため、自分自身でブログを構築する上での柔軟性が向上すると判断し、採用しました。
NotionAPI使用背景
前提として、簡単なアウトプットをするためのブログ機能を実装したいと思いまいした。私の中で、個人サイト等のブログ運営(CMS)といえばmicroCMSだったのですが、最近台頭し始めた登場したNotionAIでブログのベース作成や雛形形成を行なってくれるということで、ブログ記事執筆ひよっこの私にとってNotionをどうにかして使いたいなと感じていました。そこで見つけたのがNotionAPIでした。
こだわりポイント
記事詳細はSSGでで取得
記事内容は更新が少なく、ビルド時にサーバー側で静的ファイルを生成しとくことで、記事詳細の表示スピードが爆上がりする。 最初からHTMLで書けばええやんて話ですが、中身のコンテンツはAPIで取得しているため、ビルド時にデータをフェッチしときたい。
ブログの記事一覧をISRで取得し、ビルドを最適化する
ISR…Incremental Static Regenerationのこと(段階的な静的サイト生成) SSGとは異なり、ISRは事前にすべてのページを生成せず、初回アクセス時にのみレスポンスが生成され、次回以降はその生成された内容がレスポンスされる。revalidateを指定することで使用できる。
SSGには以下のようなデメリットが挙げられる。
このデメリットをISRが補っており、
と言った利点がある。 今回の場合、記事が増えれば増えるほど、静的なページを生成する際にページ数が多いとビルドに時間がかかる。という欠点に当てはまのでってしまうため、採用した。 revalidate: 10で前回から10秒以内のアクセスを無視すると指定してる。実際にはキャッシュの再検証を必要とするまでの秒数である。キャッシュが作られた後、10秒間はそのキャッシュを返し続けるので、10秒以内に100回アクセスされてもキャッシュの再生成はされないという機能。
お問い合わせ機能
テキストフォームはMUI(TextField)で実装。propsを渡すことでシンプルなバリデーションを設定できることもあってスムーズに作れた。 メール送信ロジックはEmail.jsを使用。 メール本文のテンプレート作成して、POST通信するだけでメール送信できるため、バックエンドの工数が省けてラッキーと思い採用した。無料枠で制限があるが、個人の小規模レベルなら多分大丈夫だろう。。 き
記事内のコンテンツテーブル
これ。notionでは「目次」で作成でき、APIではtable_of_contents というtype名で中身のアイテムを取得できる。今回はあらかじめ記事コンテンツ内のh2タグを全て取得して、表示している。
JavaScript
import { Link as Scroll } from 'react-scroll'; case 'table_of_contents': return ( <details open className='my-4 rounded-md bg-slate-50 p-2 shadow-md hover:cursor-pointer focus:outline-none white:bg-stone-900 md:p-6' > <summary className='text-base font-semibold text-stone-800 focus:outline-none duotoneLight:text-stone-900 md:text-lg'> 目次 </summary> <ol className='p-2 md:p-4'> {headerBlocks.map((block: any, index: number) => { return ( <li key={index}> <Scroll to={block.id} smooth={true} className='inline-flex items-center py-1 text-base text-stone-700 duration-300 hover:text-stone-500 duotoneLight:text-stone-900 duotoneLight:hover:text-stone-300 md:text-lg' > <BsCheck2Square className='mr-2' /> {block[block.type].rich_text[0].text.content} </Scroll> </li> ); })} </ol> </details> );
react-scrollライブラリを使うことで、指定したidの要素に画面をスクロールしてくれる。(これ地味にありがたいと思う)ただ、#xxxx のようなリンクにはなっていない。URLフラグメントを含んだリンクを踏んでも、Next.js側で簡単に実装できる記事を見つけたので検討中、、
解決したい点
liタグやolタグの子要素を描画できない
ネストした箇条書きを書くことはよくあるとは思うのだが、NotionAPIのレスポンスの都合上、かなり遠回りなやり方でワンチャン実装できる感覚だった。
JavaScript
const renderNestedList = (block: any) => { const { type } = block; const value = block[type]; if (!value) return null; // ネストした子要素を取得 const isNumberedList = value.children.results[0].type === 'numbered_list_item'; if (isNumberedList) { return ( <ol> {value.children.results.map((block: any, index: number) => renderBlock(block, index) )} </ol> ); } return ( <ul> {value.children.results.map((block: any, index: number) => renderBlock(block, index) // ブロックタイプによって分岐し、jsx要素で表示する関数 )} </ul> ); }; case 'bulleted_list_item': case 'numbered_list_item': return ( <li> <Text text={value.rich_text} key={index} /> {!!value.children && renderNestedList(block)} </li> );
実際、Notionのネストした箇条書きを見てみると、li,ulタグを使わずにdivタグでゴリ押ししていたので、気が向いたら実装してみることにする。 ちなみに現時点では箇条のネストの代用で、h3タグを利用している。
ページネーション
NotionAPIにはクエリを投げて、取得条件を絞れたり、取得件数を設定したり、並び替えができる。ページネーションというわけで、取得件数を設定するという選択肢を取ろうと思ったのだ、ページ切り替えごとにfetchが走るのが気に入らなかったのでやめた。
代わりに、全件取得して、useStateでページ数の状態を持たせて実装した。これによって、ページ切り替え時に更新せずとも、早い切り替え表示が可能になった。 MUIのPaginationコンポーネントを使用。
JavaScript
import Pagination from '@mui/material/Pagination'; import Stack from '@mui/material/Stack'; import React, { FC, Dispatch, SetStateAction } from 'react'; const BasicPagination: FC<{ setPageProp: Dispatch<SetStateAction<number>>; currentPage: number; allPages: number; }> = ({ setPageProp, currentPage, allPages }) => { return ( <div className=' flex justify-center'> <Stack spacing={2}> <Pagination count={allPages} onChange={(e, page) => setPageProp(page)} page={currentPage} /> </Stack> </div> ); }; export default BasicPagination;
まとめ
Next.jsとNotionAPIを使用することで、自分自身で簡単にブログを構築することができる。DBも必要ないので、開発も楽に進んで行き、Notionの柔軟な操作性でブログ管理が非常に楽で、さらにNext.jsの高速な表示速度やSEO対策により、多くの人々にアクセスされるブログの構築も可能なのでぜひ、この方法を試してみてほしい。