🚀

【Next.js和訳】Upgrade Guide

10 min read

この記事について

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

この記事は、Upgrade Guideの記事を和訳したものです。

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

Upgrade Guide

バージョン 10 から 11 へのアップグレード

React のバージョンを最新にアップグレード

ほとんどのアプリケーションは、すでに最新の React を使用しています。Next.js 11 では、最小の React バージョンが 17.0.2 に更新されています。

アップグレードするには、以下のコマンドを実行します。

npm install react@latest react-dom@latest

yarnを使う場合は、以下のコマンドを実行してください。

yarn add react@latest react-dom@latest

Next.js を最新のバージョンにアップグレードする

アップグレードするには、ターミナルで以下のコマンドを実行します。

npm install next@latest

または

yarn add next@latest

Webpack 5

Webpack 5 は、すべての Next.js アプリケーションのデフォルトとなっています。カスタムの webpack 設定を行っていない場合、アプリケーションはすでに webpack 5 を使用しています。カスタムの webpack を設定している場合は、Next.js の webpack 5 のドキュメントを参照してアップグレードすることができます。

distDirのクリーニングがデフォルトに

Next.js のキャッシュを除き、ビルドの出力ディレクトリ(デフォルトでは.next)がデフォルトでクリアされるようになりました。詳細については、cleaning distDir RFCを参照してください。

アプリケーションがこの動作に依存していた場合は、next.config.jscleanDistDir: falseフラグを追加することで、新しいデフォルトの動作を無効にすることができます。

PORTnext devnext startでサポート

Next.js 11 では、アプリケーションを実行するためのポートを設定する環境変数PORTがサポートされています。これまで通り-p/--portの使用が推奨されますが、-pの使用が禁止されていた場合は、代替手段としてPORTを使用することができます。

PORT=4000 next start

next.config.jsをカスタマイズして画像を取り込む

Next.js 11 では、next/imageによる静的な画像のインポートをサポートしています。この新機能は、画像のインポートを処理できるかどうかにかかっています。以前にnext-imagesまたはnext-optimized-imagesパッケージを追加していた場合は、next/imageを使った新しい組み込みサポートに移行するか、この機能を無効にすることができます。

module.exports = {
  images: {
    disableStaticImages: true,
  },
}

pages/_app.jsからsuper.componentDidCatch()を削除

next/appコンポーネントのcomponentDidCatchは、Next.js 9 以降、不要になったため非推奨となり、Next.js 11 では削除されました。

pages/_app.jsにカスタムのcomponentDidCatchメソッドがある場合、super.componentDidCatchは不要になったので削除できます。

pages/_app.jsからContainerを削除

このエクスポートは、Next.js 9 以降、不要になったため非推奨となっており、それ以降は開発中に警告が出てダメになっていました。Next.js 11 では削除されています。

pages/_app.jsnext/appからContainerをインポートしている場合は、Containerが削除されているので削除できます。詳しくはドキュメントをご覧ください。

ページコンポーネントからprops.urlの使用を削除する

このプロパティは Next.js 4 から非推奨となり、開発中に警告が表示されるようになりました。getStaticProps / getServerSidePropsの導入により、これらのメソッドはprops.urlの使用を禁止しました。Next.js 11 では、このプロパティは完全に削除されました。

詳しくはドキュメントをご覧ください。

next/imageunsizedプロパティの削除

next/imageunsizedプロパティは、Next.js 10.0.1 で非推奨となりました。代わりにlayout="fill"を使用することができます。Next.js 11 ではunsizedが削除されました。

next/dynamicmodulesプロパティの削除

next/dynamicmodulesrenderオプションは、Next.js 9.5 から非推奨となり、非推奨であることを示す警告が表示されるようになりました。これは、next/dynamicの API サーフェスをReact.lazyに近づけるために行われたものです。Next.js 11 では、modulesrenderオプションが削除されました。

このオプションは Next.js 8 以降のドキュメントでは言及されていないので、アプリケーションがこのオプションを使用している可能性は低いでしょう。

modulesrenderを使用するアプリケーションの場合は、ドキュメントを参照してください。

Head.rewindの削除

