🔍

App Router導入後のNext.js開発におけるDead Code Eliminationの活用

2023/07/01に公開

10秒で知りたい人向け

Next.jsにおけるページ作成とコード最適化の方法について書いてます。

App Routerを導入する場合、コードがどの環境で実行されるかの予測が難しくなりそうなので、code eliminationツールを活用するといいかも。

はじめに ~Next.jsでのページ作成について~

Next.jsでページを作成する際、サーバーだけで実行されるコードがある一方、バンドルに追加されてブラウザに返されるコードもあります。

Next.jsの初心者(自分)の場合、これらを区別するのは難しく、通常サーバーサイドで行われるようなデータベースへのクエリを送るコードをクライアントサイドに含めてしまうかもしれません。

そこでNext.jsは、この点についてより明確にするためのツールを提供しています。

https://next-code-elimination.vercel.app/

いきなり、エディタが開いて分けわからん人向けに記事を書きます。

このツールがあると何がいいの?

結論、パフォーマンス向上なのですが、その前に以下の説明を...

Automatic Static Optimizationとは?

Next.jsは「Automatic Static Optimization」という機能があります。

https://nextjs-ja-translation-docs.vercel.app/docs/advanced-features/automatic-static-optimization

上記を意訳

Automatic Static OptimizationはNext.jsの機能の1つです。

ページが同期的にデータを要求しない場合(つまり、ページが表示されるためにサーバーからデータを動的に取得する必要がないという意味)、Next.jsがそのページを自動的に静的(プリレンダリング可能)と判断し、静的なHTMLを返す特徴があります。

Next.jsの機能を用いて説明すると、ページがサーバーサイドのデータを必要としない場合とは、つまり、getServerSidePropsを使わずに完全にクライアントサイドでレンダリングできる場合そのページは自動的に静的なページとして解釈されます。

Next.jsでのコード最適化の具体例

これが何を意味するのか、そして、Next.jsのコード最適化がどのように働くのかを理解するために「商品詳細」ページを例にしてみましょう。

https://next-code-elimination.vercel.app/s/0SLDOcY_A

import { GetServerSideProps } from "next";

const displayPrice = (): string => {
  return "1000円";
};

const fetchItemData = (): string => {
  return "このアイテムはとても人気です";
};

// サーバサイドで実行する処理(getServerSideProps)を定義
export const getServerSideProps: GetServerSideProps = async (context) => {
  return {
    props: {
      itemData: fetchItemData(),
    },
  };
}

const ItemDetails: NextPage = () => {
  return <h1>アイテムの価格: {displayPrice()}</h1>;
};

export default ItemDetails;

このコードには、displayPrice()fetchItemData()という2つの関数が存在します。

補足

仮のコードですが、以下のようなユースケースを想定してます

  • displayPrice()はアイテムの価格を返す関数
  • fetchItemData()はアイテムに関するデータ(ここでは人気度)を返す関数

displayPrice()はページコンポーネント内で呼び出されており、fetchItemData()getServerSideProps()内で使用されます。

getServerSideProps()はNext.jsの特性で、サーバー側でデータを取得するために使用します。

しかし、Next.jsがこのページをクライアントにレンダリングするとき、下記のコードだけがブラウザに送られます

// This is the code that is bundled for the client-side:
const displayPrice = (): string => {
  return '1000円';
};
const ItemDetails: NextPage = () => {
  return <h1>アイテムの価格: {displayPrice()}</h1>;
};
export var __N_SSP = true;  // このページがサーバーサイドのデータを必要とする(つまりgetServerSidePropsを使う)ことを示す
export default ItemDetails;  // ItemDetailsコンポーネントをエクスポート

上記を見ると、fetchItemData()関数とgetServerSideProps()はブラウザ側のコードから除去されています。これはNext.jsが自動的に未使用のコードをツリーシェイキングすることで、コードを最適化しているからです。

ブラウザ側のコードからfetchItemData()関数とgetServerSideProps()が除去されているのは、Next.jsのDead Code Eliminationの機能によるものです。

ちなみに、下記は?

export var __N_SSP = true;

N_SSPという変数を定義し、それを真(true)に設定しています。

そしてexportにより、この変数を他のJavaScriptモジュールから参照できるようにしています。

N_SSPはNext.jsの内部で使われる特殊な変数で、ページがサーバーサイドで生成される(Server Side Propsを使用する)ことを示します。

Next.jsのビルドシステムはこのフラグを見て、ページの生成方法を決定してるらしい。

https://github.com/vercel/next.js/discussions/12558

App Routerとコードの実行環境の予測

App Routerの導入により、あるコードがサーバーサイドで実行されるのか、クライアントサイドで実行されるのか、という事前の予測が難しくなりました。(僕の感覚ですが)

https://zenn.dev/temasaguru/articles/0191cf919bd0a3

例えば、サーバーサイドで実行されるべきコードなのにクライアントサイドで実行されてしまったり、その逆の可能性もあります。これではパフォーマンスやセキュリティに悪影響を及ぼす可能性があります。

コードの実行環境(サーバーサイドかクライアントサイドか)が予測しにくくなれば、code eliminationツールを使用して、どのコードがクライアントサイドに送信されるかを確認することがますます重要になるでしょう。

具体的なシーンとしては、例えば環境変数へのアクセス、データベースへのクエリなどを行うようなコードが含まれている場合です。

これらの操作は通常サーバーサイドで行われ、その結果はgetServerSidePropsgetStaticPropsを通じてページに渡されます。これらのコードは、クライアントサイドでは実行されません。そのため、これらのコードがクライアントサイドのバンドルに含まれていないことを確認するためにcode eliminationツールが役立つと思います。

https://next-code-elimination.vercel.app/

参考

https://nextjs.org/docs/pages/building-your-application/data-fetching/get-server-side-props

Discussion