Closed10

【key-front】より速いWEBを目指すNext.js / nextjs-make-the-web-fasterを読んでみた

1zushun1zushun

モチベーション

  • 毎週金曜日Slackのkey_frontチャンネルでハドル機能を使いお題に対してメンバー同士ディスカッションをする時間を15〜30分程度設けている
  • 今回は「より速いWEBを目指すNext.js / nextjs-make-the-web-faster」の共有会
  • ファシリテーターは筆者なので、事前に読み込んで気になった点などをスクラップに投げていく
  • 開催日は9/1(金)で最終的に議事録として結論をまとめる

補足

  • 前回「Next.jsのSPA」に触れていたので今回はNext.jsのコア機能にフォーカスを当てる
  • Next.js13は次回取り上げるので、最新の情報とは違うかもしれないので注意

https://zenn.dev/shuuuuuun/scraps/74aa82d164028f

参考資料

https://speakerdeck.com/takefumiyoshii/nextjs-make-the-web-faster

1zushun1zushun

メモ1

今回の資料から取り上げるのは下記の3点。補足なども適宜摘みながら見ていく。

  • code-splitting
  • pre-fetching
  • pre-rendering
1zushun1zushun

適宜補足を入れながら摘んでいく

code-splitting

SPAの課題であるページ単位でのスクリプト分割は自動でやってくれる

Next.jsの様なフレームワークがなかったころSPAは単一のchunkに統合されてしまうなど、適切にchunk分割されているとは限りませんでした。

https://speakerdeck.com/takefumiyoshii/nextjs-make-the-web-faster?slide=107

SPA(CSRのみ)の場合はどうすればいいのか?

もともとはLaravel⇄Blade⇄Vue.jsのモノレポ案件でpage.js使ってchunkを分ける実装を知っていてメリットを理解していた。なのでSPA案件を始めるにあたってコード分割は早めの段階で実装に取り組むようキャッチアップしていた。

React Router・React.lazy・React.Suspenseでルート単位でコードを分割(code-splitting)するができる

https://ja.legacy.reactjs.org/docs/code-splitting.html#route-based-code-splitting

React.lazyは現在default exportのみサポートしているがタイプセーフなnamed exportでもcode-splittingできる(検証済み)

https://github.com/facebook/react/issues/14603#issuecomment-726551598

1zushun1zushun

適宜補足を入れながら摘んでいく

pre-fetching

はじめに

初めはgetServersidePropsやgetStaticPropsのこと(pre-rendering)かと思っていたが、そうではなくnext/linkの裏側の挙動についてのことだった

prefetchのメリット

Prefetchは高速なナビゲーションを実現し、視覚的安定性に貢献します

→具体的にはどうやって?

ここで<Link>コンポーネントと前回のkey-frontで取り上げた直接アクセス・SPAナビゲーションに備え「両方」用意されるに繋がる。色々と後述するが両方用意されるがその出しわけのトリガーは<Link>コンポーネントぽい。

https://speakerdeck.com/takefumiyoshii/nextjs-make-the-web-faster?slide=102

<Link> を使っていると、Pre-renderingで事前に作成されたHTMLでも、遷移時はJSによる画面切り替えを実施してくれます。

https://zenn.dev/fukurose/articles/e15df7129cc421#pre-rendering

前回のkey-frontでも取り上げた直接アクセス・SPAナビゲーションに備え「両方」用意されるの補足をする

https://zenn.dev/link/comments/8fdca7bf582c18

実際にnetworkタブで検証すればわかるが次の記事で画像ありで説明がされていた。<Link> を使ってナビゲーションするか初回描画で生成するものが異なることがわかる。

https://nishinatoshiharu.com/react-difference-csr-ssr/

1zushun1zushun

適宜補足を入れながら摘んでいく

SWR, react-queryのprefetchとnext/linkのprefetch

next/linkのprefetchはSWRのprefetchと同等の効果がある

https://speakerdeck.com/takefumiyoshii/nextjs-make-the-web-faster?slide=99

SWR, react-queryの場合

  • トリガーがない(以下参照)

    より速い WEB を目指す Next.js / nextjs-make-the-web-faster

  • 関係ないけどtakepepeさんはnext/linkに相当するものを作ってた(next/linkと同じように振る舞うcomponent)

    これら課題解決のため、できあがったものが以下の<Link>コンポーネントです。通常のnext/link挙動を残しつつ、props を拡張しました。SWR の Programmatically Prefetch を、next/linkの ISR Prefetch と同様に Intersection Observer・マウスオーバーで発火しています。

    next/link の Prefetching と型安全

  • 普通に考えたら遷移したページで次に遷移できる箇所をprefetchする感じになると思う。が過度なprefetchに繋がりそう

next/linkの場合

  • next/linkはリンク先のページにgsspが定義されている場合マウスオーバーをトリガーに遷移先のgsspを実行する(資料参照)

    より速い WEB を目指す Next.js / nextjs-make-the-web-faster

  • next/linkは大味なprefetchがデフォルトとなので注意が必要とのこと

  • 公式ドキュメントではviewportに入ったらpre-fetchingとのこと(検証済み)→これは仕様が変わったってこと?

next/linkはバックグラウンドでページ(hrefで示される)をプリフェッチします。これはクライアントサイドナビゲーションのパフォーマンスを向上させるのに有用である。ビューポート内のすべての <Link /> が(初期状態またはスクロールによって)プリロードされます。

