メインコンテンツへ移動

Next.js

無料2020-12-03#Front-End#Solution#SSG#Nextjs#Next SSR#Next SSG#React预渲染

完璧な静的レンダリング/サーバーサイドレンダリングのサポートにより、Next.js は React エコシステムの中で際立っています

一.Next.js の概要

The React Framework for Production

本番環境向けの React フレームワーク(当たり前)。静的レンダリング/サーバーサイドレンダリングの混用、TypeScript のサポート、バンドル最適化、ルートごとのプリロードなど、多くの機能を出荷時から提供しています:

Next.js gives you the best developer experience with all the features you need for production: hybrid static & server rendering, TypeScript support, smart bundling, route pre-fetching, and more. No config needed.

その中でも、完璧な静的レンダリング/サーバーサイドレンダリングのサポートにより、Next.js は React エコシステムの中で際立っています

二。コア機能

Next.js が行っていることは一つだけと言えば、それは**プリレンダリング(Pre-rendering)**です:

By default, Next.js pre-renders every page. This means that Next.js generates HTML for each page in advance, instead of having it all done by client-side JavaScript.

具体的には、プリレンダリングには 2 つの方法があります:

  • SSG(Static Site Generation):Static Generation とも呼ばれ、コンパイル時に静的 HTML を生成

  • SSR(Server-Side Rendering):Server Rendering とも呼ばれ、ユーザーリクエストが届いたときに動的に HTML を生成

SSR と比較して、Next.js がより推奨するのは SSG です。そのパフォーマンス上の利点がより大きいためです(静的コンテンツは CDN にホストでき、パフォーマンス向上は劇的です)。そのため、SSG を優先的に検討し、SSG では満たせない場合(コンパイル時に静的生成できないパーソナライズされたコンテンツなど)にのみ SSR、CSR を検討することをお勧めします

P.S.CSR、SSR などのその他のレンダリングモードについては、前端渲染模式的探索 を参照してください

コアのプリレンダリング機能を中心に、一連の関連サポートが派生しています:

  • ルーティング(ファイル規約、API):複数ページの基礎
  • ページレベルのプリレンダリング、コード分割:当然の流れ
  • 増分静的生成:大量のページ向けのコンパイル時プリレンダリング(つまり静的生成)戦略
  • ルートごとのプリロード:锦上添花
  • 国際化(ルーティングと連携):锦上添花
  • Serverless 関数の統合:锦上添花
  • 自動 polyfill、カスタムheadタグ:おまけ

さらに、一般的なシナリオのサポートも提供しています:

  • 出荷時から使用可能(設定不要)
  • TypeScript
  • CSS module、Sass
  • Fast Refresh(信頼性の高い Hot Reload サポート)
  • ユーザーの実際のデータ収集と分析(ページ読み込みパフォーマンス、エクスペリエンススコアなど)
  • デフォルト最適化付きのImageコンポーネント

三。ルーティングサポート

Next.js は 2 つのルーティングサポートを提供しています:静的ルートと動的ルート

静的ルート

静的ルートはファイル規約によって定義されます。pagesディレクトリ以下のjsファイルはすべてルートとみなされます(各静的ルートは 1 つのページファイルに対応します)。例:

pages/index.js → /
pages/blog/index.js → /blog
pages/blog/first-post.js → /blog/first-post
pages/dashboard/settings/username.js → /dashboard/settings/username

動的ルート

同様に、動的ルートもpagesディレクトリ以下にファイルを作成しますが、ファイル名が少し異なります:

