Next.js 13.5.4 をインストールして簡単に試してみた

以下の記事を読んだので、久しぶりに React を触りたくて、Next 13.5 をインストールしてみたのでメモ。

* [Reactベースのフレームワーク「Next.js 13.5」正式リリース。使用メモリ40％削減、ローカルサーバ起動の高速化など性能向上](https://www.publickey1.jp/blog/23/reactnextjs_13540.html)

私は素の React しか経験がなく、Vue/Nuxt との差分をざっくり理解したいところ。とりあえず英語の [Next.js 13.5](https://nextjs.org/blog/next-13-5) リリースノートと、日本語化いただいているひとつ前の [Next.js 13.4 全文和訳 と 補足説明](https://zenn.dev/h_tatsuru/articles/nextjs_update_translate) で状況を把握。必要に応じて公式コンテンツを参照していく予定。

# とりあえずインストール

最近、あまり更新できていないので、現在の環境はこちら。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/69f7c699-507e-b53c-30b1-9488ac848f15.png)
公式サイトの [installation](https://nextjs.org/docs/getting-started/installation) に従いインストール。選択は全てデフォルトのままです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/9dbf64a7-ccc7-ec71-2a1f-952e2fd8a5b9.png)
npm のバージョンが古いと警告が出たので、指示に従って 10.2.0 上げておきました。すると nodejs が古いと言われたので 18.18.1 に上げました。サボっていると駄目ですねぇ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/88e831cc-4f55-9489-096e-5a9a72152824.png)
とりあえず設定は見ておく。
```json:package.json
{
  "name": "next13.5",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "react": "^18",
    "react-dom": "^18",
    "next": "13.5.4"
  },
  "devDependencies": {
    "typescript": "^5",
    "@types/node": "^20",
    "@types/react": "^18",
    "@types/react-dom": "^18",
    "autoprefixer": "^10",
    "postcss": "^8",
    "tailwindcss": "^3",
    "eslint": "^8",
    "eslint-config-next": "13.5.4"
  }
}
```

# まずは実行してみる

動作確認は `npm run dev` で。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/123d8053-a962-b239-e6d8-6afad3cf0406.png)
ブラウザ表示がこちら。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/97561680-77cd-2c24-846c-34f16213f659.png)
例によって [init ブランチ](https://github.com/yamachan/next13.5/tree/init)として、この初期化直後の状態を Git に置いておきます。

# サンプルを眺めてみる

初期状態でのサンプルを見ると、表示される要素は以下のようです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/68047786-db27-ee04-ca75-1bc21da9b585.png)
App Router を選択したので、`app` フォルダ配下がルーティング対象になるようです。`layout.tsx` はレイアウト定義、そして `page.tsx` は index.html 的な各階層のインデックス扱いされる特殊なファイル名らしい。

`public` フォルダの中身は / 直下に配置されるようですね。画像など静的なファイル置き場ですね。favicon.ico や globals.css がこちらに配置されていないのが、ちょっと興味深いです。

Tailwind CSS を扱いやすくするため、VSCode に [Tailwind CSS IntelliSense](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss) プラグインを導入しておきます。

# ビルドしてみる

開発モードで動作確認できたので、ビルドしてみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/7936ceab-1cb5-8f8f-8f9e-0c95a6b912f5.png)
問題なくビルドされたので、動作させてみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/1f39f5fd-7601-8027-3db2-b2f471edb47f.png)
さきほどと同じWeb画面が表示されたので、画面キャプチャは省略。

# Zen ページを作成してみる

お馴染み？の [Zen API](https://api.github.com/zen) を利用した簡単なサンプルページを作成してみます。まず App Router の場合はフォルダごとにレイアウトを持てるようなので、元からあるファイルを少し修正して以下のレイアウトを作成。Meta データ書き換えて、h1タグ追加しただけです。

```tsx:app/zen/layout.tsx
import '../globals.css'
import type { Metadata } from 'next'
import { Inter } from 'next/font/google'

const inter = Inter({ subsets: ['latin'] })

export const metadata: Metadata = {
  title: 'Zen test pages',
  description: 'Test pages with Zen API',
}

export default function ZenLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body className={inter.className}>
        <h1>Zen test page</h1>
        {children}
      </body>
    </html>
  )
}
```

フォルダのトップページは `page.tsx` とするお作法のようなので作成。Zen API をフェッチして、それを表示するだけのシンプルな内容。

```tsx:app/zen/page.tsx
export default async function Zen() {
    const res = await fetch('https://api.github.com/zen')  
    const data = await res.text()
    return (
      <main>
        <p className="p-4">{data}</p>
      </main>
  )
}
```
ブラウザで表示した結果がこちら。サーバー側でキャッシュしているのか、リロードで文言が変わらず、スーパーリロードで更新されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/3de13758-bb3e-42b3-65b5-2129d665499c.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/9be6041c-da28-8564-f5ae-1bad7feaade6.png)
ブラウザ側は localhost のみのアクセスで、Zen APIへのアクセスの跡はありません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/c9fb5520-dfad-c476-8805-faeab51561aa.png)
`app.tsx` 内の `res.text()` 部分を `res.json()` と変えるとサーバー側のコンソールに以下のエラーが表示されたので、Zen API へのアクセスと結果の変換はサーバーサイドで実行されているようですね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/9823d795-ddac-15ea-dc10-de052265a9ca.png)
なかなか良い感じです。

変更は [init-zen](https://github.com/yamachan/next13.5/tree/init-zen) ブランチとしてpushしておきました。[initとの差分](https://github.com/yamachan/next13.5/compare/init...init-zen) をご参照ください。

# ローディング制御をいれてみる

`Suspense` 機能を利用したローディング制御を実装してみます。まずは以下のようにZen API呼び出しを部品化します。動作をみるため2秒のsleepをいれています。

```tsx:app/components/ZenComponent.tsx
function sleep(ms: number) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

export default async function ZenComponent() {
  const res = await fetch('https://api.github.com/zen')
  await sleep(2000)
  const data = await res.text()
  return (
    <span>{data}</span>
  );
}
```

そしてこの部品を使って以下のように記述。コンポーネント呼び出しの指定がよくわからなかったので、とりあえず相対パスにしています。

```tsx:app/zen/page.tsx
import { Suspense } from 'react';
import ZenComponent from '../components/ZenComponent'

export default async function Zen() {
    return (
        <main>
            <p className="p-4">
                <Suspense fallback={<span>Loading...</span>}>
                    <ZenComponent />
                </Suspense>
            </p>
        </main>
    )
}
```

実行してみると、以下のようにローディング中の表示がされていることがわかります。
![Animation4.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193519/ed200704-f2d3-cee1-046b-6b1e51c8424a.gif)

変更は [init-zen-component](https://github.com/yamachan/next13.5/tree/init-zen-component) ブランチとしてpushしておきました。[init-zenとの差分](https://github.com/yamachan/next13.5/compare/init-zen...init-zen-component) をご参照ください。

# というわけで

Next 13.5 をインストールして簡単に試してみました。個人的には Nuxt よりも好きかもしれません。React を思い出しつつ、公式ドキュメント読みつつ、もう少し遊んでみようとおもいます。

ではでは。