【Next.js】dynamic import(動的インポート)の概要と使いどころ

JavaScript

dynamic import(動的インポート)について

dynamic import(動的インポート)とは動的にimportを実行するための機能です。ES2020でJavaScriptの仕様として導入されました。dynamic importを利用すると非同期でモジュールを読み込めます。

例えば、初期表示で読み込み不要なモジュールにdynamic importを適用すると初期ロードの負荷を軽減でき、表示速度を改善できます。

Next.jsでdynamic importを利用する方法

Next.jsはdynamic importをサポートしており、next/dynamicで利用できます。

import対象がdefault exportなのかnamed exportなのかによってdynamic importの記述方法が異なります。以下ではそれぞれについて紹介します。

default export(デフォルトエクスポート)をdynamic importする場合

../components/helloというモジュール(ファイル)でdefault exportされたコンポーネントをdynamic importする場合は以下のようになります。

import dynamic from "next/dynamic";

const DynamicComponent = dynamic(() => import('../components/hello'));

named export(名前付きエクスポート)をdynamic importする場合

../components/helloというモジュール(ファイル)でnamed exportされたコンポーネントをdynamic importする場合は以下のようになります。
named exportの場合はthen()でモジュールを受け取り、モジュールを通じてコンポーネントにアクセスします。

export側

export const Hello = () => {
  return <p>Hello!</p>;
};

import側

import dynamic from "next/dynamic"

// JSXの場合はdynamicの直後の『<{}>』は不要
const Hello = dynamic<{}>(() =>
  import("../components/hello").then((module) => module.Hello)
);

Next.jsにおけるdynamic importのつかいどころ

Next.jsにおけるdynamic importのつかいどころは主に『SSR回避』と『ロード時のパフォーマンス改善』の2点です。
以下ではそれぞれについて紹介します。

dynamic importを利用したSSR回避について

Next.jsのページのレンダリングはデフォルトでPre-rendering、つまりSSR(Server-side Rendering)もしくはSSG(Static Site Generation)です。1

サーバサイドではブラウザの機能は利用できないので、サーバサイドでのレンダリング時にブラウザの機能を利用すると以下のようなサーバエラーになります。

上記の問題はSSR回避のオプション(ssr: false)を付与したdynamic importによって解決できます。
SSR回避とはサーバサイドで処理が実行されないように制御することです。サーバサイドで実行するとサーバエラーになってしまう処理に対してSSR回避が利用されます。

以下のコードはdynamic importを利用したSSR回避の例です。
dynamic importによりサーバサイドでWindowが参照されなくなるため、サーバエラーを防げます。

import dynamic from "next/dynamic";
const Alert = dynamic(() => import("../components/Alert"), { ssr: false });

const App = () => {
  return (
    <>
      <div>App Page</div>
      <Alert />
    </>
  );
};

export default App;

// サーバサードで当該コンポーネントを呼び出すとwindowが参照できずサーバエラーになる

const Alert: React.FC = () => {
  const showAlert = () => {
    window.alert("Hello");
  };

  return <>{showAlert()}</>;
};

export default Alert;

dynamic importを利用したロード時のパフォーマンス改善

初回表示で不要なモジュールにdynamic importを適用することにより、ロード時の読み込み対象が減るためパフォーマンス改善につながります。

以下は初期表示でロードする必要のないLargeListコンポーネントをdynamic importで非同期に読み込む例です。

import { useState } from "react";
import dynamic from "next/dynamic";

// Listコンポーネントはサイズが大きく初期表示の遅延の原因になるものと想定
const LargeList = dynamic(() => import("../components/LargeList"));

const App = () => {
  const [isVisible, setIsVisible] = useState(false);
  return (
    <>
      <button onClick={() => setIsVisible(!isVisible)}>
        Toggle Visible Status
      </button>
      {isVisible && <LargeList />}
    </>
  );
};
export default App;

next/dynamicのオプションについて

next/dynamicにはSSR回避をはじめとしたオプションが用意されています。next/dynamicのオプションは以下の通りです。2

next/dynamicのオプション
  • ssr
  • suspense
  • loading

以下ではそれぞれについて紹介します。

ssr: SSR回避するためのオプション

サーバサイドで呼び出したくないコンポーネントに対して使うオプションです。
SSR回避のサンプルコードは前述したため省略します。

loading: カスタムローディングを利用するためのオプション

dynamic importが完了するまでの間の画面表示をカスタマイズしたい時に利用するオプションです。
{ loading: () => ローディング中に表示させたい内容}とすることでカスタムローディングを実装できます。

具体的なコードは以下の通りです。
以下の例ではLargeListコンポーネントをロードしている間、LargeListコンポーネントの代わりにNow Loading...の文字列が表示されます。

import { useState } from "react";
import dynamic from "next/dynamic";

const LargeList = dynamic(() => import("../components/LargeList"), {
  loading: () => <p>Now Loading...</p>,
});

const App = () => {
  const [isVisible, setIsVisible] = useState(false);
  return (
    <>
      <button onClick={() => setIsVisible(!isVisible)}>
        Toggle Visible Status
      </button>
      {isVisible && <LargeList />}
    </>
  );
};
export default App;

ローディング時間が短すぎてloadingの挙動がわかりにくい場合はChrome DevToolsのNetworkタブから通信速度の変更をします。
通信速度の変更方法はChrome DevtoolsのNetwork features reference#Emulate slow network connectionsを参考にしてください。

suspense: Suspenseコンポーネントと組み合わせる場合のオプション

dynamic importしたコンポーネントを<Suspense>の子コンポーネントで利用する場合に使うオプションです。

Suspenseはreact 18で正式導入される機能です3。2021年11月現在、reactの最新版は17.0.2ですのでsuspenseの説明はNext.js『Dynamic Import#With suspense』の参考実装を紹介するだけにとどめておきます。

// https://nextjs.org/docs/advanced-features/dynamic-import#with-suspense より引用

import dynamic from 'next/dynamic'

const DynamicLazyComponent = dynamic(() => import('../components/hello4'), {
  suspense: true,
})

function Home() {
  return (
    <div>
      <Suspense fallback={`loading`}>
        <DynamicLazyComponent />
      </Suspense>
    </div>
  )
}

今回のまとめ

dynamic importのまとめ
  • dynamic importを利用すると非同期でimport処理ができる
  • named exportとdefault exportでdynamic importの記述方法が異なる
  • Next.jsにおけるdynamic importの使いどころSSR回避とロード時のパフォーマンス改善
  • dynamic importのオプションにはssr, loading, suspenseがある

さいごに

Twitter(@nishina555)やってます。フォローしてもらえるとうれしいです!

参考資料