🚀

【Next.js和訳】Migrating to Next.js/Migrating from Gatsby

11 min read

この記事について

株式会社 UnReact はプロジェクトの一環としてNext.js ドキュメントの和訳を行っています。

この記事は、Migrating to Next.js/Migrating from Gatsby の記事を和訳したものです。

記事内で使用する画像は、公式ドキュメント内の画像を引用して使用させていただいております。

Migrating from Gatsby

このガイドでは、既存のGatsbyプロジェクトからNext.jsに移行する方法をご紹介します。Next.jsに移行することで、以下のことが可能になります。

他にもいろいろあります。それでは、移行を完了するための一連の手順を説明します。

package.json のアップデートと dependencies

Next.jsに移行するための最初のステップは、package.jsonと依存関係を更新することです。以下の作業を行ってください。

  • Gatsby関連のパッケージをすべて削除します(ただし、reactreact-domは残します)。
  • next をインストールします。
  • Next.js 関連のコマンドを scripts に追加します。一つは next dev で、これは localhost:3000 で開発サーバーを実行します。また、プロダクションビルドを作成・起動するために、next buildnext startも追加しましょう。

以下は、package.json の例です (view diff)。

{
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start"
  },
  "dependencies": {
    "next": "latest",
    "react": "latest",
    "react-dom": "latest"
  }
}

静的資産とコンパイル済み出力

Gatsbyはコンパイル済みの出力にpublicディレクトリを使用しますが、Next.jsはスタティックアセットにpublicディレクトリを使用します。ここでは、移行の手順を紹介します(view diff)。

  • .gitignore から .cache/public を削除し、両方のディレクトリを削除します。
  • Gatsby の static ディレクトリを public にリネームします。
  • .gitignore.next を追加します。

Creating Routes

GatsbyとNextはどちらもpagesディレクトリをサポートしており、file-system based routingを使用しています。ギャツビーのディレクトリはsrc/pagesで、これも Next.jsでサポート されています。

