「Next.js」の版間の差分
ナビゲーションに移動
検索に移動
(→useRef) |
|||
(同じ利用者による、間の46版が非表示) | |||
1行目: | 1行目: | ||
− | [[React]] | + | [[React]] | [[MUI]] | |
{{amazon|4815619484}} | {{amazon|4815619484}} | ||
− | + | =概要= | |
+ | *[https://ja.next-community-docs.dev/docs/app-router/getting-started/ Next.js Document] | ||
+ | *[https://qiita.com/newbee1939/items/7ce919f9a1a7153582b8#:~:text=React%20Serv React Server ComponentsとApp Routerをそろそろちゃんと理解したい] | ||
*Reactは、あくまでUI部分のみ | *Reactは、あくまでUI部分のみ | ||
*本格的なアプリ開発には周辺領域を担うためのフレームワークが必要 | *本格的なアプリ開発には周辺領域を担うためのフレームワークが必要 | ||
13行目: | 15行目: | ||
**リソース組み込みの自動最適化 | **リソース組み込みの自動最適化 | ||
**CSSフレームワーク、Tailwind CSSへの標準対応 | **CSSフレームワーク、Tailwind CSSへの標準対応 | ||
− | |||
=導入= | =導入= | ||
44行目: | 45行目: | ||
npm run build | npm run build | ||
npm start | npm start | ||
+ | </pre> | ||
+ | ===静的サイトとしてビルドした場合の実行=== | ||
+ | *Prisma でのデータアクセスの処理がビルド時に実行され、その時点の静的な出力になる | ||
+ | <pre> | ||
+ | npm run build | ||
+ | npx serve@latest out | ||
+ | </pre> | ||
+ | |||
+ | ====ビルド結果がクリーンされない==== | ||
+ | *https://blog.unimoku.com/20201209 | ||
+ | */.next /out はビルド時に削除しても良い。 | ||
+ | *デフォルトでクリーンされない | ||
+ | <pre> | ||
+ | rm -rf .next out | ||
</pre> | </pre> | ||
59行目: | 74行目: | ||
*ルート定義不要、フォルダ階層に準じてリクエストパスとコンポーネント(.jsファイル)との対応関係が決まる | *ルート定義不要、フォルダ階層に準じてリクエストパスとコンポーネント(.jsファイル)との対応関係が決まる | ||
*page.jsはファイル名も固定でフォルダー構造だけでリクエストパスが決定される | *page.jsはファイル名も固定でフォルダー構造だけでリクエストパスが決定される | ||
+ | *デフォルトで作成したコンポーネントがサーバーコンポーネントになる | ||
+ | *クライアントコンポーネントにするにはuse clientを記述する必要がある | ||
+ | |||
+ | =====アプリ共通の外枠===== | ||
+ | *layout.js | ||
+ | *<html>、<body> 要素が存在し、個々のページを埋め込むため chilrenプロパティを引用していることが条件 | ||
+ | |||
+ | ===ルートパラメータ=== | ||
+ | *[名前] : 単一パラメータ | ||
+ | *[...名前] : キャッチオールセグメント | ||
+ | *<nowiki>[[...名前]]</nowiki> : 省略可能なキャッチオールセグメント | ||
+ | |||
+ | {| class="wikitable" | ||
+ | |+ App Routerのルートパラメータ | ||
+ | |- | ||
+ | ! フォルダ階層 !! 対応リクエスト !! パラメータ | ||
+ | |- | ||
+ | | /app/hoge/[id]/page.js || /hoge/001 || { id : '001' } | ||
+ | |- | ||
+ | | /app/foo/[...keys]/page.js || /foo/aaa/bbb || { keys: ['aaa', 'bbb'] } | ||
+ | |- | ||
+ | | /app/bar/<nowiki>[[...keys]]</nowiki>/page.js || /bar || {} | ||
+ | |- | ||
+ | | 〃 || /bar/aaa/bbb || { keys: ['aaa', 'bbb'] } | ||
+ | |||
+ | |} | ||
+ | |||
+ | ==Tailwind CSS== | ||
+ | *https://tailwindcss.com/ | ||
+ | *ユーティリティファーストをコンセプトに開発されたCSSフレームワーク | ||
+ | *ユーティリティを組み合わせてデザインを作るので独自のスタイルを作りやすく細かな調整もしやすいメリット | ||
+ | *標準のサイズ、カラーなどは用意されている | ||
+ | |||
+ | [https://tailwindcsscheatsheet.com/ Cheat Sheet] | ||
+ | |||
+ | *Tailwind CSSによるスタイルを適用 | ||
+ | |||
+ | <pre> | ||
+ | import "./globals.css"; | ||
+ | : | ||
+ | |||
+ | <ul className="fle bg-blue-600 mb-4 pl-2">...</ul> | ||
+ | </pre> | ||
+ | |||
+ | ==Prisma== | ||
+ | *JavaScriptアプリからデータベース操作を取り持つO/Rマッパー | ||
+ | ===インストール=== | ||
+ | *成功すると以下のファイルが生成される | ||
+ | **.env : 環境変数ファイル、gitignore に追加する | ||
+ | ** prisma/schema.prisma : Prismaのスキーマファイル | ||
+ | |||
+ | sqliteでの構成例 | ||
+ | <pre> | ||
+ | $ npm install prisma --save-dev | ||
+ | $ npx prisma init --datasource-provider sqlite | ||
+ | warn You already have a .gitignore file. Don't forget to add `.env` in it to not commit any private information. | ||
+ | </pre> | ||
+ | |||
+ | ===データモデルの定義=== | ||
+ | *prisma/schema.prisma に追記 | ||
+ | <pre> | ||
+ | model reviews { | ||
+ | id String @id | ||
+ | title String | ||
+ | author String | ||
+ | price Int | ||
+ | publlisher String | ||
+ | published String | ||
+ | image String | ||
+ | read DateTime @default(now()) | ||
+ | memo String | ||
+ | } | ||
+ | </pre> | ||
+ | |||
+ | *DBに反映 | ||
+ | <pre> | ||
+ | $ npx prisma db push | ||
+ | </pre> | ||
+ | |||
+ | *Prisma Studio(Webベース管理ツール)で確認 | ||
+ | <pre> | ||
+ | $ npx prisma studio | ||
+ | </pre> | ||
+ | |||
+ | |||
+ | *Error: @prisma/client did not initialize yet. Please run "prisma generate" and try to import it again. | ||
+ | <pre> | ||
+ | $ nps prisma generate | ||
+ | </pre> | ||
+ | <pre> | ||
+ | import { PrismaClient } from "@prisma/client"; | ||
+ | </pre> | ||
+ | |||
+ | *Prisma Clientの利用 | ||
+ | <pre> | ||
+ | import { PrismaClient } from "@prisma/client"; | ||
+ | </pre> | ||
+ | |||
+ | ==サーバーアクション== | ||
+ | *Next.js内蔵の仕組み | ||
+ | *ページコンポーネント、イベントハンドラー、<form<要素のaction属性などからサーバーコードを呼び出す仕組み | ||
+ | *サーバーとのやりとりに際し、fetchなどの非同期通信コードの不要となり、例えばコンポーネントからデータベースを操作するにも直感的なコードを記述できる | ||
+ | *現時点ではα版 | ||
+ | |||
+ | ===有効化=== | ||
+ | |||
+ | next.config.[https://qiita.com/taisei-ide-lyd/items/f3673e913e8b658946a6#:~:text=%E6%8B%A1%E5%BC%B5%E5%AD%90%E3%81%8C.mjs%E3%81%A8. mjs] | ||
+ | <pre> | ||
+ | const nextConfig = { | ||
+ | experimental: { | ||
+ | serverActions: true | ||
+ | } | ||
+ | }; | ||
+ | </pre> | ||
+ | |||
+ | =コンポーネントの処理= | ||
+ | *[https://ja.next-community-docs.dev/docs/app-router/building-your-application/rendering/composition-patterns#:~:text=%E3%82%B5%E3%83%BC%E3%83%90%E3%83%BC%E3%81%A8%E3%82%AF%E3%83%A9%E3%82%A4%E3%82%A2%E3%83%B3 サーバーとクライアントの構成パターン] | ||
+ | |||
+ | ==サーバー側で実行== | ||
+ | *Next.jsの基本はサーバー側でコンポーネントを処理 | ||
+ | *[https://www.ey-office.com/blog_archive/2024/05/29/tried-running-react-server-component-in-a-production-environment-and-got-a-little-hooked/ 本番環境で動かす] | ||
+ | *メリット | ||
+ | **サーバーリソースの活用 | ||
+ | **ダウンロードコードサイズの最小化 | ||
+ | **機密情報(サクセストークンなど)の管理 | ||
+ | *クライアントに依存する操作には対応していない | ||
+ | **State関連操作(useState,useReducer,useEffectなど) | ||
+ | **イベントリスナー(onXxxxx) | ||
+ | **ブラウザAPIアクセス | ||
+ | **上記を伴うカスタムフック | ||
+ | **クラスコンポーネント利用 | ||
+ | |||
+ | ==クライアント側で実行== | ||
+ | *ファイルの先頭に、'use client'ディレクティブを記述することでコンポーネントをクライアント側で実行可 | ||
+ | |||
+ | ==静的サイト== | ||
+ | *[https://zenn.dev/hiromu617/articles/1ed6811dc6cf26 静的サイトの生成] | ||
+ | *静的エクスポートを有効にすることによりビルド時の生成物がNode.jsサーバーを必要としないものとなる | ||
+ | <pre> | ||
+ | const nextConfig = { | ||
+ | : | ||
+ | output: 'export' | ||
+ | } | ||
+ | </pre> | ||
+ | *この機能を有効にすることで、ridirectsやmiddlewareなどのサーバーを必要とする一部機能が使えなくなります | ||
+ | *動的レンダリングも一切できなくなってしまうのでこの機能を有効にするかはアプリケーションの性質を考慮して検討 | ||
+ | |||
+ | =実装= | ||
+ | |||
+ | ==ルートレイアウト== | ||
+ | ===アプリ内のリンクにはLinkコンポーネントを利用=== | ||
+ | |||
+ | <pre> | ||
+ | import Link from "next/link"; | ||
+ | </pre> | ||
+ | |||
+ | 規定で以下のようなコンポーネントが用意されており、アプリ内リソースを効率的に利用できる | ||
+ | {| class="wikitable" | ||
+ | |+ コンポーネント | ||
+ | |- | ||
+ | ! コンポーネント !! 概要 | ||
+ | |- | ||
+ | | <Link> || リンクを生成 | ||
+ | |- | ||
+ | | <Script> || スクリプトのロード | ||
+ | |- | ||
+ | | <Image> || 画像を表示 | ||
+ | |} | ||
+ | |||
+ | ===メタデータ=== | ||
+ | *メタデータを定義しておくことで、個々のページに適切なヘッダーなどを埋め込める | ||
+ | *複数箇所で宣言された場合、シャローマージされる | ||
+ | <pre> | ||
+ | export const metadata: Metadata = { | ||
+ | title: "Reading Recorder", | ||
+ | description: "自分が読んだ書籍の記録を残す", | ||
+ | }; | ||
+ | </pre> | ||
+ | |||
+ | *generateMetadata関数を利用しデータベースなどから取得した結果を元に動的に生成も可能 | ||
+ | <pre> | ||
+ | export async function generateMetadata({ params, searchParams }) { | ||
+ | : | ||
+ | return { | ||
+ | title : result.title, | ||
+ | : | ||
+ | } | ||
+ | } | ||
+ | </pre> | ||
+ | |||
+ | ===Route Segment Config=== | ||
+ | *https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config | ||
+ | *レイアウト、ページ単位の設定 | ||
+ | *あらかじめ決められた名前の変数をエクスポートしておくことで、個別の設定を施せる | ||
+ | *force-dynamic : 極力キャッシュを無効にし、ページを動的に描画しようとする | ||
+ | |||
+ | <pre> | ||
+ | export const dynamic = 'force-dynamic' | ||
+ | </pre> | ||
+ | |||
+ | =フォント= | ||
+ | *Googleフォント(無償フォントのディレクトリサービス)を有効化 | ||
+ | *具体的なフォントはGoogleフォントから自由に選択できるが一般的にはバリアブルフォント推奨(下例ではInconsolata) | ||
+ | *フォントはファイルサイズが大きいので、subsetで利用する文字のみ取り出す | ||
+ | *fnc.className でフォントを適用できる | ||
+ | <pre> | ||
+ | import { Inconsolata } from 'next/font/google'; | ||
+ | const fnt = Inconsolata({ subsets: ['latin'] }); | ||
+ | |||
+ | : | ||
+ | <body className={`${fnt.className}` }> | ||
+ | </pre> | ||
+ | |||
+ | =MUI= | ||
+ | [[MUI]] | ||
+ | |||
+ | =外部サービスからのデータ取得= | ||
+ | *標準JavaScriptのfetchが拡張されている | ||
+ | *関数コンポーネントを非同期(async)とすることでコンポーネント配下でfetchメソッドを実行できる | ||
+ | *個々のfetch呼び出しに際して、キャッシュ/再取得ルールを指定できる | ||
+ | *コンポーネント改装間で同一データを取得の場合、リクエストの重複を除去 | ||
+ | <pre> | ||
+ | fetch('https://...", { cache: 'force-cache' }); // 無制限にキャッシュ | ||
+ | fetch('https://..."); // 無制限にキャッシュ | ||
+ | fetch('https://...", { cache: 'no-store' }); // 常にデータを取得 | ||
+ | fetch('https://...", { cache: 'revalidate: 10' }); // 10秒以上でデータを再取得 | ||
+ | </pre> | ||
+ | |||
+ | =Objects= | ||
+ | ==useRef== | ||
+ | *https://ja.react.dev/reference/react/useRef | ||
+ | *ref を変更しても、再レンダーはトリガされないということです。このことから、ref は、出力されるコンポーネントの外見に影響しないデータを保存するのに適しています | ||
+ | *ref を使用することで、次のことが保証されます。 | ||
+ | *レンダーを跨いで情報を保存できます(通常の変数は、レンダーごとにリセットされます)。 | ||
+ | *変更しても再レンダーはトリガされません(state 変数は、変更すると再レンダーがトリガされます)。 | ||
+ | *保存された情報は、コンポーネントのインスタンスごとに固有です(コンポーネントの外側で定義された変数は、コンポーネントのインスタンス間で共有されます)。 | ||
+ | *[https://zenn.dev/tns_00/articles/react-typescript-form-error-of-value TypeScriptでエラーの場合] | ||
+ | |||
+ | <pre> | ||
+ | import { useRef } from 'react'; | ||
+ | |||
+ | const txtKeyword = useRef<HTMLInputElement>(null); | ||
+ | console.log(`txtKeyword.current.value:${txtKeyword.current?.value}`); | ||
+ | : | ||
+ | <input type="text" ref={txtKeyword} ../> | ||
+ | </pre> | ||
+ | |||
+ | =Tips= | ||
+ | |||
+ | ==リモートホストからの画像表示を許可する== | ||
+ | *next.config.mjs | ||
+ | <pre> | ||
+ | const nextConfig = { | ||
+ | |||
+ | : | ||
+ | |||
+ | images: { | ||
+ | remotePatterns: [ | ||
+ | { | ||
+ | hostname: 'books.google.com' | ||
+ | } | ||
+ | ] | ||
+ | } | ||
+ | }; | ||
+ | </pre> |
2024年10月6日 (日) 02:44時点における最新版
概要
- Next.js Document
- React Server ComponentsとApp Routerをそろそろちゃんと理解したい
- Reactは、あくまでUI部分のみ
- 本格的なアプリ開発には周辺領域を担うためのフレームワークが必要
- Reactベースのフレームワークとしてデファクトスタンダードと言える存在
- 主なライブラリ
- ファイルシステムベースの設定レスルーター
- サーバーコンポーネント
- データ取得用fetchメソッド
- リソース組み込みの自動最適化
- CSSフレームワーク、Tailwind CSSへの標準対応
導入
newdelhi:Workspaces piroto$ npx create-next-app@latest Need to install the following packages: create-next-app@14.2.14 Ok to proceed? (y) y ✔ What is your project named? … first-nextjs-app ✔ Would you like to use TypeScript? … No / Yes ✔ Would you like to use ESLint? … No / Yes ✔ Would you like to use Tailwind CSS? … No / Yes ✔ Would you like to use `src/` directory? … No / Yes ✔ Would you like to use App Router? (recommended) … No / Yes ✔ Would you like to customize the default import alias (@/*)? … No / Yes Creating a new Next.js app in /Users/piroto/Workspaces/first-nextjs-app.
実行
開発
npm run dev
本番ビルド+実行
npm run build npm start
静的サイトとしてビルドした場合の実行
- Prisma でのデータアクセスの処理がビルド時に実行され、その時点の静的な出力になる
npm run build npx serve@latest out
ビルド結果がクリーンされない
- https://blog.unimoku.com/20201209
- /.next /out はビルド時に削除しても良い。
- デフォルトでクリーンされない
rm -rf .next out
App Router
- React Routerとは異なる独自のルーターを標準で提供
- コンポーネントそのものはReactと変わらない
2種類のルーター
- Pages Router : 旧来から提供されている
- App Router : Next.js 13から導入された新しいルーター(推奨)
App Router
- フォルダベースのルーター
- ルート定義不要、フォルダ階層に準じてリクエストパスとコンポーネント(.jsファイル)との対応関係が決まる
- page.jsはファイル名も固定でフォルダー構造だけでリクエストパスが決定される
- デフォルトで作成したコンポーネントがサーバーコンポーネントになる
- クライアントコンポーネントにするにはuse clientを記述する必要がある
アプリ共通の外枠
- layout.js
- <html>、<body> 要素が存在し、個々のページを埋め込むため chilrenプロパティを引用していることが条件
ルートパラメータ
- [名前] : 単一パラメータ
- [...名前] : キャッチオールセグメント
- [[...名前]] : 省略可能なキャッチオールセグメント
フォルダ階層 | 対応リクエスト | パラメータ |
---|---|---|
/app/hoge/[id]/page.js | /hoge/001 | { id : '001' } |
/app/foo/[...keys]/page.js | /foo/aaa/bbb | { keys: ['aaa', 'bbb'] } |
/app/bar/[[...keys]]/page.js | /bar | {} |
〃 | /bar/aaa/bbb | { keys: ['aaa', 'bbb'] } |
Tailwind CSS
- https://tailwindcss.com/
- ユーティリティファーストをコンセプトに開発されたCSSフレームワーク
- ユーティリティを組み合わせてデザインを作るので独自のスタイルを作りやすく細かな調整もしやすいメリット
- 標準のサイズ、カラーなどは用意されている
- Tailwind CSSによるスタイルを適用
import "./globals.css"; : <ul className="fle bg-blue-600 mb-4 pl-2">...</ul>
Prisma
- JavaScriptアプリからデータベース操作を取り持つO/Rマッパー
インストール
- 成功すると以下のファイルが生成される
- .env : 環境変数ファイル、gitignore に追加する
- prisma/schema.prisma : Prismaのスキーマファイル
sqliteでの構成例
$ npm install prisma --save-dev $ npx prisma init --datasource-provider sqlite warn You already have a .gitignore file. Don't forget to add `.env` in it to not commit any private information.
データモデルの定義
- prisma/schema.prisma に追記
model reviews { id String @id title String author String price Int publlisher String published String image String read DateTime @default(now()) memo String }
- DBに反映
$ npx prisma db push
- Prisma Studio(Webベース管理ツール)で確認
$ npx prisma studio
- Error: @prisma/client did not initialize yet. Please run "prisma generate" and try to import it again.
$ nps prisma generate
import { PrismaClient } from "@prisma/client";
- Prisma Clientの利用
import { PrismaClient } from "@prisma/client";
サーバーアクション
- Next.js内蔵の仕組み
- ページコンポーネント、イベントハンドラー、<form<要素のaction属性などからサーバーコードを呼び出す仕組み
- サーバーとのやりとりに際し、fetchなどの非同期通信コードの不要となり、例えばコンポーネントからデータベースを操作するにも直感的なコードを記述できる
- 現時点ではα版
有効化
next.config.mjs
const nextConfig = { experimental: { serverActions: true } };
コンポーネントの処理
サーバー側で実行
- Next.jsの基本はサーバー側でコンポーネントを処理
- 本番環境で動かす
- メリット
- サーバーリソースの活用
- ダウンロードコードサイズの最小化
- 機密情報(サクセストークンなど)の管理
- クライアントに依存する操作には対応していない
- State関連操作(useState,useReducer,useEffectなど)
- イベントリスナー(onXxxxx)
- ブラウザAPIアクセス
- 上記を伴うカスタムフック
- クラスコンポーネント利用
クライアント側で実行
- ファイルの先頭に、'use client'ディレクティブを記述することでコンポーネントをクライアント側で実行可
静的サイト
- 静的サイトの生成
- 静的エクスポートを有効にすることによりビルド時の生成物がNode.jsサーバーを必要としないものとなる
const nextConfig = { : output: 'export' }
- この機能を有効にすることで、ridirectsやmiddlewareなどのサーバーを必要とする一部機能が使えなくなります
- 動的レンダリングも一切できなくなってしまうのでこの機能を有効にするかはアプリケーションの性質を考慮して検討
実装
ルートレイアウト
アプリ内のリンクにはLinkコンポーネントを利用
import Link from "next/link";
規定で以下のようなコンポーネントが用意されており、アプリ内リソースを効率的に利用できる
コンポーネント | 概要 |
---|---|
<Link> | リンクを生成 |
<Script> | スクリプトのロード |
<Image> | 画像を表示 |
メタデータ
- メタデータを定義しておくことで、個々のページに適切なヘッダーなどを埋め込める
- 複数箇所で宣言された場合、シャローマージされる
export const metadata: Metadata = { title: "Reading Recorder", description: "自分が読んだ書籍の記録を残す", };
- generateMetadata関数を利用しデータベースなどから取得した結果を元に動的に生成も可能
export async function generateMetadata({ params, searchParams }) { : return { title : result.title, : } }
Route Segment Config
- https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config
- レイアウト、ページ単位の設定
- あらかじめ決められた名前の変数をエクスポートしておくことで、個別の設定を施せる
- force-dynamic : 極力キャッシュを無効にし、ページを動的に描画しようとする
export const dynamic = 'force-dynamic'
フォント
- Googleフォント(無償フォントのディレクトリサービス)を有効化
- 具体的なフォントはGoogleフォントから自由に選択できるが一般的にはバリアブルフォント推奨(下例ではInconsolata)
- フォントはファイルサイズが大きいので、subsetで利用する文字のみ取り出す
- fnc.className でフォントを適用できる
import { Inconsolata } from 'next/font/google'; const fnt = Inconsolata({ subsets: ['latin'] }); : <body className={`${fnt.className}` }>
MUI
外部サービスからのデータ取得
- 標準JavaScriptのfetchが拡張されている
- 関数コンポーネントを非同期(async)とすることでコンポーネント配下でfetchメソッドを実行できる
- 個々のfetch呼び出しに際して、キャッシュ/再取得ルールを指定できる
- コンポーネント改装間で同一データを取得の場合、リクエストの重複を除去
fetch('https://...", { cache: 'force-cache' }); // 無制限にキャッシュ fetch('https://..."); // 無制限にキャッシュ fetch('https://...", { cache: 'no-store' }); // 常にデータを取得 fetch('https://...", { cache: 'revalidate: 10' }); // 10秒以上でデータを再取得
Objects
useRef
- https://ja.react.dev/reference/react/useRef
- ref を変更しても、再レンダーはトリガされないということです。このことから、ref は、出力されるコンポーネントの外見に影響しないデータを保存するのに適しています
- ref を使用することで、次のことが保証されます。
- レンダーを跨いで情報を保存できます(通常の変数は、レンダーごとにリセットされます)。
- 変更しても再レンダーはトリガされません(state 変数は、変更すると再レンダーがトリガされます)。
- 保存された情報は、コンポーネントのインスタンスごとに固有です(コンポーネントの外側で定義された変数は、コンポーネントのインスタンス間で共有されます)。
- TypeScriptでエラーの場合
import { useRef } from 'react'; const txtKeyword = useRef<HTMLInputElement>(null); console.log(`txtKeyword.current.value:${txtKeyword.current?.value}`); : <input type="text" ref={txtKeyword} ../>
Tips
リモートホストからの画像表示を許可する
- next.config.mjs
const nextConfig = { : images: { remotePatterns: [ { hostname: 'books.google.com' } ] } };
© 2006 矢木浩人