Next.jsを使っているのだが、内部的にどう動いているのかよくわかっていないので軽く調べてみた。
以下を使って、SSR処理時やルーティングでの挙動を見てみる。 https://github.com/zeit/next.js/tree/fc05c9c27307fd6910f7d87b8c65b085751ce190/examples/parameterized-routing
以下のような流れでサーバ起動まで実行できる。
$ npx create-next-app --example parameterized-routing parameterized-routing-app $ cd parameterized-routing-app $ npm run build $ npm start
pagesディレクトリは以下のようなファイル構成になっている。
pages ├── blog.js └── index.js 0 directories, 2 files
ビルド時に生成される .next
は以下のようなディレクトリになっていた。
.next ├── BUILD_ID ├── build-manifest.json ├── bundles │ └── pages │ ├── _app.js │ ├── _error.js │ ├── blog.js │ └── index.js ├── server │ ├── bundles │ │ └── pages │ │ ├── _app.js │ │ ├── _document.js │ │ ├── _error.js │ │ ├── blog.js │ │ └── index.js │ └── pages-manifest.json └── static └── commons └── main-ee347f50f148624764ad.js 7 directories, 13 files
.next/bundles, .next/server/bundles の各ディレクトリに、pagesディレクトリのファイルに対応する blog.js, index.js が生成され、 _app.js
, _error.js
, _document.js
も生成されている (_document.js
はserverのみ)
_app.js
, _error.js
, _document.js
は各ページのテンプレートのようなものであり、ソースコード上でカスタマイズもできる。今回は特にカスタマイズしていなかったが、出力としては個別のファイルに出たのだろう。
クライアント用のコードは圧縮されており、pages配下のディレクトリを、クライアント用とサーバ用でそれぞれ生成しているのだということが予想できる。(_document.js
はサーバ側でしか実行されないため、サーバ用のコードしか吐かれていないのだろう)
npm start
してから curl localhost:3000
すると、以下のようなHTMLが取れた。
<!DOCTYPE html><html><head><meta charSet="utf-8" class="next-head"/><link rel="preload" href="/_next/8f79b495-de7d-47be-8e34-55823460818f/page/index.js" as="script"/><link rel="preload" href="/_next/8f79b495-de7d-47be-8e34-55823460818f/page/_app.js" as="script"/><link rel="preload" href="/_next/8f79b495-de7d-47be-8e34-55823460818f/page/_error.js" as="script"/><link rel="preload" href="/_next/static/commons/main-ee347f50f148624764ad.js" as="script"/></head><body><div id="__next"><ul><li><a href="/blog/first">My first blog post</a></li><li><a href="/blog/second">My second blog post</a></li><li><a href="/blog/last">My last blog post</a></li></ul></div><div id="__next-error"></div><script> __NEXT_DATA__ = {"props":{"pageProps":{}},"page":"/","pathname":"/","query":{},"buildId":"8f79b495-de7d-47be-8e34-55823460818f","assetPrefix":"","nextExport":false,"err":null,"chunks":[]} module={} __NEXT_LOADED_PAGES__ = [] __NEXT_LOADED_CHUNKS__ = [] __NEXT_REGISTER_PAGE = function (route, fn) { __NEXT_LOADED_PAGES__.push({ route: route, fn: fn }) } __NEXT_REGISTER_CHUNK = function (chunkName, fn) { __NEXT_LOADED_CHUNKS__.push({ chunkName: chunkName, fn: fn }) } false </script><script async="" id="__NEXT_PAGE__/" src="/_next/8f79b495-de7d-47be-8e34-55823460818f/page/index.js"></script><script async="" id="__NEXT_PAGE__/_app" src="/_next/8f79b495-de7d-47be-8e34-55823460818f/page/_app.js"></script><script async="" id="__NEXT_PAGE__/_error" src="/_next/8f79b495-de7d-47be-8e34-55823460818f/page/_error.js"></script><script src="/_next/static/commons/main-ee347f50f148624764ad.js" async=""></script></body></html>% [
グローバル変数を用いて、必要なデータをObjectとして渡しているのがわかる。 他の処理はhistory関連の処理だろうか?
気になるのは _next/8f79b495-...
みたいなファイルをダウンロードしようとしているところだ。 .next
のディレクトリと一対一で対応していない。
8f79b495-...
の値は .next/BUILD_ID
と同じもののようだ。これを見てNext.jsが動的にルーティングしているのだろうか。
また、各jsファイルはbundleディレクトリ配下のものと同じものっぽい。
$ cat .next/bundles/pages/_app.js | md5 9f6ae7fa86f03007f1ffd9b31e5581d6 $ curl http://localhost:3000/_next/8f79b495-de7d-47be-8e34-55823460818f/page/_app.js | md5 9f6ae7fa86f03007f1ffd9b31e5581d6
以上の処理を見ると、レンダリング結果のHTMLを返しつつも、クライアント側に必要なjsファイルは非同期でダウンロードしているようだ。
また、別のページとなる blog.js
はダウンロードしていないということにも注意したい。
次は、ブラウザからlocalhost:3000にアクセスし、画面遷移を行なってみる。
リンクをクリックして画面遷移すると、 blog.js
だけが新たにダウンロードされて画面遷移が行われた。
ここはSPAの挙動となり、スムーズな画面遷移が行えるようになっている。