📝Next.js備忘録
Next.jsとは
ReactベースのJavaScriptフレームワーク。人気がある。
使うメリット
- 開発環境の構築が簡単
- ページごとにレンダリング方式を選べる
- CSR(Client Side Rendering)
- クライアント側でデータのフェッチやページの生成を行う
- SPA(Single Page Application)はこれ
- 最初のロードで HTML や JavaScript など全てのデータを取得する必要があるため、ロードに時間がかかる
- SSR(Server Side Rendering)
- データのフェッチやページの生成をサーバー側で行う
- クライアント側は表示するだけ
- リクエストごとに毎回ページを生成するためサーバーにかかる負荷は重い
- コンテンツの更新頻度が高いサービス向け
- SSG(Static Site Generation)
- 事前にビルドして全てのページをサーバー側で生成
- 毎回ページ生成を行わないので、サーバーにかかる負荷が軽い
- コンテンツの更新頻度が低いサービス向け
- ISR(Incremental Static Regeneration)
- SSGの発展型
- 設定した期間ごとにサーバー側で自動で再ビルド
- ある程度更新頻度が高いようなデータを扱う場合におすすめ
- CSR(Client Side Rendering)
プロジェクトを作成
sessionnpx create-next-app hogehoge
カレントディレクトリで作る場合は
sessionnpx create-next-app .
SSG の設定方法
next.config.js を以下のように編集する。
/** @type {import('next').NextConfig} */ const nextConfig = { output: "export", } module.exports = nextConfig
古いバージョンの Next.js だと next export っていう命令を出す必要があったけど、13からは output: "export" を書けば勝手に next build で out フォルダが作られる。
開発コマンド
session$ npx next dev
ビルドコマンド
session$ npm run build
page.tsx は最低限以下のような記述が必要
export default function Page() { return ( <h1>hoge</h1> ) }
Page() の文字列はなんでもいいみたい。
build して出来た out フォルダ内の index.html を開いたら CSS が適用されていない
原因はリンク切れ。ルートからの絶対パスで指定してあるから、ルートが違う環境ではリンク切れを起こす。
ref: 📝Next.jsで静的ビルドしたソースコードをサブディレクトリパスにデプロイする方法
そもそも out はサーバーにアップロードする最後の段階だから別に気にしなくて良いかも。
タグを複数入れられないのは React と同じらしい
<> <h1>hogehoge</h1> <p>hugahuga</p> </>
みたいに空のタグを使ってもいいらしい。
リンクを張る
普通に aタグで書くと無駄なロードが発生して非効率らしいので、Link というコンポーネントを使う。Next.js にデフォルトで準備されてる機能。
<p><Link href="/about">About</Link></p>
metadata は基本 layout.tsx に書く
各ページで title などを変えたい場合は各ページの page.tsx で metadata をエクスポートすればそっちが優先される。
ヘッダーを実装
app ディレクトリー直下に Header.tsx を作成。layout.tsx で読み込み。
getStaticProps が廃止されてる!?
microCSM からブログデータを取得するために必要なのに!13から App Router が主流になったせいで色々変わったっぽい。
ref: 📝microCMSとNext.js13 Server Components
代わりに async/await でデータを取得するらしい。
microCMS からのデータが更新されない
client.js をこれにして解決。
import { createClient } from 'microcms-js-sdk'; export const client = createClient({ serviceDomain: process.env.MICROCMS_SERVICE_DOMAIN, apiKey: process.env.MICROCMS_API_KEY, customFetch: (input, init) => { if (typeof input === 'string') { const newInput = new URL(input) const time = new Date() newInput.searchParams.set('cacheclearparam', `${time.getMinutes()}`) return fetch(newInput.href, init) } return fetch(input, init) }, })
ref: 📝Next.js 13 + appDir + vercel + microcms-js-sdk でAPIのデータが更新されない時の対処法
環境ファイル
.env.development.local を用意してやる。全部大文字の変数名でデータを格納。
Hydration failed because the initial UI does not match what was rendered on the server.
調べてみるとサーバーが返すHTMLとクライアントが生成するHTMLの中身が違うときに出るエラーらしい。ブログ部分にシンタックスハイライトを適用させようといろいろ弄ってたら出てきた。
imgタグのsrcのパス
/image.png が /public/image.png となる。
外部リンクの場合は Link を使わなくていいらしい
普通にaタグでOK。
ブログ部分はMarkdownで書きたい
この記事が一番詳しい。
ref: 📝Next.jsを利用した初めての本格的Markdownブログサイトの構築
最新版にアップデートするコマンド
公式が推奨するコマンドらしい。13から13.5にアップデートする際に使用。
npm i next@latest react@latest react-dom@latest eslint-config-next@latest
Googleアナリティクスを追加
ref: 📝【Next.js 13】環境ファイル別で Google Analytics を設定する
ビルド時に「missing "key" prop for element in iterator」
mapの中でのkeyの位置がおかしかった。下記のようにLinkタグではなくpタグに書くのが正解。
{tagPosts.map((post) => ( <p key={post.slug} className="m-0 a"> <Link href={`/blogs/${post.slug}`}>{post.data.title}</Link> </p> ))}
ビルド時に「useSearchParams() should be wrapped in a suspense boundary〜」
useSearchParamsを使ったコンポーネントを<Suspense>で囲えばOK。今回はGoogleAnalyticsで使ってたのでこうした。
import { Suspense } from 'react' <Suspense> <GoogleAnalytics></GoogleAnalytics> </Suspense>
ref: 📝Nextのapp routerでuseSearchParamsを使うとページ全体がクライアントに読み込まれてしまう
シンタックスハイライトを追加
ref: 📝【react-markdown】コードブロックを追加してハイライトする
react-markdownで外部リンクのみ別タブで開く
hrefに「http」が含まれるかどうかで外部リンクかどうかを判別する。
<ReactMarkdown className="blog" remarkPlugins={[remarkGfm]} components={{ code: customCode, a: props => { if (props.href?.startsWith('http')) { return <a href={props.href} target="_blank" rel="noopener noreferrer"> {props.children} </a> } else { return <Link href={props.href!}>{props.children} </Link> } } }} >{content}</ReactMarkdown>