Head.rewindは Next.js 9.5 から廃止されていましたが、Next.js 11 で削除されました。Next.js 11 で削除されました。Head.rewindを使用しているアプリケーションは安全に削除できます。

Moment.js のロケールがデフォルトで除外されている

Moment.js には、多くのロケールの翻訳がデフォルトで含まれています。Next.js では、Moment.js を使用するアプリケーションのバンドルサイズを最適化するために、これらのロケールをデフォルトで自動的に除外するようになりました。

特定のロケールを読み込むには、次のスニペットを使用します。

import moment from "moment"
import "moment/locale/ja"

moment.locale("ja")

この新しいデフォルトの動作を望まない場合は、next.config.jsexcludeDefaultMomentLocales: falseを追加することで無効にすることができます。

router.eventsの使用方法の更新

レンダリング時にrouter.eventsにアクセスしている場合、Next.js 11 ではプリレンダリング時にrouter.eventsが提供されなくなりました。useEffectrouter.eventsにアクセスしていることを確認してください。

useEffect(() => {
  const handleRouteChange = (url, { shallow }) => {
    console.log(`App is changing to ${url} ${shallow ? "with" : "without"} shallow routing`)
  }

  router.events.on("routeChangeStart", handleRouteChange)

  // コンポーネントがアンマウントされた場合は、`off`メソッドでイベントの配信を停止します。
  return () => {
    router.events.off("routeChangeStart", handleRouteChange)
  }
}, [router])

公開されていない内部プロパティであるrouter.router.eventsを使用している場合は、必ずrouter.eventsも使用してください。

React 16 から 17 へ

React 17 では、新しいJSX Transformが導入され、長い間使われてきた Next.js の機能が広く React のエコシステムに導入されました。JSX を使用する際にimport React from 'react'を書く必要はありません。React 17 を使用する際、Next.js は自動的にこの新しいトランスフォームを使用します。このトランスフォームはReactの変数をグローバルにしません。これは以前の Next.js の実装では意図しない副作用でした。誤ってReactをインポートせずに使用してしまったケースを自動的に修正するcodemod が用意されています

バージョン 9 から 10 へのアップグレード

バージョン 9 から 10 への変更点はありません。

アップグレードするには、以下のコマンドを実行してください。

npm install next@10

yarnを使う場合は、以下のコマンドを実行してください。

yarn add next@10

バージョン 8 から 9 へのアップグレード

プリアンブル

Vercel でのプロダクション・デプロイメント

以前、vercel.jsonファイルにダイナミックルート用のroutesを設定していた場合、Next.js 9 の新機能であるダイナミックルートを活用する際に、これらのルールを削除することができます。

Next.js 9 のダイナミックルートはVercel上で自動的に設定され、vercel.json のカスタマイズは必要ありません。

Dynamic Routing についてはこちらをご覧ください。

