【Next.js和訳】Migrating to Next.js/Migrating from Gatsby
この記事について
この記事は、Migrating to Next.js/Migrating from Gatsby の記事を和訳したものです。
記事内で使用する画像は、公式ドキュメント内の画像を引用して使用させていただいております。
Migrating from Gatsby
このガイドでは、既存の Gatsby プロジェクトから Next.js に移行する方法をご紹介します。Next.js に移行することで、以下のことが可能になります。
- ページごとに、どのようなdata fetching戦略をとるかを選択する。
- インクリメンタル・スタティック・リジェネレーションを使用して、トラフィックが入ってきたときにバックグラウンドで再レンダリングを行い、既存のページを更新する。
- API Routesを使用します。
他にもいろいろあります。それでは、移行を完了するための一連の手順を説明します。
package.json
のアップデートと dependencies
Next.js に移行するための最初のステップは、package.json
と依存関係を更新することです。以下の作業を行ってください。
- Gatsby 関連のパッケージをすべて削除します(ただし、
react
とreact-dom
は残します)。 -
next
をインストールします。 - Next.js 関連のコマンドを
scripts
に追加します。一つはnext dev
で、これはlocalhost:3000
で開発サーバーを実行します。また、プロダクションビルドを作成・起動するために、next build
とnext 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 を持っています。
import { Link } from 'gatsby'
export default function Home() {
return <Link to="/blog">blog</Link>
}
import Link from 'next/link'
export default function Home() {
return (
<Link href="/blog">
<a>blog</a>
</Link>
)
}
すべてのインポートステートメントを更新し、to
を href
に切り替え、要素の子として <a>
タグを追加します。
Data Fetching
Gatsby と Next.js の最大の違いは、データフェッチの実装方法です。Gatsby は、アプリケーション全体でデータを取得するためのデフォルトの戦略として、GraphQL を採用しています。Next.js では、どのような方法でデータを取得するかを選択することができます(GraphQL はサポートされているオプションの 1 つです)。
Gatsby は graphql
タグを使って、サイトのページにあるデータを照会します。このデータには、ローカルデータ、リモートデータ、サイトの構成に関する情報などが含まれます。Gatsby では、静的なページしか作成できません。Next.js では、ページごとに どのような data fetching strategy を選択するかを決めることができます。例えば、getServerSideProps
では、サーバーサイドレンダリングを行うことができます。静的なページを生成したい場合は、pageQuery
ではなく、ページ内で getStaticProps
/ getStaticPaths
をエクスポートします。例えば、以下のようになります。
// 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
パッケージを使って、ファイルシステムからの読み込みを行うことができます。
// 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 を使用する必要がなくなります。
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 コンポーネントを紹介します。
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 を使った同じ例では、サイトの設定ファイルからの読み込みも行っています。
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