2021/11/07
はじめに
Web アプリ開発では主に React を使っていて,その知識を活用しながら開発を進められるということで,以前 Next.js と Gatsby で悩んだ結果,本ブログは Next.js で構築している.
ブログ開設時には Next.js を体系的に学ばず,サンプルを改造することにより,最低限の必須事項をピックアップしながら Web ページを構築した.
そのため,Next.js について知らないことが多く,時間の余裕もあるということで,公式チュートリアルを進めながら再入門した.
内容
Next.js の特徴
React を使って Web ページを作る際,ミニマムな SPA を作るだけなら簡単だが,一歩深みに入ると,途端に難しくなる.Code Splitting や Lazy Loading などがそれにあたる.
逆に,それを使わないと初期ロード時に全ての JS がバンドルされ,かなり初期ロードコストが上昇する.
初期ロード時間以上に困るのが,SPA ライブラリにおいてロードされるのは HTML ページではなく JS の塊であり,JS を実行しないとページ内の文章を認識できないず,SEO に弱いという言説も聞くことがある(近年のクローラは JS を実行するという話を聞いたこともある).
その観点において Next.js は Pre-rendering という,全てのページをあらかじめレンダリングしておくという概念を持っており,とあるページに URL からアクセスした場合に初期表示が HTML 内に含まれており,高パフォーマンスで,かつ,SEO に弱くない.
ルーティング
Next.js におけるルーティングは,react-router-dom
等のようなソースコード内に記載する形ではなく,プロジェクトのディレクトリで定義する.
この仕組みが,React がライブラリで Next.js がフレームワークであるというの点をよく表していると感じる.
具体的に,pages/*
がルーティングにあたる.
例えば,pages/index.js
はページのルート/
にあたり,pages/posts/first-post.js
は,/posts/first-post
にあたるといったものである.
ページ移動
Next.js において注意しなければ行けない点が,<a>
タグを使うと Next.js の強みを活かしきれないという点である.
<a>
の代わりに<Link>
を使う.これはnext/link
から import できるコンポーネントであり,Next.js 内でのリンクの役割を担う.
<a>
を使ってしまうと,ブラウザの機能を使って全く新しいページとしてロードされてしまうため,極力使わないほうが良い.
import Link from 'next/link';
// ミニマム
<Link href="/posts/first-post">text</Link>;
// リンクの色にする
<Link href="/posts/first-post" passHref>
<a>text</a>
</Link>;
Global CSS
Next.js では,GlobalCSS はpages/_app.js
で読み込む.
ここで言う GlobalCSS とは,html, body, *
などのスタイリングや,サイト全体に影響を及ぼすスタイリングのことを指す.
import '../styles/global.css'
export default function App({Component, pageProps}) {
return <Component {...pageProps}/>
}
メタデータの扱い
ページごとにタイトルを設定したり,メタデータを設定したいことがあると思う.そんな時は,<Head>
内に<meta>
を配置するとよい.<Head>
はnext/head
から import 可能.
また,ページごとに別の値が必要になりつつも一定のルールに基づいて記載するため,<Layout>
の Props に渡すのが良さそうと感じた.
Pre-rendering
Pre-rendering は,Next.js の長所の一つで,各ページの初期表示を HTML として表示する概念である.ブラウザの JS を無効にしてページにアクセスしてみるか,curl
コマンドで HTML を見ると,初期の HTML がどのようになっているのかを確認することが出来る.
Pre-rendering には,Static Generation と Server-side Rendering (SSR) の2種類があり,ハイブリッドモードも選択可能である.
2種類の Rendering 方法について,基本的には Static Generation で CDN で提供することが推奨されている.
Static Generation には良くない側面もあり,それは,Pre-rendering されたページが必ずしも最新版ではないという点である.その点においては Server-side Rendering は常に最新版のデータを返すというメリットがある(ただし SSR は低速である).
この面に関して言うと,Static Generation には Data を含めるのは必須ではなく,クライアント側で Fetch してもよい.
with Data
async
関数であるgetStaticProps
を使うと,データを埋め込みながら SG できる.この関数は,ページの表示コンポーネントに対して,静的な Props を注入できる.
export default function Home(props) { ... }
// 戻り値がPageにpassされる.
export async function getStaticProps() {
const data = ...
return {
props: ...
}
}
// propsを受けることが出来る.
export default function Page(props) {
return (
...
)
}
このデータ取得は,ページのコンポーネントの Pre-render のビルド時に呼ばれ,最初に解決される(development モードでは,リクエスト毎に呼び出される).
without Data
データを Pre-render しなくても良い場合は,Client-side Rendering という方法がある.
これに向くのは,SEO に関係なく頻繁に変更されるデータ,例えばユーザのダッシュボードなどである.
Dynamic Routes
ルーティングに,外部データを反映させる方法.
/post/xxx
//post/yyy
のように,タイトル等を基にルーティングを生成することが出来る.
そのためには,getStaticPaths
を使う.これは,getStaticProps
と同様に,ページのコンポーネントのビルド時に用いられる.
更に言うと,getStaticPaths
の結果がgetStaticProps
に注入され,上述の項目につながる.
Dynamic Routes においても,ディレクトリベースのルーティングを用いるが,ここでは,[id].js
のようにすることで,Dynamic Routes を指定できる.
export async function getStaticPaths() {
const paths = getAllPostIds();
/*
[
{params: {id: 'xxx'}},
{params: {id: 'yyy'}},
]
*/
return {
paths,
fallback: false,
};
}
export async function getStaticProps({ params }) {
const postData = await getPostData(params.id);
return {
props: {
postData,
},
};
}
getStaticPaths
の戻り値がgetStaticProps
に注入されるが,その形式はコメントのとおりである.
getStaticPaths
は,development モードでは毎度呼ばれるが,production モードではビルド時のみ呼ばれる.
fallback: false
とすることで,getStaticPaths
で返されないページが 404 を返すようになる.
初めて知ったのが,[...id].js
とid
を受け,params: id: ['a', 'b']
と渡すと,/pages/posts/a/b
というルートになるようだ.
404 をつくる
pages/404.js
とすると,404 で返すページを作れる.
API をつくる
pages/api/xx.js
とすると,/api/xx
の API を作ることが出来る.
形式は,
export default function handler(req, res) {
// req: HTTP incoming message
// res: HTTP server response
...
// res.status(200).json({text: 'hello'})
}
また,API も Dynamic API Routes といって,Dynamic な API を提供する.この場合も,pages/api/[pid].js
のようにし,以下のようにpid
を受け取ることが出来る.
export default function handler(req, res) {
const { pid } = req.query
res.end(`Post: ${pid}`)
}
自分が Next.js を使っていなかったら知りたい Q&A
Next.js を始めるか悩んでいた頃に気になっていたことを勝手に答えてしまう.
全ての React 開発者が Next.js を使うべきか?
そんなことはない.Next.js を扱えても,小さい Web アプリは React で作りたい.ただ,Web ページとなると Next.js を使う利点が大きくかんじる.WordPress みたいなノリで CMS 的な使い方をするなら Gatsby という選択も良いと思う.
どんな時に Next.js を選びたいか?
React の初期ロードが遅く感じる規模の Web アプリ・SEO を考慮した開発・Web ページを作りたい時
どのように Next.js を始めるとよいか?
具体的な目的があれば,Examplesからピックアップして,改変して目的を目指すのが良いと思う.学ぶことを目的とする場合は,公式チュートリアルをやってみて,興味のある Eample を動かしてみるのが良いと思う.
おわりに
Next.js やっぱりいいね!!
これまでに学んできたことの再確認,および,分散して学習した内容の体系化ができたので,時間をかけてでも公式チュートリアルをやってみてよかった.