カスタムのチェック(pages/_app.js

Custom <App>の例をコピーした場合は、getInitialPropsを削除できるかもしれません。

Next.js の新機能を活用するには、可能な限りpages/_app.jsからgetInitialPropsを削除することが重要です。

次のgetInitialPropsは何もしないので、削除してもよいでしょう。

class MyApp extends App {
  // 私を外してください、私は何もしません
  static async getInitialProps({ Component, ctx }) {
    let pageProps = {}

    if (Component.getInitialProps) {
      pageProps = await Component.getInitialProps(ctx)
    }

    return { pageProps }
  }

  render() {
    // ... など
  }
}

変更点

@zeit/next-typescriptはもう必要ありません

Next.js は、@zeit/next-typescriptの使用を無視し、削除するように警告するようになりました。このプラグインをnext.config.jsから削除してください。

カスタム.babelrc(もしあれば)から@zeit/next-typescript/babelへの参照を削除してください。

fork-ts-checker-webpack-pluginの使用もあなたのnext.config.jsから削除してください。

TypeScript Definitions はnextパッケージで公開されているので、衝突するため@types/nextをアンインストールする必要があります。

以下のタイプが異なります。

このリストは、アップグレードを支援するためにコミュニティによって作成されたものです。もし他の違いを見つけた場合は、他のユーザーを支援するために、このリストにプルリクエストを送信してください。

以下から

import { NextContext } from "next"
import { NextAppContext, DefaultAppIProps } from "next/app"
import { NextDocumentContext, DefaultDocumentIProps } from "next/document"

以下へ

import { NextPageContext } from "next"
import { AppContext, AppInitialProps } from "next/app"
import { DocumentContext, DocumentInitialProps } from "next/document"

configキーがページ上でエクスポートされるようになりました

configというカスタム変数をページからエクスポートすることはできなくなりました(export { config } / export const config ...など)。このエクスポートされた変数は、Optin AMP や API Route の機能など、ページレベルの Next.js の設定を指定するために使われるようになりました。

Next.js を使用しないconfigのエクスポートは、別の名前に変更する必要があります。

next/dynamicは、ロード中にデフォルトで"loading..."を表示しなくなりました

ダイナミックコンポーネントは、デフォルトではロード中に何も表示しません。loadingプロパティを設定することで、この動作をカスタマイズすることができます。

import dynamic from "next/dynamic"

const DynamicComponentWithCustomLoading = dynamic(() => import("../components/hello2"), {
  loading: () => <p>Loading</p>,
})

withAmpが削除され、エクスポートされる設定オブジェクトが採用されました

Next.js にはページレベルの設定という概念があるため、一貫性を保つために高次コンポーネントであるwithAmpが削除されました。

この変更は、Next.js プロジェクトのルートで以下のコマンドを実行することで、自動的に移行することができます。

curl -L https://github.com/vercel/next-codemod/archive/master.tar.gz | tar -xz --strip=2 next-codemod-master/transforms/withamp-to-config.js npx jscodeshift -t ./withamp-to-config.js pages/**/*.js

この移行を手動で行うか、または codemod が生成するものを確認するには、以下を参照してください。

Before

import { withAmp } from 'next/amp'

function Home() {
  return <h1>My AMP Page</h1>
}

export default withAmp(Home)
// または
export default withAmp(Home, { hybrid: true })

After

export default function Home() {
  return <h1>My AMP Page</h1>
}

export const config = {
  amp: true,
  // または
  amp: "hybrid",
}

next exportでページをindex.htmlとしてエクスポートしなくなる

以前は、pages/about.jsをエクスポートするとout/about/index.htmlが出力されていました。この動作は、out/about.htmlとなるように変更されました。

以前の動作に戻すには、次の内容のnext.config.jsを作成します。

next.config.js
module.exports = {
  trailingSlash: true,
}

./pages/api/の扱いの変更

./pages/api/のページはAPI ルートとみなされます。このディレクトリのページには、クライアント側のバンドルは含まれなくなります。

非推奨の機能

next/dynamicでは、複数のモジュールを一度に読み込む機能が非推奨となりました。

next/dynamicでは、React の実装(React.lazySuspense)に近づけるために、複数のモジュールを一度にロードする機能が非推奨となりました。

この動作に依存しているコードを更新することは、比較的簡単です アプリケーションを移行する際の参考として、before/after の例を用意しました。

Before

import dynamic from "next/dynamic"

const HelloBundle = dynamic({
  modules: () => {
    const components = {
      Hello1: () => import("../components/hello1").then((m) => m.default),
      Hello2: () => import("../components/hello2").then((m) => m.default),
    }

    return components
  },
  render: (props, { Hello1, Hello2 }) => (
    <div>
      <h1>{props.title}</h1>
      <Hello1 />
      <Hello2 />
    </div>
  ),
})

function DynamicBundle() {
  return <HelloBundle title="Dynamic Bundle" />
}

export default DynamicBundle

After

import dynamic from "next/dynamic"

const Hello1 = dynamic(() => import("../components/hello1"))
const Hello2 = dynamic(() => import("../components/hello2"))

function HelloBundle({ title }) {
  return (
    <div>
      <h1>{title}</h1>
      <Hello1 />
      <Hello2 />
    </div>
  )
}

function DynamicBundle() {
  return <HelloBundle title="Dynamic Bundle" />
}

export default DynamicBundle

Discussion

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