本記事は公式ドキュメントの翻訳を一部含みます
https://nextjs.org/docs/#routing
静的なルーティング
Next.jsはデフォルトで、ファイルシステムがルーティングとなります。例えば、pages以下に置かれたファイルがpages/about.js
だったとすると、localhost:3000/aboutにアクセスできるようになります。
そしてクライアントサイドのみの遷移を実現するために、Next.jsは<Link>
というコンポーネントを用意してくれています。シンプルなルーティングでは、以下のようにhrefパラメータにhref="/about"
と書けば遷移できます。
import Link from 'next/link'
export default () => (
<div>
Click{' '}
<Link href="/about">
<a>here</a>
</Link>{' '}
to read more
</div>
)
動的なルーティング
では/about/:name
のようなページがパラメータを持って変化するような、動的なページ遷移はどうしたらよいでしょう。
<Link>
コンポーネントには、hrefの他にasというパラメータがあり、ブラウザヒストリーの書き換えはasに渡された文字列で行われます。
一方、どのページに、どんなパラメータで遷移するかをNext.js内部に知らせるには、下記サンプルのようなURLオブジェクトをhrefに渡します。
pathnameはpages以下のファイル、queryの値にはクエリのオブジェクトを渡します。
import Link from 'next/link'
export default () => (
<div>
Click{' '}
<Link
as='/about/Zeit'
href={{ pathname: '/about', query: { name: 'Zeit' } }}
>
<a>here</a>
</Link>{' '}
to read more
</div>
)
URLオブジェクト
<Link>
コンポーネントと同じように、Routerの関数にURLオブジェクトを渡すことで、履歴のpush
とreplace
が可能です。
import Router from 'next/router'
const handler = () => {
Router.push({
pathname: '/about',
query: { name: 'Zeit' }
})
}
export default () => (
<div>
Click <span onClick={handler}>here</span> to read more
</div>
)
ルーターイベント
ルーター内部で実行されるイベントを捕捉したコールバックを作ることができます。
以下のイベントがサポートされています:
-
routeChangeStart(url)
- ルートが変化を開始する際に発火 -
routeChangeComplete(url)
- ルートの変化が完了した際に発火 -
routeChangeError(err, url)
- ルートが変化する最中、エラーが起こった際に発火 -
beforeHistoryChange(url)
- ブラウザ履歴が変化する直前に発火 -
hashChangeStart(url)
- ページが遷移せず、ハッシュでの遷移(id付きのaタグでスクロールするなど)が開始する際に発火 -
hashChangeComplete(url)
- ページが遷移せず、ハッシュでの遷移が完了した際に発火
routeChangeStart
を使った例がこちらです。
const handleRouteChange = url => {
console.log('App is changing to: ', url)
}
Router.events.on('routeChangeStart', handleRouteChange)
リアルワールドでのルーティング例
#1 ローディング中を示したい
SSRされたあとはSPAとしてページ遷移を行っているので、遷移中(ローディング中)ということをユーザーに示したいこともあります。
そういう場合はNProgressなどを導入しますが、これをどうやってNext.jsで実行するかが、こちらに例として載っています。
各イベントに対してNProgressのイベントをマッピングさせています。
Router.events.on('routeChangeStart', (url) => {
console.log(`Loading: ${url}`)
NProgress.start()
})
Router.events.on('routeChangeComplete', () => NProgress.done())
Router.events.on('routeChangeError', () => NProgress.done())
#2 オーバーレイコンポーネントが閉じないとき
SPAあるあるだと思いますが、ページ遷移時に前のコンポーネントの状態が残っていることがあると思います。例えば以下のケースです。
- ハンバーガーメニューを押すとオーバーレイのコンポーネントが出てくる
- メニュー内のリンクを押す
- ページ遷移は起こるがオーバーレイのコンポーネントが閉じない
これを解決するのにはrouteChangeComplete
をイベントを利用すればよいでしょう。
Router.events.on('routeChangeComplete', () => closeMenu())
#3 記事中のaタグでもクライアントで移動したい
CMSからの記事をdangerouslySetInnerHTML
でそのまま出力するとき、クライアントサイドで遷移させたいaタグがあったとしても、そのまま<Link>
コンポーネントを使用することは出来ないと思います。
そんなときは、componentDidMount
ライフサイクルなどで、ターゲットとなるaタグに上記のRouter.pushを使ってNext.jsのリンクを後付けすれば、クライアントサイドでの遷移が実現可能です。
#4 Next.js version6以前の場合、ルーティングイベントが重複して登録できない
Next.js version6以前だと、Router.event.onというイベントハンドラが無かったため、下記のようなutilを作成していました。今はversion7なので需要はないとは思いますが。
import Router from "next/router";
export const hookOnRouterChangeStart = (fnc, that) => {
const prev = Router.onRouteChangeStart;
Router.onRouteChangeStart = async () => {
if (typeof prev === "function") {
prev.call(that);
}
fnc();
};
};
おわりに
他にもNext.jsのルーティングは細かいところまでサポートしています。
例えば
-
<Link prefetch>
によるページのプリフェッチ -
scroll={false}
によるスクロールイベントの中止 -
<Link>
の子コンポーネントがaタグ以外の要素でのページ遷移 -
getInitialProps
が走らないで遷移をするShallow Routing -
withRouter
HOCによるrouterオブジェクトへのアクセス
などがあります。