はじめに
こんばんは。
今日の積本はこちら。
その中から Next.jsの勉強をしました。
こレに習って、自分が勉強した部分を備忘録で残しておきます。
本題
1. Next.jsとは
Reactベースのwebフレームワーク。
主要な技術はこちら
- React
- TypeScript
- webpack
SPAだけではなくマルチページにも対応している。
対応出来るニーズが広いため、その分設計ノウハウが必要になる。
HTMLの作成方法
Next.jsは大きく分けて以下のHTML作成手段を提供している。
それぞれ長所短所あるが、短所はキャッシュ等である程度カバーできる (逆にキャッシュで長所を消してしまう可能性もある)
1. Server Side Rendering(SSR)
リクエストのたびにサーバー側でHTMLを構築する
長所
常に最新の情報を返せる
短所
毎回レンダリングするのでサーバに負荷を与える
2. Static Site Generation(SSG)
事前にHTMLをビルドしておき、ビルド済みのHTMLを返す
長所
すでにHTMLが作られているのでサーバー負荷が低い
短所
HTMLを更新したい場合は再ビルドの必要があるので、更新頻度が高いサービスは不向き
3. Client Side Rendering(CSR)
必要最小限のHTMLのみかえして、その後APIでコンテンツを取得してHTML要素をレンダリングして構築する
長所
SSR同様最新の情報を取得できる
短所
APIで情報を取得後HTMLが構築されるまで何も表示されない
JSファイルが大きくなる傾向がある
2. セットアップ
A. Vercelで実行環境を構築する
サインアップ後 スターターキットで Next.js
を選ぶ。
レポジトリの名前を決めて create
Create Teamは自分ひとりなので skip
Buildを待つと出来上がるのでその後ダッシューボードへ
その後 Visit
を押下するとデプロイされたアプリケーションを表示できる。
B. ローカル環境構築
作成された git repositoryを cloneしてnodeのバージョンを合わせる。
本書ではnodeのバージョンが 14.17.0
だった。
自分のローカルのバージョンは以下だったので、nodeenvでバージョンを合わせるようにした。
$ node -v v16.11.0 $ nodenv install 14.17.0 Downloading node-v14.17.0-darwin-x64.tar.gz... -> https://nodejs.org/dist/v14.17.0/node-v14.17.0-darwin-x64.tar.gz Installing node-v14.17.0-darwin-x64... Installed node-v14.17.0-darwin-x64 to /path/to/.anyenv/envs/nodenv/versions/14.17.0 $ nodenv versions 14.17.0 * 16.11.0 (set by /path/to/.anyenv/envs/nodenv/version) $ nodenv local 14.17.0 $ node -v v14.17.0
その後 npm i
で各種ライブラリをインストール後 npm run dev
で開発サーバを立ち上げて、ブラウザアクセスが可能になる
C. Next.jsのファイル構成
$ tree -L 3 . . ├── README.md ├── next.config.js ├── node_modules │ ├── @babel │ ├── ... │ ├── ... │ ├── ... │ └── yocto-queue ├── package-lock.json ├── package.json ├── pages │ ├── _app.js │ ├── api │ │ └── hello.js │ └── index.js ├── public │ ├── favicon.ico │ └── vercel.svg ├── styles │ ├── Home.module.css │ └── globals.css └── yarn.lock
ルーティング
pages/
以下にファイルを配置していくことでルーティングが決定する。
例)
pages/users/index.js
=> /userspages/users/profile.js
=> /users/profilepages/users/[id].js
=> /users/{id}
API
pages/api/
配下にリクエストハンドラーをセットすることでAPIが提供できる。
const handler = (req, res) => { const body = { message: "プロフィールだよ"} res.statusCode = 200; res.json(body) }; export default handler
D. srcディレクトリは勝手に識別してくれる
特に設定を書かなくても src
は勝手に認識してくれる。
また、 pages
ディレクトリは src/
以下に配置しても認識してくれる。
E. TypeScript化が容易である
TypeScriptも簡単に導入可能。
以下のコマンドを実行し、TSをインストールする。
$ npm i -D typescript @types/react
pages/index.js
pages/_app.js
の拡張子を .tsx
に変更する。
その後ビルドし直すと next-env.d.ts
tsconfig.json
が作成される。
これで導入が完了。
3. 静的サイトの生成
Next.jsはページの特性に合わせてSSG or SSR or CSR などを切り替えることが出来る。
本書のP53ページに記載されているフローチャートが、とても理解しやすく、最適解の助けになるものだった。
ぜひ一読してもらいたい。
A. 今までの課題
今までの SSG
の課題は2つ
- ページ量にSSGのビルド時間が比例して大きくなる
- データ更新の都度、ビルドするという燃費の悪さ
Next.jsはこの弱点を Incremental Static Regeneration(ISR)
というしくみで克服している。
B. 静的生成の際に使用されるAPI
Next.jsのページ生成の静的生成において、非常に重要なAPIが以下。
- getStaticProps
ページの静的生成に外部からデータを取得して、page componentにpropsとして渡すことが出来る。
ページ生成に外部からデータを取る必要がない場合は定義の必要なし。
/** * 静的生成時にデータの取得・出力をする * @param context * @returns */ export async function getStaticProps(context) { const res = await fetch('https://.../users') const users = await res.json(); return { props: { users }} }
- getStaticPaths
ページの静的生成時に動的ルートを取得・出力できる
users/{id}
のように動的なURLでも 静的生成可能。
つまり事前に全ルートのファイルを事前作成する。
/** * params.idに1 or 2を渡す * これで、getStaticPropsがそれを扱えるようになる * @returns */ export async function getStaticPaths() { return { paths: [ { params: { id: 1 } }, { params: { id: 2 } } ], fallback: false } } export async function getStaticProps({ parmas }) { // params.idに1 or 2がはいる const res = await fetch(`https://.../users/${params.id}`) const user = await res.json(); return { props: { user }} }
決め打ちの数値だけではなく getStaticPaths()
でもfetchを使って、対象のID群を抜き出して渡すことも可能。
export async function getStaticPaths() { const res = await fetch(`https://.../items`) const items = await res.json() return { paths: items.map((item) => ({ params: { id: item.id }})), fallback: false } }
ただ、上記のような取得方法だと id
が何万件もあるようなものでは、更新されるたびに作るという作業が非効率で時間もかかりすぎる。
Next.jsはこの解決法に「段階的に静的生成する」というアプローチを取る。
それが、 fallback: true
C. fallback: true
fallback: true
を指定すると、事前生成されたページが見つからない場合、そのページを生成する。
事前に生成するのではなく、リクエストが来た段階でファイルの存在を判定し、なければ生成するという形。
当然あれば、それを返す。これで大量のページが存在するサイトでも、すべてをビルド時に生成する必要がなくなる。
D. fallback: 'blocking'
fallback: true
のデメリットとして、ファイルが存在せずページを生成するフローの場合、バックグラウンドで該当ページの生成が終わるまでは、空のHTMLが返ることになる。
ユーザーの表示的にはよく見るローディングページなどになると思う。
生成が完了したらJSで表示の更新が行われる。これを起こさせないのが、 fallback: 'blocking'
fallback: 'blocking'
を指定すると、ファイルが存在しないページへのリクエストの場合、最初にからのHTMLを返すことを許さない。
バックグラウンドのページ生成が終わるまでHTML表示を待たせることになる。
SEO対策が必要なページに適している。
この fallback
に関しても本では図解でわかりやすく解説されていたので、ぜひ読んでほしい。
E. Incremental Static Regeneration
今までの中で、まだ解決されてない問題が以下
一度生成されたページをどうやって更新するか
これを解決するのは、 getStaticProps()
の revalidate
オプション
export async function getStaticProps({ parmas }) { // params.idに1 or 2がはいる const res = await fetch(`https://.../users/${params.id}`) const user = await res.json(); // revalidate = 再生成をする間隔(秒数 = 下記だと100秒) return { props: { user }, revalidate: 100 } }
これによりページ更新のためにアプリケーション全体を再ビルドしなくて良くなった。
4. 静的生成に取り組む下準備
第5章以降でGithubAPIを題材に取り組む前の下準備が紹介されている。
- Githubのtoken取得
- GithubAPIを扱うための
Octokit
をインストール - fetch用のロジックを作成
- 実際にAPIを実行して取得してみる
- Github OAuth Appのセッティング
実際にAPIを実行して取得してみる
import { octokit } from "@/utils/fetcher" import { InferGetStaticPropsType } from "next" export type PageProps = InferGetStaticPropsType<typeof getStaticProps> export const getStaticProps = async () => { const repos = await octokit.request( "GET /users/{username}/repos", { username: 'kojirock5260' } ) return { props: { repos }} } export default function Page(props: PageProps) { console.log(props.repos.data.map(v => { const { name, full_name } = v return { name, full_name } })); return <div>Hellow Next.js</div> }
Github OAuth Appのセッティング
5. 実際に作ってみる
本に記載されている githubのコードを写経して作成してみた。
トップページ
ユーザー検索結果
自分のページ(認証前)
自分のページ(認証後)
commit一覧
一部TSの型エラーが出ていたので、制約を緩めれば今でもこの本のコード通りに動くと思われる。
終わりに
初期設定はとても簡単なものでした。
また、 fallback
や revalidate
なども実際本記載のサンプルアプリケーションを使って動きを確認できて、理解を深めることが出来ました。
react自体もあまりやったことないので、今度はreactの本も勉強してみようと思いました。
現場からは以上です。