Next.jsのチュートリアルから学んだこと

# はじめに
Next.jsには英語のチュートリアルがありますが、それを読んで学んだことを自分の備忘録として残しておきます。

https://nextjs.org/learn/foundations/about-nextjs?utm_source=next-site&utm_medium=homepage-cta&utm_campaign=next-website

# 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.

　　　サーバサイドの処理を簡単に書くことができる。

## 環境構築

```shell
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

```javascript
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`コンポーネントを使えばページごとに<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`としてページに渡すことができる。

```javascript
// データフェッチ用の非同期関数の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()`関数

```javascript
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のサーバーレス関数として簡単に作成することができる。

```javascript
// `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

https://vercel.com/signup

GitHubアカウントでVercelにサインアップしてリポジトリをimportするだけで、簡単にNext.jsのアプリをデプロイできる。
![deploy-to-vercel.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1504894/fdd7adec-b0c0-917b-2c22-5a9cf7f5f31f.png)

もちろんVerel以外にも、Node.jsをサポートしているホスティングプロバイダにデプロイすることができる。buildスクリプトを一度実行すると、`.next`フォルダに本番アプリケーションがビルドされる。ビルドした後、startスクリプトを実行すると、SSGやSSR、API RoutesをサポートするNode.jsサーバが起動される。

# TypeScript
TypeScriptも簡単に導入できる。

```tsx
export default function Home({
  allPostsData
}: {
  allPostsData: {
    date: string
    title: string
    id: string
  }[]
}) {
  return (
    ...
  )
}
```