Learn Next.js Getting Started | Work Through
※初めてのzenn投稿です。だらだらとコメントを追記してくスタイルで進めています
いろんなテンプレートがある(ただのめも)
pnpmをインストール
npm install -g pnpm
結果:OK
added 1 package in 6s
プロジェクト作成
今回はnext-official-app配下に作成する
npx create-next-app@latest nextjs-dashboard --example "https://github.com/vercel/next-learn/tree/main/dashboard/starter-example" --use-pnpm
結果:OK:Successと表示されている
350MBくらい
フォルダ構成を確認
以下ファイルを確認
app\lib\definitions.ts
データベースから返される型を「手動」で定義しているファイルとのこと
プロジェクトのパッケージをインストール
pnpm i
結果:OK
開発サーバーを起動
pnpm dev
結果:OK
localhost:3000にアクセス
第2章
グローバルスタイルをインポート
+import '@/app/ui/global.css';
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
);
}
結果:OK(スタイルが変わった)
tailwindを利用する
export default function Page() {
return (
<main className="flex min-h-screen flex-col p-6">
<div className="flex h-20 shrink-0 items-end rounded-lg bg-blue-500 p-4 md:h-52">
{/* <AcmeLogo /> */}
</div>
<div className="mt-4 flex grow flex-col gap-4 md:flex-row">
<div className="flex flex-col justify-center gap-6 rounded-lg bg-gray-50 px-6 py-10 md:w-2/5 md:px-20">
+ <div className="relative w-0 h-0 border-l-[15px] border-r-[15px] border-b-[26px] border-l-transparent border-r-transparent border-b-black"/>
<p className={`text-xl text-gray-800 md:text-3xl md:leading-normal`}>
<strong>Welcome to Acme.</strong> This is the example for the{' '}
<a href="https://nextjs.org/learn/" className="text-blue-500">
Next.js Learn Course
</a>
, brought to you by Vercel.
</p>
・・・
結果:OK(三角形が表示された)
tailwindからCSSモジュールに置き換える
+.shape {
+ height: 0;
+ width: 0;
+ border-bottom: 30px solid black;
+ border-left: 20px solid transparent;
+ border-right: 20px solid transparent;
+ }
import Link from 'next/link';
+ import styles from '@/app/ui/home.module.css';
・・・
export default function Page() {
return (
<main className="flex min-h-screen flex-col p-6">
<div className="flex h-20 shrink-0 items-end rounded-lg bg-blue-500 p-4 md:h-52">
{/* <AcmeLogo /> */}
</div>
<div className="mt-4 flex grow flex-col gap-4 md:flex-row">
<div className="flex flex-col justify-center gap-6 rounded-lg bg-gray-50 px-6 py-10 md:w-2/5 md:px-20">
- <div className="relative w-0 h-0 border-l-[15px] border-r-[15px] border-b-[26px] border-l-transparent border-r-transparent border-b-black"/>
+ <div className={styles.shape} />
<p className={`text-xl text-gray-800 md:text-3xl md:leading-normal`}>
<strong>Welcome to Acme.</strong> This is the example for the{' '}
<a href="https://nextjs.org/learn/" className="text-blue-500">
Next.js Learn Course
</a>
, brought to you by Vercel.
</p>
・・・
結果:OK(表示が変わらないことを確認できた)
CSSモジュール
以下とのこと、どのようなクラス名が生成されているか見てみる
CSS モジュールは各コンポーネントに一意のクラス名を作成するため、スタイルの衝突を心配する必要はありません
結果:OK
ブラウザで確認すると以下のようなクラス名が生成されてた
home-module__7sF-sa__shape
clsx を認識する。以下との説明
クラス名を簡単に切り替えられるライブラリ
詳細は、外部リンクに委ねられていた
所感:便利そう
3章
フォントのデリバリを最適化する仕組みが備わっているとのこと
ビルド時にフォント ファイルをダウンロードし、他の静的アセットとともにホストします。つまり、ユーザーがアプリケーションにアクセスしたときに、パフォーマンスに影響を与えるフォントの追加ネットワーク リクエストは発生しません。
プライマリフォントの追加
+import { Inter } from 'next/font/google';
+export const inter = Inter({ subsets: ['latin'] });
import '@/app/ui/global.css';
+import { inter } from '@/app/ui/fonts';
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
- <body>{children}</body>
+ <body className={`${inter.className} antialiased`}>{children}</body>
</html>
);
}
結果:OK(フォントが適用された
セカンダリフォントの追加
-import { Inter, Lusitana } from 'next/font/google'
+import { Inter, Lusitana } from 'next/font/google'
export const inter = Inter({ subsets: ['latin'] });
+export const lusitana = Lusitana({
+ weight: ['400', '700'],
+ subsets: ['latin'],
+});
<div className={styles.shape} />
- <p className={`text-xl text-gray-800 md:text-3xl md:leading-normal`} />
+ <p className={`${lusitana.className} text-xl text-gray-800 md:text-3xl md:leading-normal`}>
・・・
- </* AcmeLogo / */>
+ <AcmeLogo />
・・・
結果:OK(フォントが適用され、ロゴが表示
画像のデリバリを開発者が意識することなく最適化する仕組みが備わっているとのこと
Web 開発における大きなトピックであり、それ自体が専門分野であると言えます。これらの最適化を手動で実装する代わりに、next/imageコンポーネントを使用して画像を自動的に最適化できます。
ヒーローイメージを追加
import styles from '@/app/ui/home.module.css';
+import Image from 'next/image';
・・・
{/* Add Hero Images Here */}
+ <Image
+ src="/hero-desktop.png"
+ width={1000}
+ height={760}
+ className="hidden md:block"
+ alt="Screenshots of the dashboard project showing desktop version"
+ />
+ <Image
+ src="/hero-mobile.png"
+ width={560}
+ height={620}
+ className="block md:hidden"
+ alt="Screenshot of the dashboard project showing mobile version"
+ />
結果:OK(画像が表示
デスクトップ版
モバイル版
4章
ファイルシステムルーティング
画像がわかりやすい
ダッシュボードページの作成
export default function Page() {
return <p>Dashboard Page</p>;
}
結果:OK
ダッシュボードページに共通レイアウトを追加
import SideNav from '@/app/ui/dashboard/sidenav';
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<div className="flex h-screen flex-col md:flex-row md:overflow-hidden">
<div className="w-full flex-none md:w-64">
<SideNav />
</div>
<div className="flex-grow p-6 md:overflow-y-auto md:p-12">{children}</div>
</div>
);
}
結果:OK(invoiceページの追加セクションは投稿を省略)
部分レンダリング
レイアウトを利用するとダッシュボードページおよび配下ページに自動で適用され、ページ遷移の際にロード済みのレイアウト部分は再レンダリングされない仕組みを備えている
LINKコンポーネント
前述の部分レンダリングの実装にあたる仕組み
結果:OK(DevToolでロードを確認すると部分レンダリングされてる)
import {
UserGroupIcon,
HomeIcon,
DocumentDuplicateIcon,
} from '@heroicons/react/24/outline';
+import Link from 'next/link';
・・・
- <a>
+ <Link
key={link.name}
href={link.href}
className="flex h-[48px] grow items-center justify-center gap-2 rounded-md bg-gray-50 p-3 text-sm font-medium hover:bg-sky-100 hover:text-blue-600 md:flex-none md:justify-start md:p-2 md:px-3"
>
<LinkIcon className="w-6" />
<p className="hidden md:block">{link.name}</p>
- </a>
+ </Link>
6章
Vercelアカウントを作成,GitHubリポジトリ連携してデプロイ
結果:OK:リリースされてることを確認
データベースをシードする
Vercel Postgres SDKをインストール
pnpm i @vercel/postgres
WARNが2点、発生した)とりあえず進めます
localhost:3000/seedにアクセス
結果:NG:
1.以下メッセージが表示されましたが、「成功」のメッセージが確認できませんでした
Uncomment this file and remove this line. You can delete this file when you are finished
2.コンソールにてエラーが発生しました
Error: Connection terminated unexpectedly
at un.<anonymous> (C:\dev\next-official-app\nextjs-dashboard\.next\server\chunks\node_modules__pnpm_c4c50d._.js:9496:77)
at Object.onceWrapper (C:\dev\next-official-app\nextjs-dashboard\.next\server\chunks\node_modules__pnpm_c4c50d._.js:6628:141)
at un.emit (C:\dev\next-official-app\nextjs-dashboard\.next\server\chunks\node_modules__pnpm_c4c50d._.js:6598:37)
at x.<anonymous> (C:\dev\next-official-app\nextjs-dashboard\.next\server\chunks\node_modules__pnpm_c4c50d._.js:9340:19)
at x.emit (C:\dev\next-official-app\nextjs-dashboard\.next\server\chunks\node_modules__pnpm_c4c50d._.js:6598:37)
at WebSocket.<anonymous> (C:\dev\next-official-app\nextjs-dashboard\.next\server\chunks\node_modules__pnpm_c4c50d._.js:8601:26)
at callListener (C:\dev\next-official-app\nextjs-dashboard\.next\server\chunks\node_modules__pnpm_c4c50d._.js:13128:18)
at WebSocket.onClose (C:\dev\next-official-app\nextjs-dashboard\.next\server\chunks\node_modules__pnpm_c4c50d._.js:13067:17)
at WebSocket.emit (node:events:519:28)
at WebSocket.emitClose (C:\dev\next-official-app\nextjs-dashboard\.next\server\chunks\node_modules__pnpm_c4c50d._.js:13526:14)
at TLSSocket.socketOnClose (C:\dev\next-official-app\nextjs-dashboard\.next\server\chunks\node_modules__pnpm_c4c50d._.js:14307:19)
at TLSSocket.emit (node:events:531:35)
at node:net:343:12
at TCP.done (node:_tls_wrap:650:7)
at TCP.callbackTrampoline (node:internal/async_hooks:130:17)
⨯ uncaughtException: Error: Connection terminated unexpectedly
at un.<anonymous> (C:\dev\next-official-app\nextjs-dashboard\.next\server\chunks\node_modules__pnpm_c4c50d._.js:9496:77)
at Object.onceWrapper (C:\dev\next-official-app\nextjs-dashboard\.next\server\chunks\node_modules__pnpm_c4c50d._.js:6628:141)
at un.emit (C:\dev\next-official-app\nextjs-dashboard\.next\server\chunks\node_modules__pnpm_c4c50d._.js:6598:37)
at x.<anonymous> (C:\dev\next-official-app\nextjs-dashboard\.next\server\chunks\node_modules__pnpm_c4c50d._.js:9340:19)
at x.emit (C:\dev\next-official-app\nextjs-dashboard\.next\server\chunks\node_modules__pnpm_c4c50d._.js:6598:37)
at WebSocket.<anonymous> (C:\dev\next-official-app\nextjs-dashboard\.next\server\chunks\node_modules__pnpm_c4c50d._.js:8601:26)
at callListener (C:\dev\next-official-app\nextjs-dashboard\.next\server\chunks\node_modules__pnpm_c4c50d._.js:13128:18)
at WebSocket.onClose (C:\dev\next-official-app\nextjs-dashboard\.next\server\chunks\node_modules__pnpm_c4c50d._.js:13067:17)
at WebSocket.emit (node:events:519:28)
at WebSocket.emitClose (C:\dev\next-official-app\nextjs-dashboard\.next\server\chunks\node_modules__pnpm_c4c50d._.js:13526:14)
at TLSSocket.socketOnClose (C:\dev\next-official-app\nextjs-dashboard\.next\server\chunks\node_modules__pnpm_c4c50d._.js:14307:19)
at TLSSocket.emit (node:events:531:35)
at node:net:343:12
at TCP.done (node:_tls_wrap:650:7)
at TCP.callbackTrampoline (node:internal/async_hooks:130:17)
データベースへの接続に失敗していそうです
先ほどのWARNでSDKのインストールが正しく行われていない?
pnpm i @vercel/postgresの結果に関しては、同様のissueを発見
package.jsonでメッセージに従ってバージョンを調整してみます
dependencies:
- react 19.0.0-rc-cd22717c-20241013
+ react 19.0.0-rc-65a56d0e-20241020
- react-dom 19.0.0-rc-cd22717c-20241013
+ react-dom 19.0.0-rc-65a56d0e-20241020
結果:OK(先ほどの後者のWARNメッセージが発生しないことを確認
しかりlocalhost:3000/seedにアクセスするとコンソールのエラーは変わりません
やはりデータベースへ接続できないようです
そもそも以下の箇所をコメントアウトor削除する必用があったことに気が付いた・・
export async function GET() {
- return Response.json({
- message:
- 'Uncomment this file and remove this line. You can delete this file when you are finished.',
- });
try {
await client.sql`BEGIN`;
await seedUsers();
await seedCustomers();
await seedInvoices();
await seedRevenue();
await client.sql`COMMIT`;
return Response.json({ message: 'Database seeded successfully' });
} catch (error) {
await client.sql`ROLLBACK`;
return Response.json({ error }, { status: 500 });
}
}
結果:OK(成功メッセージを確認、テーブル生成を確認)
ただし先ほどのエラーログは継続していた。
※本セクションとは関係ないエラーなのかもしれない・・
すると、ワーニングの解消も別に不要であったかもしれない・・
とりあえず当セクションは進められるので進めることにする
7章
※コードdiffを省略
結果:OK(ダッシュボードページに各種コンポーネントが反映されている
ここまでの実装では以下の課題を抱えているとのこと
・要は、このダッシュボート画面においてはパラレルにデータの取得をして差し支えない場面なのに、各コンポーネントに対して1つずつ完了をまってから直接で実行をしていてる
(ウォーターフォール的なアプローチになっているとのこと)
8章
静的レンダリング
動的レンダリング
9章
ストリーミング
10章