ys memos

Blog

next.js再入門した


nextjs

2021/11/07


Web アプリ開発では主に React を使っていて,その知識を活用しながら開発を進められるということで,以前 Next.js と Gatsby で悩んだ結果,本ブログは Next.js で構築している.

ブログ開設時には Next.js を体系的に学ばず,サンプルを改造することにより,最低限の必須事項をピックアップしながら Web ページを構築した.

そのため,Next.js について知らないことが多く,時間の余裕もあるということで,公式チュートリアルを進めながら再入門した.



React を使って Web ページを作る際,ミニマムな SPA を作るだけなら簡単だが,一歩深みに入ると,途端に難しくなる.Code Splitting や Lazy Loading などがそれにあたる.

逆に,それを使わないと初期ロード時に全ての JS がバンドルされ,かなり初期ロードコストが上昇する.

初期ロード時間以上に困るのが,SPA ライブラリにおいてロードされるのは HTML ページではなく JS の塊であり,JS を実行しないとページ内の文章を認識できないず,SEO に弱いという言説も聞くことがある(近年のクローラは JS を実行するという話を聞いたこともある).

その観点において Next.js は Pre-rendering という,全てのページをあらかじめレンダリングしておくという概念を持っており,とあるページに URL からアクセスした場合に初期表示が HTML 内に含まれており,高パフォーマンスで,かつ,SEO に弱くない.


Next.js におけるルーティングは,react-router-dom等のようなソースコード内に記載する形ではなく,プロジェクトのディレクトリで定義する.

この仕組みが,React がライブラリで Next.js がフレームワークであるというの点をよく表していると感じる.

具体的に,pages/*がルーティングにあたる.

例えば,pages/index.jsはページのルート/にあたり,pages/posts/first-post.jsは,/posts/first-postにあたるといったものである.


Next.js において注意しなければ行けない点が,<a>タグを使うと Next.js の強みを活かしきれないという点である.

<a>の代わりに<Link>を使う.これはnext/linkから import できるコンポーネントであり,Next.js 内でのリンクの役割を担う.

<a>を使ってしまうと,ブラウザの機能を使って全く新しいページとしてロードされてしまうため,極力使わないほうが良い.

import Link from 'next/link';

// ミニマム
<Link href="/posts/first-post">text</Link>;

// リンクの色にする
<Link href="/posts/first-post" passHref>
  <a>text</a>
</Link>;

Next.js では,GlobalCSS はpages/_app.jsで読み込む.

ここで言う GlobalCSS とは,html, body, *などのスタイリングや,サイト全体に影響を及ぼすスタイリングのことを指す.

_app.js
import '../styles/global.css'

export default function App({Component, pageProps}) {
  return <Component {...pageProps}/>
}

ページごとにタイトルを設定したり,メタデータを設定したいことがあると思う.そんな時は,<Head>内に<meta>を配置するとよい.<Head>next/headから import 可能.

また,ページごとに別の値が必要になりつつも一定のルールに基づいて記載するため,<Layout>の Props に渡すのが良さそうと感じた.


Pre-rendering は,Next.js の長所の一つで,各ページの初期表示を HTML として表示する概念である.ブラウザの JS を無効にしてページにアクセスしてみるか,curlコマンドで HTML を見ると,初期の HTML がどのようになっているのかを確認することが出来る.

Pre-rendering には,Static Generation と Server-side Rendering (SSR) の2種類があり,ハイブリッドモードも選択可能である.

2種類の Rendering 方法について,基本的には Static Generation で CDN で提供することが推奨されている.

Static Generation には良くない側面もあり,それは,Pre-rendering されたページが必ずしも最新版ではないという点である.その点においては Server-side Rendering は常に最新版のデータを返すというメリットがある(ただし SSR は低速である).

この面に関して言うと,Static Generation には Data を含めるのは必須ではなく,クライアント側で Fetch してもよい.

async関数であるgetStaticPropsを使うと,データを埋め込みながら SG できる.この関数は,ページの表示コンポーネントに対して,静的な Props を注入できる.

export default function Home(props) { ... }

// 戻り値がPageにpassされる.
export async function getStaticProps() {
  const data = ...
  return {
    props: ...
  }
}

// propsを受けることが出来る.
export default function Page(props) {
  return (
    ...
  )
}

このデータ取得は,ページのコンポーネントの Pre-render のビルド時に呼ばれ,最初に解決される(development モードでは,リクエスト毎に呼び出される).

データを Pre-render しなくても良い場合は,Client-side Rendering という方法がある.

これに向くのは,SEO に関係なく頻繁に変更されるデータ,例えばユーザのダッシュボードなどである.


ルーティングに,外部データを反映させる方法.

/post/xxx//post/yyyのように,タイトル等を基にルーティングを生成することが出来る.

そのためには,getStaticPathsを使う.これは,getStaticPropsと同様に,ページのコンポーネントのビルド時に用いられる.

更に言うと,getStaticPathsの結果がgetStaticPropsに注入され,上述の項目につながる.

Dynamic Routes においても,ディレクトリベースのルーティングを用いるが,ここでは,[id].jsのようにすることで,Dynamic Routes を指定できる.

export async function getStaticPaths() {
  const paths = getAllPostIds();
  /*
  [
    {params: {id: 'xxx'}},
    {params: {id: 'yyy'}},
  ]
  */
  return {
    paths,
    fallback: false,
  };
}

export async function getStaticProps({ params }) {
  const postData = await getPostData(params.id);
  return {
    props: {
      postData,
    },
  };
}

getStaticPathsの戻り値がgetStaticPropsに注入されるが,その形式はコメントのとおりである.

getStaticPathsは,development モードでは毎度呼ばれるが,production モードではビルド時のみ呼ばれる.

fallback: falseとすることで,getStaticPathsで返されないページが 404 を返すようになる.

初めて知ったのが,[...id].jsidを受け,params: id: ['a', 'b']と渡すと,/pages/posts/a/bというルートになるようだ.


pages/404.jsとすると,404 で返すページを作れる.


pages/api/xx.jsとすると,/api/xxの API を作ることが出来る.

形式は,

pages/api/hello.js
export default function handler(req, res) {
  // req: HTTP incoming message
  // res: HTTP server response
  ...
  // res.status(200).json({text: 'hello'})
}

また,API も Dynamic API Routes といって,Dynamic な API を提供する.この場合も,pages/api/[pid].jsのようにし,以下のようにpidを受け取ることが出来る.

pages/api/[pid].js
export default function handler(req, res) {
  const { pid } = req.query
  res.end(`Post: ${pid}`)
}

Next.js を始めるか悩んでいた頃に気になっていたことを勝手に答えてしまう.


そんなことはない.Next.js を扱えても,小さい Web アプリは React で作りたい.ただ,Web ページとなると Next.js を使う利点が大きくかんじる.WordPress みたいなノリで CMS 的な使い方をするなら Gatsby という選択も良いと思う.


React の初期ロードが遅く感じる規模の Web アプリ・SEO を考慮した開発・Web ページを作りたい時


具体的な目的があれば,Examplesからピックアップして,改変して目的を目指すのが良いと思う.学ぶことを目的とする場合は,公式チュートリアルをやってみて,興味のある Eample を動かしてみるのが良いと思う.


Next.js やっぱりいいね!!

これまでに学んできたことの再確認,および,分散して学習した内容の体系化ができたので,時間をかけてでも公式チュートリアルをやってみてよかった.


関連タグを探す