25000cc

Works

Blogs

About

Contact

📝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の発展型
      • 設定した期間ごとにサーバー側で自動で再ビルド
      • ある程度更新頻度が高いようなデータを扱う場合におすすめ

プロジェクトを作成

session
npx create-next-app hogehoge

カレントディレクトリで作る場合は

session
npx 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>