pages/blog/[slug].js → /blog/:slug (/blog/hello-world)
pages/[username]/settings.js → /:username/settings (/foo/settings)
pages/post/[...all].js → /post/* (/post/2020/id/title)

パス内で変化するパラメータはgetStaticPathsによって埋められます:

// pages/posts/[id].js
export async function getStaticPaths() {
  return {
    // 必須で paths という名前、値は配列である必要があります
    paths: [{
      // 各項目はこの形式である必要があります
      params: {
        // id を含む必要があります
        id: 'ssg-ssr'
      }
    },{
      params: {
        id: 'pre-rendering'
      }
    }],
    fallback: false
  }
}

さらにgetStaticPropsに渡してパラメータごとにデータを取得し、ページをレンダリングします:

// pages/posts/[id].js
export async function getStaticProps({ params }) {
  // ルートパラメータに応じて対応するデータを取得
  const postData = await getPostData(params.id)
  return {
    props: {
      postData
    }
  }
}

// ページをレンダリング
export default function Post({ postData }) {
  return (
    <Layout>
      <Head>
        <title>{postData.title}</title>
      </Head>
      <article>
        <h1 className={utilStyles.headingXl}>{postData.title}</h1>
        <div className={utilStyles.lightText}>
          <Date dateString={postData.date} />
        </div>
        <div dangerouslySetInnerHTML={{ __html: postData.contentHtml }} />
      </article>
    </Layout>
  )
}

まずファクトリ page(例:pages/[ルートパラメータ 1]/[ルートパラメータ 2].js)を作成し、次にgetStaticPathsがルートパラメータを埋め、getStaticProps({ params })がパラメータに応じて異なるデータをリクエストし、最後にデータがページコンポーネントに入ってプリレンダリングを開始すると理解できます:

四。SSG サポート

最もシンプルで、同時にパフォーマンスも最適なプリレンダリング方法は静的生成(SSG)で、コンポーネントのレンダリング作業を完全にコンパイル時に前倒しします:

  1. (コンパイル時)データ取得
  2. (コンパイル時)コンポ���ネントをレンダリングし、HTML を生成

生成された HTML 静的リソースを Web サーバーまたは CDN にホストするだけで、React エンジニアリングの利点と Web の極限パフォーマンスを両立できます

まずデータ取得の問題を解決する必要があります。Next.js のアプローチは、ページが依存するデータを一元的に管理することです:

// pages/index.js
export default function Home(props) { ... }

// 静的データを取得
export async function getStaticProps() {
  // Get external data from the file system, API, DB, etc.
  const data = ...

  // The value of the `props` key will be
  //  passed to the `Home` component
  return {
    props: ...
  }
}

ここで、getStaticPropsはサーバー側でのみ実行されます(クライアントバンドルに入ることは決してありません)。返された静的データはページコンポーネント(上記の例ではHome)に渡されます。つまり、getStaticPropsを通じてページが依存するすべてのデータを事前に準備する必要があり、データの準備が整ってからコンポーネントのレンダリングが開始され、HTML が生成されます

P.S.注意:ページのみがgetStaticPropsを通じてそのデータ依存を宣言でき、通常のコンポーネントは許可されていません。そのため、ページ全体が依存するすべてのデータを 1 か所にまとめる必要があります

HTML を生成してレンダリングする部分は、React が提供する SSR API を使用して完了できます

これで、依存するデータが事前に取得可能なページであれば、理論的にはすべて静的 HTML にコンパイルできますが、2 つの問題也随之に発生します:

  • データは変化する可能性があり、すでに生成された静的ページを更新する必要がある

  • データ量が「永遠に」コンパイルし終わらないほど多くなる可能性がある

EC サイトのページを例にすると、大量の商品データをすべて静的ページにコンパイルするのはほぼ不可能です(おそらく 1 世紀ほどかかるでしょう)。すべて生成したとしても、商品情報は頻繁に更新されるため、静的ページを再生成する必要があります:

If your app has a very large number of static pages that depend on data (think: a very large e-commerce site). You want to pre-render all product pages, but then your builds would take forever.

そのため、増分静的再生成(Incremental Static Regeneration)が生まれました

ISR サポート

コンパイル時に列挙しきれない大量のページや更新が必要なシナリオに対して、Next.js はランタイムでの再生成を許可します(ランタイム静的化に相当します):

Incremental Static Regeneration allows you to update existing pages by re-rendering them in the background as traffic comes in.

例:

export async function getStaticProps() {
  const res = await fetch('https://.../posts')
  const posts = await res.json()

  return {
    props: {
      posts,
    },
    // 有効期限を設定し、ISR を有効化
    revalidate: 1, // 秒単位
  }
}

revalidate: 1は、ランタイム(ユーザーリクエストが届いたとき)に静的 HTML を再生成しようと試み、1 秒に最大 1 回再生成することを意味します

ランタイムでの静的生成には時間がかかります(ユーザーリクエストが HTML を待っています)。この過程で 3 つの選択肢があります:

  • fallback: false:フォールバックなし、未生成の静的ページにヒットするルートは直接 404

  • fallback: true:フォールバックあり、未生成の静的ページにヒットするルートはまずフォールバックページを返す(この時propsは空で、通常 loading を表示)。静的 HTML を生成する同時に、フォールバックページが CSR で使用するための JSON も生成され、完了後にブラウザがデータを取得(クライアント側でpropsを埋める)し、完全なページをレンダリング

  • fallback: 'blocking':フォールバックなし、かつユーザーリクエストが新しいページの静的生成が完了するまで待ち続ける(実際には SSR で、レンダリングプロセスはブロッキングだが、完了後に結果 HTML を保持する)

つまり、ルーティング(getStaticPaths)と連携して、未生成のページに対してフォールバックを行います。例:

// pages/index.js
import { useRouter } from 'next/router'

function Post({ post }) {
  const router = useRouter()

  // フォールバックページをレンダリング
  if (router.isFallback) {
    return <div>Loading...</div>
  }

  // Render post...
}

export async function getStaticPaths() {
  return {
    paths: [{ params: { id: '1' } }, { params: { id: '2' } }],
    // (ページレベルの)フォールバック戦略。true は未生成のページに遭遇した場合にフォールバックページを提供し、生成完了後にクライアントが自動的に更新されることを意味
    fallback: true,
  }
}

P.S.詳細は Incremental Static Regeneration、および The fallback key を参照

しかし、すべてのシナリオがコンパイル時に静的生成を快適に行えるわけではありません。典型的には、コンポーネントが依存するデータが動的な場合、コンパイル時に事前にデータを取得することは明らかにできず、静的生成は不可能になります

五。SSR サポート

コンパイル時に静的ページを生成できないシナリオでは、SSR を検討せざるを得ません:

SSG のgetStaticPropsとは異なり、Next.js は SSR 専用のgetServerSideProps(context)を提供しています:

// pages/index.js
export async function getServerSideProps(context) {
  const res = await fetch(`https://...`)
  const data = await res.json()

  if (!data) {
    return {
      notFound: true,
    }
  }

  return {
    props: {}, // ページコンポーネントに props として渡されます
  }
}

同様にデータ取得に使用されますが、getStaticPropsとの最大の違いはリクエストが来るたびに実行されるため、リクエストコンテキストパラメータ(context)を取得できることです

P.S.詳細情報は getServerSideProps (Server-side Rendering) を参照

六。まとめ

プリレンダリングでデータを取得する方法を中心に、Next.js は独特のルーティングサポートと精巧な SSG、SSR サポートを探索しました。それだけでなく、Next.js は両方を得られる混用サポートも提供しています。異なるレンダリングモードを組み合わせるとどれほど強力になるかは、次回の記事で解説します

参考資料

コメント

コメントはまだありません

コメントを書く