以下の記事を読んだので、久しぶりに React を触りたくて、Next 13.5 をインストールしてみたのでメモ。
私は素の React しか経験がなく、Vue/Nuxt との差分をざっくり理解したいところ。とりあえず英語の Next.js 13.5 リリースノートと、日本語化いただいているひとつ前の Next.js 13.4 全文和訳 と 補足説明 で状況を把握。必要に応じて公式コンテンツを参照していく予定。
とりあえずインストール
最近、あまり更新できていないので、現在の環境はこちら。
公式サイトの installation に従いインストール。選択は全てデフォルトのままです。
npm のバージョンが古いと警告が出たので、指示に従って 10.2.0 上げておきました。すると nodejs が古いと言われたので 18.18.1 に上げました。サボっていると駄目ですねぇ。
とりあえず設定は見ておく。
{
"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
で。
ブラウザ表示がこちら。
例によって init ブランチとして、この初期化直後の状態を Git に置いておきます。
サンプルを眺めてみる
初期状態でのサンプルを見ると、表示される要素は以下のようです。
App Router を選択したので、app
フォルダ配下がルーティング対象になるようです。layout.tsx
はレイアウト定義、そして page.tsx
は index.html 的な各階層のインデックス扱いされる特殊なファイル名らしい。
public
フォルダの中身は / 直下に配置されるようですね。画像など静的なファイル置き場ですね。favicon.ico や globals.css がこちらに配置されていないのが、ちょっと興味深いです。
Tailwind CSS を扱いやすくするため、VSCode に Tailwind CSS IntelliSense プラグインを導入しておきます。
ビルドしてみる
開発モードで動作確認できたので、ビルドしてみます。
問題なくビルドされたので、動作させてみます。
さきほどと同じWeb画面が表示されたので、画面キャプチャは省略。
Zen ページを作成してみる
お馴染み?の Zen API を利用した簡単なサンプルページを作成してみます。まず App Router の場合はフォルダごとにレイアウトを持てるようなので、元からあるファイルを少し修正して以下のレイアウトを作成。Meta データ書き換えて、h1タグ追加しただけです。
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 をフェッチして、それを表示するだけのシンプルな内容。
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>
)
}
ブラウザで表示した結果がこちら。サーバー側でキャッシュしているのか、リロードで文言が変わらず、スーパーリロードで更新されます。
ブラウザ側は localhost のみのアクセスで、Zen APIへのアクセスの跡はありません。
app.tsx
内の res.text()
部分を res.json()
と変えるとサーバー側のコンソールに以下のエラーが表示されたので、Zen API へのアクセスと結果の変換はサーバーサイドで実行されているようですね。
なかなか良い感じです。
変更は init-zen ブランチとしてpushしておきました。initとの差分 をご参照ください。
ローディング制御をいれてみる
Suspense
機能を利用したローディング制御を実装してみます。まずは以下のようにZen API呼び出しを部品化します。動作をみるため2秒のsleepをいれています。
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>
);
}
そしてこの部品を使って以下のように記述。コンポーネント呼び出しの指定がよくわからなかったので、とりあえず相対パスにしています。
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>
)
}
実行してみると、以下のようにローディング中の表示がされていることがわかります。
変更は init-zen-component ブランチとしてpushしておきました。init-zenとの差分 をご参照ください。
というわけで
Next 13.5 をインストールして簡単に試してみました。個人的には Nuxt よりも好きかもしれません。React を思い出しつつ、公式ドキュメント読みつつ、もう少し遊んでみようとおもいます。
ではでは。