Gatsbyは、gatsby-node.jsの中のcreatePages APIを使って、ダイナミックなルートを作成します。Nextでは、pagesの中でDynamic Routesを使って同じ効果を得ることができます。template`ディレクトリを持つ代わりに、ダイナミックルートファイルの中でReactコンポーネントを使うことができます。例えば、以下のようになります。

  • Gatsby: gatsby-node.js の中の createPages API を使って、各ブログ記事を作成し、src/templates/blog-post.js にテンプレートファイルを用意します。
  • Next: ブログ記事のテンプレートを含む pages/blog/[slug].js を作成します。slugの値は、クエリパラメータでアクセスできます。例えば、ルート /blog/first-post は、クエリオブジェクト { 'slug': 'first-post' }pages/blog/[slug].js に転送します(詳しくはこちら)。

Styling

Gatsbyでは、グローバルなCSSインポートは、gatsby-browser.jsに含まれています。Nextでは、グローバルCSS用にcustom _app.jsを作成する必要があります。移行の際には、CSSインポートを直接コピーして、必要に応じて相対ファイルパスを更新することができます。Next.jsはビルトインCSSサポートを備えています。

リンク

GatsbyのLinkとNext.jsの Linkコンポーネントは、若干異なるAPIを持っています。

Gatsby
import { Link } from 'gatsby'

export default function Home() {
  return <Link to="/blog">blog</Link>
}
Next.js
import Link from 'next/link'

export default function Home() {
  return (
    <Link href="/blog">
      <a>blog</a>
    </Link>
  )
}

すべてのインポートステートメントを更新し、tohref に切り替え、要素の子として <a> タグを追加します。

Data Fetching

GatsbyとNext.jsの最大の違いは、データフェッチの実装方法です。Gatsbyは、アプリケーション全体でデータを取得するためのデフォルトの戦略として、GraphQLを採用しています。Next.jsでは、どのような方法でデータを取得するかを選択することができます(GraphQLはサポートされているオプションの1つです)。

Gatsbyは graphql タグを使って、サイトのページにあるデータを照会します。このデータには、ローカルデータ、リモートデータ、サイトの構成に関する情報などが含まれます。Gatsbyでは、静的なページしか作成できません。Next.jsでは、ページごとに どのような data fetching strategy を選択するかを決めることができます。例えば、getServerSideProps では、サーバーサイドレンダリングを行うことができます。静的なページを生成したい場合は、pageQuery ではなく、ページ内で getStaticProps / getStaticPaths をエクスポートします。例えば、以下のようになります。

src/pages/[slug].js
// remark と remark-html をインストール
import remark from 'remark'
import html from 'remark-html'
import { getPostBySlug, getAllPosts } from '../lib/blog'

export async function getStaticProps({ params }) {
  const post = getPostBySlug(params.slug)
  const markdown = await remark()
    .use(html)
    .process(post.content || '')
  const content = markdown.toString()

  return {
    props: {
      ...post,
      content,
    },
  }
}

export async function getStaticPaths() {
  const posts = getAllPosts()

  return {
    paths: posts.map((post) => {
      return {
        params: {
          slug: post.slug,
        },
      }
    }),
    fallback: false,
  }
}

ファイルシステム(gatsby-source-filesystem)の読み込み、マークダウンファイル(gatsby-transformer-remark)の処理などに使われるGatsbyプラグインをよく見かけます。例えば、人気の高いstarter blogの例では、15個のGatsby固有のパッケージ があります。Nextは異なるアプローチを取ります。共通の機能をフレームワークの中に直接取り込み、外部パッケージとの統合をユーザーが自由にコントロールできるようにしています。例えば、ファイルシステムからの読み込みをプラグインに抽象化するのではなく、getStaticProps / getStaticPaths の中にあるNode.jsのネイティブな fs パッケージを使って、ファイルシステムからの読み込みを行うことができます。

src/lib/blog.js
// gray-matter と date-fns をインストール
import matter from 'gray-matter'
import { parseISO, format } from 'date-fns'
import fs from 'fs'
import { join } from 'path'

// `src/content/blog` の中にmarkdownファイルを追加
const postsDirectory = join(process.cwd(), 'src', 'content', 'blog')

export function getPostBySlug(slug) {
  const realSlug = slug.replace(/\.md$/, '')
  const fullPath = join(postsDirectory, `${realSlug}.md`)
  const fileContents = fs.readFileSync(fullPath, 'utf8')
  const { data, content } = matter(fileContents)
  const date = format(parseISO(data.date), 'MMMM dd, yyyy')

  return { slug: realSlug, frontmatter: { ...data, date }, content }
}

export function getAllPosts() {
  const slugs = fs.readdirSync(postsDirectory)
  const posts = slugs.map((slug) => getPostBySlug(slug))

  return posts
}

画像コンポーネントと画像の最適化

バージョン10.0.0以降、Next.jsには Image Component and Automatic Image Optimization が組み込まれています。

Next.jsのImage Componentであるnext/imageは、HTMLの<img>要素を拡張したもので、現代のウェブ用に進化しています。

自動画像最適化では、ブラウザがサポートしている場合、WebP などの最新フォーマットで画像をリサイズ、最適化して提供することができます。これにより、ビューポートが小さいデバイスに大きな画像を配信することがなくなります。また、Next.jsは将来の画像フォーマットを自動的に採用し、それらのフォーマットをサポートするブラウザに提供することができます。

Gatsby Imageからの移行

Next.jsでは、ビルド時に画像を最適化するのではなく、ユーザーからのリクエストに応じてオンデマンドで画像を最適化します。スタティックサイトジェネレーターやスタティックオンリーのソリューションとは異なり、10枚の画像を出荷する場合でも、1,000万枚の画像を出荷する場合でも、ビルド時間は長くなりません。

つまり、次のような一般的なGatsbyプラグインを削除することができます。

  • gatsby-image
  • gatsby-transformer-sharp
  • gatsby-plugin-sharp

代わりに、組み込みのnext/imageコンポーネントとAutomatic Image Optimizationを使ってください。

import Image from 'next/image'

export default function Home() {
  return (
    <>
      <h1>My Homepage</h1>
      <Image
        src="/me.png"
        alt="Picture of the author"
        width={500}
        height={500}
      />
      <p>Welcome to my homepage!</p>
    </>
  )
}

サイトの構成

Gatsbyでは、サイトのメタデータ(名前、説明など)は gatsby-config.js に格納されます。このメタデータは、GraphQL APIを通じて公開され、pageQuery やコンポーネント内の静的なクエリを通じて消費されます。

Next.jsでは、以下のようなコンフィグファイルを作成することをお勧めします。このファイルをどこかにインポートすれば、サイトのメタデータにアクセスするためにGraphQLを使用する必要がなくなります。

src/config.js
export default {
  title: 'Starter Blog',
  author: {
    name: 'Lee Robinson',
    summary: 'who loves Next.js.',
  },
  description: 'A starter blog converting Gatsby -> Next.',
  social: {
    twitter: 'leeerob',
  },
}

検索エンジン最適化

Gatsbyのほとんどの例では、適切なSEOのためのmetaタグの追加を支援するために react-helmet を使用しています。Next.jsでは、next/head を使用して、<head /> 要素に meta タグを追加します。例えば、Gatsbyを使ったSEOコンポーネントを紹介します。

src/components/seo.js
import { Helmet } from 'react-helmet'

export default function SEO({ description, title, siteTitle }) {
  return (
    <Helmet
      title={title}
      titleTemplate={siteTitle ? `%s | ${siteTitle}` : null}
      meta={[
        {
          name: `description`,
          content: description,
        },
        {
          property: `og:title`,
          content: title,
        },
        {
          property: `og:description`,
          content: description,
        },
        {
          property: `og:type`,
          content: `website`,
        },
        {
          name: `twitter:card`,
          content: `summary`,
        },
        {
          name: `twitter:creator`,
          content: twitter,
        },
        {
          name: `twitter:title`,
          content: title,
        },
        {
          name: `twitter:description`,
          content: description,
        },
      ]}
    />
  )
}

また、Next.jsを使った同じ例では、サイトの設定ファイルからの読み込みも行っています。

src/components/seo.js
import Head from 'next/head'
import config from '../config'

export default function SEO({ description, title }) {
  const siteTitle = config.title

  return (
    <Head>
      <title>{`${title} | ${siteTitle}`}</title>
      <meta name="description" content={description} />
      <meta property="og:type" content="website" />
      <meta property="og:title" content={title} />
      <meta property="og:description" content={description} />
      <meta property="og:site_name" content={siteTitle} />
      <meta property="twitter:card" content="summary" />
      <meta property="twitter:creator" content={config.social.twitter} />
      <meta property="twitter:title" content={title} />
      <meta property="twitter:description" content={description} />
    </Head>
  )
}

さらに詳しく

GatsbyからNext.jsにアプリを移行する方法の詳細については、このプルリクエスト をご覧ください。ご質問がある場合や、このガイドがうまくいかない場合は、GitHub Discussions のコミュニティまでお気軽にお問い合わせください。

Discussion

ログインするとコメントできます