# Next.jsのPages
Next.jsのウェブサイトのData Pages というページに沿ってやっていきます。
pagesというディレクトリがあって、そこに about.js
というファイルを作って配置すると、
function About() {
return <div>About</div>
}
export default About
↓こんな感じの About ページが出来上がります。このゼロコンフィグな感じ熱い。
ちなみに前回は npm run dev
でローカルで開発モードでサーバーを起動しましたが、今回は npx next dev
で立ち上げました。両方やってることは同じっぽいけど、next devの方がそれっぽくてイイのかな。
でもって、この page は Reactコンポーネント ということで、.js / .jsx / .ts / .tsx が対象とのことです。
.jsxって何?っていうところですが、↓こんなヤツです。ReactでJavaScript内でマークアップできちゃう的な。
const element = <h1>Hello, world!</h1>;
.tsxについても同様で、今まで雰囲気でTypeScriptをコピペしてたので実はあんまり文法とかよくわからないのですが↓こういうのちょいちょい見ますやね、と。
const MyComponent: React.FC<Props> = (props) => {
return <span>{props.foo}</span>
}
次にDynamic Routesと一緒に使うPage、と。posts/1
とかposts/2
みたいなパスが会った時に、posts/[id].js
とかって書いてやればOK的なヤツ。これはそれ用のドキュメントがあるからそっち見てねってことになっているみたいです。
# Pre-rendering
デフォルトではNext.jsは全部のページを pre-rendering しますよ、と。つまり全部のページのHTMLを事前に作っておくことで、全部がクライアント側で動くので早いしSEOにもイイじゃん、と。
# Pre-renderingにも2種類ある
- Static Generation
- Server-side Rendering
この2つの違いはどのタイミングでHTMLを生成するか。
- Static Generationは推奨されている方法でビルドするタイミングでHTML作るヤツ。
- Server-side Renderingはリクエストを受ける時にHTMLを作るヤツ
用途に応じてどっち使ってもいいし、アプリ内で大体なページはStatic Generationだけど一部にServer-side Renderingするハイブリッドな感じでももちろんOK。
でもって、さらにClient-side Renderingっていうのもあって、ページの一部をクライアント側のJSでレンダリングっていうことだけど、こちらはData Fetchingのところで詳しく、とのこと。
で、Static Generationの場合は next build
とするとHTMLが吐き出されるとのことで、CDNでキャッシュもできますがな的な。
# Static Generationでデータがない場合
これは上記のAboutと同様。
# Static Generationでデータがある場合
ビルド時にデータを取ってこなきゃいけない場合どうするかっていう話。
- ページのコンテンツが外部のデータに依存する場合は
getStaticProps
というのを使う。 - パスが外部データに依存する場合は
getStaticPaths
を使う。大体getStaticProps
と併せて使う感じになるらしい。
# シナリオ1 ページのコンテンツが外部データに依存
例えば、ブログのポストがCMSからデータをひっぱってくるようなヤツ
まずは柄というかポストのデータを受け取ってループを回してそのタイトルを表示する的なヤツ。
// TODO: API叩いてポストのデータを取ってくる。レンダリングがはじまる前にデータを取得する必要がある
function Blog({ posts }) {
return (
<ul>
{posts.map((post) => (
<li>{post.title}</li>
))}
</ul>
)
}
export default Blog
でもって、この事前にデータを取ってくるっていうところに対して async(非同期)なファンクションをexportすることをNext.jsでは出来るようにしてるよ、と。で、それにはgetStaticProps
っていうのをコールするらしいのだけど、こうすることでビルド時にそれがコールされて、データをフェッチしてprops
に突っ込んでくれるらしい。
ということで、上記のfunction Blogとexport default Blogの間にexport async function getStaticProps()を↓のように配置
function Blog({ posts }) {
return (
<ul>
{posts.map((post) => (
<li>{post.title}</li>
))}
</ul>
)
}
// ビルドの時にコールされるファンクション
export async function getStaticProps() {
// 外部APIの呼び出し
const res = await fetch('https://.../posts')
const posts = await res.json()
// コンテンツが { props: { posts } } で帰ってくるのでBlogはpropとして受け取ります、と。
return {
props: {
posts,
},
}
}
export default Blog
で、これに関しては Data Fetching のところで詳しく紹介します、と。
# シナリオ2 外部データにパスが依存する場合
Next.jsはDynamic Routesをサポートしますっていう話は上にもあったけど、posts/[id].js ってファイル名にしておくとidが1だったら posts/1
っていうパスになります、と。で、これも詳細はDynamic Routingに庵するドキュメントがあるので、それ見てねて感じですが、以下が例になります、と。
例えば、データが1件しかなければ posts/1 だけpre-renderすればいいけど、2件目が追加されたら posts/2 が必要ですやね。
ということで、パスは外部データによって決まると言えるわけですが、そうした場合は getStaticPaths
が使えるんですよ、と。(データを取得する場合は getStaticProps で、パスを生成する場合は getStaticPathsっていうのはテストに出そうですねぇw)
getStaticPathsは↓こんな感じ
// このファンクションはビルド時に呼ばれます
export async function getStaticPaths() {
// 外部APIを叩いてデータを取得
const res = await fetch('https://.../posts')
const posts = await res.json()
// プリレンダーするパス
const paths = posts.map((post) => ({
params: { id: post.id },
}))
// ビルドする時だけこのパスを作ります
// { fallback: false } はコレ以外のルートは404になるということ
return { paths, fallback: false }
}
もちろん、posts/[id].jsの個々のページの中でデータを取得する必要があるでしょう、と。その場合は getStaticPaths しながら、更に getStaticProps することも出来るんですよ、と。それが↓。
function Post({ post }) {
// 内容のレンダリング
}
export async function getStaticPaths() {
// 上記のようにパスを生成する用のアレ
}
// ビルドの時に呼ばれる
export async function getStaticProps({ params }) {
// パラメーターは post `id`なので、/posts/1, だったら params.id は 1 という形
const res = await fetch(`https://.../posts/${params.id}`)
const post = await res.json()
// このデータをプロパティとして返す
return { props: { post } }
}
export default Post
でもって、この getStaticPaths に関してもData Fetchingのドキュメント読んでね、と
# Static Generation推し
頻繁にユーザーが投稿したり、表示内容をアップデートするようなものじゃなければStatic Generationしときなよって話ですが、Stitic Generationできなさそうな時は、例えばClient-side Renderingを使えばビルド時のレンダリングをその部分だけスキップさせることも出来るし、Server-Side Renderingっていうリクエストごとにレンダリングさせる方法もあるよ、と。
# Server-side Rendering
もし、リクエスト毎にHTMLを生成する必要があるのであれば、Server-sideレンダリングを使いなさいっていうところですが、その場合は上記の getStaticProps や getStaticPaths と同じようなノリで、 getServerSideProps を使いなさい、とのこと。
function Page({ data }) {
// レンダリング
}
// リクエスト毎に呼ばれる
export async function getServerSideProps() {
// 外部からデータを取ってくる
const res = await fetch(`https://.../data`)
const data = await res.json()
// propsとしてデータを渡す
return { props: { data } }
}
export default Page
これもData Fetchingのところに詳細が〜っていうことみたいですが、実装する人からすればとても直感的というか。
# まとめ
- Static Generationがオススメ。HTMLはビルド時に生成される。外部データを取ってくるのは getStaticProps を使うと良くて、getStaticPathsするとpage/1とかpage/2とかできる
- Server-side Renderingはリクエスト毎にHTMLを吐き出すのであんまりオススメじゃないけどgetServerSidePropsを使えば実装的にはStatic Generationと同じノリでOK。
ということで、次回はData Fetching。