https://nextjs.org/docs/pages/api-reference/components/link#prefetch

補足

router.pushでpre-fetching

https://zenn.dev/d_suke/articles/0fc7670b2da750f6dd87

1zushun1zushun

適宜補足を入れながら摘んでいく

CSR・SSR・SSG・ISR

  • CSR:空のHTMLを返してブラウザでレンダリングする
  • SSR:リクエストごとにサーバサイドでHTMLを組み立ててブラウザに返す
  • SSG:ビルド時にHTMLを組み立ててリクエスト時にそのHTMLをブラウザに返す
  • ISR:stale-while-revaildateしてくれるSSG

上記は調べれば資料がたくさん出てくるので割愛する。どちらかと言うと今まで勘違いしていたことや用語などにフォーカスする

メモ2

  • 「pre-rendering」は「事前レンダリング」と言われている
  • 事前レンダリングで出てくる「中身が空のHTML」や「最小限のJSコード」は次のことを指している
index.html
// 中身が空のHTML
<html>
  <head>
    <title>sample</title>
  </head>
  <body>
    <div id="app"></div>
    <script src="/js/bundle.js"></script> //「最小限のJSのコード」
  </body>
</html>
1zushun1zushun

適宜補足を入れながら摘んでいく

メモ3

自分がNext.jsに対して誤解していたことと同じ内容だったのでメモ。

私はnextjsを学んでいて、ある場所ではnextjsは最初のページだけをプリレンダリングすると書いてあり、他の場所ではnextjsはデフォルトですべてのページをプリレンダリングすると書いてあるので、どちらが本当なのか理解できません。

https://stackoverflow.com/questions/73812425/does-nextjs-prerender-all-pages-or-only-the-first-page

恐らく上記の質問者は自分と同じことを考えていて、自分が疑問に思ったのはPre-renderingでよく見る次の説明。

Pre-renderingは事前にHTMLを生成することです。Next.jsではデフォルトで全ページをPre-rendering
してくれています

誤解1

デフォルトで全ページをPre-renderingされている(HTMLを生成している)のかと思っていて、それはつまりMPAではないのかとずっと思っていた。

けれどこの説明は事実ではあるけれど色々と補足情報が足りていないと調査を進めていく中で分かった。

誤解2

またSPAがSSRやSSGと同等に比較されている記事も見ていてそもそも視点がずれていたこともある。SPAがCSRっぽい説明だったりというのもあったような気がする。

けれどこれもよく見れば補足があったりと自分が詳細まで目を通せなかったのが原因かもしれない。

1zushun1zushun

補足

リクルートの研修資料と同じ内容だけどより詳細に違う角度から説明しており、非常に分かりやすかった。

リクルートの研修資料では直接アクセス・SPAナビゲーションに備え「両方」用意されると説明されていたが下記の方がもっと具体的でわかりやすいかも

Next.jsのレンダリングは、大きく分けて、ページの再読み込み時、画面遷移時の2つに分かれる。

実際に挙動を見るDEMOアプリもあるのでリクルートの研修資料でざっと大枠を理解してから記事を見るのがおすすめ。

https://speakerdeck.com/recruitengineers/nextjs-2023

https://magicode.io/Sumiren/articles/9a182ba35cc0410fbe6917d84eff2488

1zushun1zushun

後で肉付けする

一旦まとめ

ReactとNext.jsは次のように分類することができる

  • React = SPA(CSRのみ)
  • Next.js = SPA(CSR + 事前レンダリング)

Next.jsはReactのつらみを解消してくれる(課題解決をしてくれる)

  • ルーティング(Reactデフォルトではない、react-routerで可能)
    →Next.jsではFile-Based-Routingになる

  • コード分割(Reactデフォルトではない、react-router・React.lazy・React.Suspenseで可能)
    →Next.jsでは自動でcode-splittingをする

  • 初期描画のパフォーマンス
    →Next.jsではpre-renderingで初期描画の改善が可能

  • SPAナビゲーション(client-dide-navigation)
    →Next.jsではpre-fetchingをして本来あるSPAの強みを活かす =厳密にはcode-splitting(JSコードの自動分割)とpre-fetching(コンポーネントの事前生成)という技術を用いる

他にも色々あるが割愛します。

https://zenn.dev/ttani/articles/nextjs_basic_learning

Next.jsに思っていた誤解

  • SPA・SSR・SSGは同じ括りではない
    →SPAの枠の中にSSR・SSGがあるイメージ。そもそもSSR・SSGはレンダリングで、ここのSPAはCSRを指していると思っていてこれがこんがらがる要因ぽい。

  • Next.jsではデフォルトで全ページをpre-renderingする = MPAではない
    →初期描画はHTMLを表示するがSPAナビゲーション(厳密にはnext/linkなどの遷移)ではJSONでDOMの書き換えをする

あとはkey-front当日に結論(今回の内容の感想など)を出す

1zushun1zushun

議事録_20230901

  • 9/1(金)に実施
  • 結論を出すコンテンツではなかったため特に結論はなし
  • 参加人数は6名(以下エビデンス)
  • 本当は次回React.js18やNext.js13周りをやろうかと思っていたが先にreact-testing-libraryを実施する
このスクラップは2023/09/01にクローズされました