はじめに
Next.jsには英語のチュートリアルがありますが、それを読んで学んだことを自分の備忘録として残しておきます。
Create a Next.js App
Next.jsを使うメリット
Reactでゼロから完全なWebアプリケーションを構築する際に、考慮すべき多くの内容をいい感じにやってくれる
-
Code has to be bundled using a bundler like webpack and transformed using a compiler like Babel.
Webpackなどを用いたコードのバンドル、Babelなどを用いた下位互換バージョンへのコード変換の設定が不要。
-
You need to do production optimizations such as code splitting.
コード分割によるページロードの高速化を自動でやってくれる
-
You might want to statically pre-render some pages for performance and SEO. You might also want to use server-side rendering or client-side rendering.
パフォーマンスとSEOのために、ページの静的なプリレンダリングを簡単に行うことができる。
-
You might have to write some server-side code to connect your React app to your data store.
サーバサイドの処理を簡単に書くことができる。
環境構築
npx create-next-app nextjs-blog --use-npm --example "https://github.com/vercel/next-learn/tree/master/basics/learn-starter"
-
--use-npm
:Yarnがローカルにインストールされている場合でも、パッケージマネージャとしてYarnではなくnpmを使用する -
--example
:サンプル(雛形)を指定してアプリを作成する
Navigate Between Pages
- file system routing
pages
ディレクトリに作ったファイルのファイル名に基づいてルーティングが作成される -
Link
コンポーネントを用いたclient-side navigation
import Link from "next/link";
Welcome to <Link href="/posts/first-post">最初の投稿</Link>
これだけで、クライアントサイドでのルーティング(JavaScriptを使用)が行われ、URLが切り替わってもページの再読み込みが不要なため、ブラウザでの遷移よりも速い。
- code splittingとprefetching
Next.jsでは、表示しているページに必要なものだけを読み込むようにしている。また、Link
コンポーネントを使用すると、Next.jsはバックグラウンドで自動的に遷移先ページのコードをプリフェッチしてくれるため、ページ遷移はほぼ一瞬で完了する。
Assets, Metadata, and CSS
- 静的アセット
Next.jsでは、画像やページのメタデータなどの静的アセットを、public
ディレクトリで管理する。public
ディレクトリ内のファイルは、pages
ディレクトリ内のファイルのように、アプリケーションのルートから参照することができる。 - 遅延読み込み
Image
コンポーネントを使った画像はデフォルトで遅延ロードされる。つまり、画像をビューポートにスクロールすると画像がロードされるため、ビューポート外の画像の場合、ページ速度が低下することはない。 - メタデータ
Reactではライブラリを使わなければページごとにtitle
などのメタデータを設定することができず、SEOに弱い。しかし、Next.jsのHead
コンポーネントを使えばページごとにタグを簡単に設定できる。 - CSS Modules
CSSモジュールのクラス名は自動でユニークな名前に変換されるため、クラス名の競合について心配は無用。また、Next.jsのcode splittingはCSSモジュールでも動作するため、ページごとに最小限のCSSが読み込まれる。これによりバンドルサイズが小さくなる。 - グローバルCSS
CSSモジュールは、コンポーネントレベルのスタイルに便利だが、あるスタイルをすべてのページで使うためグローバルなCSSファイルを読み込むには、Next.jsではpages/_app.js
というファイルを作成する。グローバルCSSをpages/_app.js
以外のファイルからインポートすることはできない。
Pre-rendering and Data Fetching
プリレンダリング
デフォルトでは、Next.jsは全ページをプリレンダリングする。これは、Next.jsがクライアントサイドのJavaScriptで全て行うのではなく、あらかじめ各ページのHTMLを生成しておくことを意味するため、パフォーマンスとSEOの向上につながる。
生成された各HTMLには、そのページに必要最小限のJavaScriptコードだけが関連付けられており、ブラウザがページを読み込んだ際に、そのJavaScriptコードが実行され、ページが完全にインタラクティブとなる。これをhydration
と呼ぶ。
Next.jsでは、ページごとにどのプリレンダリング(SSG or SSR)を行うかを選ぶことができる。
- SSG
ビルド時にHTMLを生成し、ユーザがアクセスしてきた時には既に生成したHTMLを返す。そしてプリレンダリングされたHTMLは各リクエストで再利用される。ページを一度構築してCDNで提供することができ、リクエストごとにサーバがページをレンダリングするよりもはるかに高速であるため、Next.jsではSSGを推奨している。 - SSR
ユーザがリクエストする度に、HTMLを生成して返す。頻繁に更新されるデータを表示するページで、リクエストのたびにページの内容が変わるような場合に使うと良い。
データフェッチ
- 外部からデータを取得しないページ
アプリが本番用にビルドされたとき(next build
を実行したとき)に静的に生成される。 - 外部からデータを取得するページ
Next.jsでは、ページのコンポーネントをexport
すると同時に、getStaticProps()
関数をexport
すると、ビルド時にこの非同期関数が実行されて、この関数の内部で外部からデータを取得し、それをprops
としてページに渡すことができる。
// データフェッチ用の非同期関数のexport
export async function getStaticProps() {
const allPostsData = getSortedPostsData();
return {
props: {
allPostsData,
},
};
}
// ページコンポーネントのexport(propsとして上のgetStaticProps()で取得した外部データを受け取る!)
export default function Home({ allPostsData }) {
return (
<Layout home>
<section className={`${utilStyles.headingMd} ${utilStyles.padding1px}`}>
<h2 className={utilStyles.headingLg}>Blog</h2>
<ul className={utilStyles.list}>
{allPostsData.map(({ id, date, title }) => (
<li className={utilStyles.listItem} key={id}>
{title}
<br />
{id}
<br />
{date}
</li>
))}
</ul>
</section>
</Layout>
);
}
リクエストがある度にデータをフェッチしたい場合(SSR)は、getStaticProps()
の代わりにgetServerSideProps(context)
を使う。
また、データをプリレンダリングする必要がない場合は、SSGで生成したページで、リクエストが来た際にクライアントサイドのJavaScriptでデータをフェッチする(CSR)こともできる。CSRはユーザのダッシュボードのページなど、ユーザ固有のプライベートなページでSEOを考慮するが必要ないためプリレンダリングされる必要はないが、ページ内のデータは頻繁に更新されるためにリクエスト時にデータを取得する必要があるようなページで有効。
Dynamic Routes
Next.jsでは、pages/posts/[id].js
のように、ファイル名が[]
で囲まれたページはdynamic routes
となる。
pages/posts/[id].js
というファイルを作成すると、
-> https://next-tutorial.com/post/id1
-> https://next-tutorial.com/post/id2
-> https://next-tutorial.com/post/id3
のような動的ルーティングになる。
[id].js
に必要なもの
- ページにレンダリングするReactコンポーネント
- idの配列を返す
getStaticPaths()
関数 -
getStaticPaths()
で返ってきたidに紐付くデータをフェッチするためのgetStaticProps()
関数
import Layout from "../../components/layout";
import { getAllPostIds, getPostData } from "../../lib/posts";
export default function Post({ postData }) {
return (
<Layout>
{postData.title}
<br />
{postData.id}
<br />
{postData.date}
</Layout>
);
}
export async function getStaticPaths() {
const paths = getAllPostIds();
return {
paths,
fallback: false,
};
}
export async function getStaticProps({ params }) {
const postData = getPostData(params.id);
return {
props: {
postData,
},
};
}
API Routes
Next.jsでは、APIエンドポイントをNode.jsのサーバーレス関数として簡単に作成することができる。
// `req` is an instance of http.IncomingMessage, plus some pre-built middlewares.
// `res` is an instance of http.ServerResponse, plus some helper functions.
export default function handler(req, res) {
res.status(200).json({ text: 'Hello' })
}
getStaticProps
とgetStaticPaths
はサーバサイドでのみ実行され、クライアントサイドでは実行されず、ブラウザ用のJSバンドルに含まれることもないため、これらの関数からAPI Route
を取得することはできない。その代わりに、サーバサイドのコードを直接これらの関数内に書く必要がある。
Deploying Your Next.js App
GitHubアカウントでVercelにサインアップしてリポジトリをimportするだけで、簡単にNext.jsのアプリをデプロイできる。
もちろんVerel以外にも、Node.jsをサポートしているホスティングプロバイダにデプロイすることができる。buildスクリプトを一度実行すると、.next
フォルダに本番アプリケーションがビルドされる。ビルドした後、startスクリプトを実行すると、SSGやSSR、API RoutesをサポートするNode.jsサーバが起動される。
TypeScript
TypeScriptも簡単に導入できる。
export default function Home({
allPostsData
}: {
allPostsData: {
date: string
title: string
id: string
}[]
}) {
return (
...
)
}