はじめに
Next.js 9がリリースされました。
TypeScriptを標準サポートし、ものすごく簡単にアプリが作れるようになったようなので、試してみましたので、お知らせします。
やること
- Next.jsでアプリを作ってみる。
- WebAPIも作って、アプリから叩けるようにする。
できたこと
0. セットアップ
セットアップは簡単でした。
以下のようにライブラリをインストールしましょう。
$ npm -s i next react react-dom typescript @types/react @types/react-dom @types/node
package.json
のscriptにはnext dev
, next build
, next start
を書いておきましょう。
Next.jsは基本的にはサーバーサイドレンダリングのフレームワークです。開発時にはnext dev
でserve, 本番ではnext build
してプロダクションビルドした上で、next start
することで、余計なコードが除かれた状態でserveできるようになります。
1. 適当なアプリを作ってみる
まずは、next dev
コマンドで開発サーバーを立ち上げておきます。
画面を出す
まずは、適当なアプリを作ってみましょう。
基本的にはReactなようなので、Next.js特有のところだけ書いていきます。
ページを用意するには、pages
フォルダを用意します。
ここに、適当なtsx
ファイルを用意することで、ルーティングができます。
pages/foo.tsx
を用意すれば/foo
で画面にアクセスできるようになります。すごい。
しかも、これ、サーバーサイドレンダリング(SSR)なんですって。簡単。
import { NextPage } from "next";
const Index: NextPage = props => {
return (
<h1>Hello world!</h1>
);
}
// default exportしないといけない
export default Index;
画面を出す前に通信をする
シングルページアプリケーション(SPA)ならクライアントで通信するのですが、SSRではサーバー内で取得した上でレンダリングしたHTMLをクライアントにレスポンスします。
レンダリング前にサーバー側で必要なデータを取得するには以下のようにします。
import { NextPage } from "next";
interface Props {
data: { foo: string };
}
const Index: NextPage<Props> = props => {
return <div>{props.data.foo}</div>;
}
// async必須。
Index.getInitialProps = async () => {
// axiosとかで通信する
// SSRなので、fetchは使えません(node-fetch入れればいける)。
return { data: { foo: "bar" } };
}
export default Index;
getInitialProps
で得られた値はprops
で得られます。
別のページに飛ばしたい
aタグのhrefでいけます。
() => {
return (<a href="/foo/bar">barに飛ぶ</a>);
}
idとか使って動的ルーティングしたい
例えば、/users
というページがあり、/users/:id
という動的なパスを持つページがあるとしましょう。
こんなとき、以下のようなジャンプ元の画面と、
(ファイルパスは/pages/users/index.tsx
と/pages/users.tsx
のどちらかでよいです。)
import Link from "next/link"
const Users = () => {
// Linkを使って動的ルーティングを実現する。Linkタグの間にはaタグが必要。
// hrefは、URLのフォーマットを示す。
// asは実際に飛ばすURLを示す。
return (
<ul>
<li><Link href="/users/[id]" as="/users/0"><a>user1</a></Link></li>
<li><Link href="/users/[id]" as="/users/1"><a>user2</a></Link></li>
<li><Link href="/users/[id]" as="/users/2"><a>user3</a></Link></li>
</ul>
);
}
以下のような、ジャンプ先の画面を用意します。
ファイル名はほんとに[id].tsx
とします。
import { useRouter } from "next/router";
const User = () => {
const router = useRouter();
// ジャンプ元で用意した[id]とquery.idが一致する。
return <div>userId: {router.query.id}</div>
}
あとから気づいたのですが、ジャンプ元の実装はSPAっぽい挙動になるだけでした。
普通に、aタグ使ってhrefで/users/50
とかやっても普通にルーティングされます。
動的ルーティングした結果を元に、SSR前にデータを取りたい
getInitialProps
と動的ルーティングの複合技です。
ジャンプ元のコードは同じです。
const User = () => {
}
User.getInitialProps = async context => {
const id = context.query.id; // context.query.idにパスが含まれるので、通信に使うなどしましょう。
}
SPAを作る
普通に、Reactと同じです。
動的ルーティングにはLink
を使いましょう。
useState
やuseEffect
も使えます。
2. WebAPIも作って、SPAから叩けるようにする
APIを作る場合には/pages/api/
というフォルダの下にファイルまたはフォルダを作ります。
なお、実装はexpress.js
のmiddleware
に似ている気がしますので、ご存知の方は簡単かもしれません。
import { NextApiRequest, NextApiResponse } from "next";
// export defaultが必要
export default (req: NextApiRequest, res: NextApiResponse) => {
res.setHeader("content-type", "application/json");
res.status(200);
res.end(JSON.stringify({ users: [{ name: "foo" }] }));
}
叩く場合は/api/users
をGET
するなどしましょう。
なお、ざっと調べた限りでは、GET, POSTなどメソッドによって呼び分けてくれたりするわけではなさそうなので、関数内で自力でメソッド判定するのがよいかと思いました。
※参考
WebAPIも動的ルーティングする
こちらはそんなに難しくなく、req.query
に入っています。
やはりファイル名が重要です。
(req: NextApiRequest) => {
const id = req.query.id;
}
まとめ
ファイルやフォルダ階層でパスを分けることができるところがわかりやすくてよかったです。
何も考えなくてもSSRできたのも
今回は無理やりWebAPIも作ってみましたが、基本的にはSSRするので、いらないよなー、と思いました。
覚えるコマンドも三つだし、Herokuにデプロイするならメチャ簡単でした。
以上です。よろしくお願いします。