Comment charger en différé les composants clients et les bibliothèques

Le chargement différé dans Next.js aide à améliorer les performances de chargement initial d'une application en réduisant la quantité de JavaScript nécessaire pour rendre une route.

Il vous permet de reporter le chargement des composants clients et des bibliothèques importées, et de ne les inclure dans le bundle client que lorsqu'ils sont nécessaires. Par exemple, vous pourriez vouloir différer le chargement d'une modale jusqu'à ce qu'un utilisateur clique pour l'ouvrir.

Il existe deux façons d'implémenter le chargement différé dans Next.js :

  1. En utilisant les importations dynamiques avec next/dynamic
  2. En utilisant React.lazy() avec Suspense

Par défaut, les composants serveur sont automatiquement découpés en morceaux (code split), et vous pouvez utiliser le streaming pour envoyer progressivement des parties de l'interface du serveur vers le client. Le chargement différé s'applique aux composants clients.

next/dynamic

next/dynamic est une combinaison de React.lazy() et de Suspense. Il se comporte de la même manière dans les répertoires app et pages pour permettre une migration progressive.

Exemples

Importation de composants clients

app/page.js
'use client'

import { useState } from 'react'
import dynamic from 'next/dynamic'

// Composants clients :
const ComponentA = dynamic(() => import('../components/A'))
const ComponentB = dynamic(() => import('../components/B'))
const ComponentC = dynamic(() => import('../components/C'), { ssr: false })

export default function ClientComponentExample() {
  const [showMore, setShowMore] = useState(false)

  return (
    <div>
      {/* Charge immédiatement, mais dans un bundle client séparé */}
      <ComponentA />

      {/* Charge à la demande, uniquement si la condition est remplie */}
      {showMore && <ComponentB />}
      <button onClick={() => setShowMore(!showMore)}>Basculer</button>

      {/* Charge uniquement côté client */}
      <ComponentC />
    </div>
  )
}

Remarque : Lorsqu'un composant serveur importe dynamiquement un composant client, le découpage en morceaux (code splitting) automatique n'est pas actuellement pris en charge.

Désactivation du SSR

Lorsque vous utilisez React.lazy() et Suspense, les composants clients seront pré-rendus (SSR) par défaut.

Remarque : L'option ssr: false ne fonctionnera que pour les composants clients. Déplacez-la dans les composants clients pour garantir le bon fonctionnement du découpage en morceaux côté client.

Si vous souhaitez désactiver le pré-rendu pour un composant client, vous pouvez utiliser l'option ssr définie sur false :

const ComponentC = dynamic(() => import('../components/C'), { ssr: false })

Importation de composants serveur

Si vous importez dynamiquement un composant serveur, seuls les composants clients enfants du composant serveur seront chargés en différé - pas le composant serveur lui-même. Cela aidera également à précharger les ressources statiques telles que le CSS lorsque vous l'utilisez dans des composants serveur.

app/page.js
import dynamic from 'next/dynamic'

// Composant serveur :
const ServerComponent = dynamic(() => import('../components/ServerComponent'))

export default function ServerComponentExample() {
  return (
    <div>
      <ServerComponent />
    </div>
  )
}

Remarque : L'option ssr: false n'est pas prise en charge dans les composants serveur. Vous verrez une erreur si vous essayez de l'utiliser dans des composants serveur. ssr: false n'est pas autorisé avec next/dynamic dans les composants serveur. Veuillez le déplacer dans un composant client.

Chargement de bibliothèques externes

Les bibliothèques externes peuvent être chargées à la demande en utilisant la fonction import(). Cet exemple utilise la bibliothèque externe fuse.js pour la recherche floue. Le module n'est chargé côté client qu'après que l'utilisateur a saisi une recherche.

app/page.js
'use client'

import { useState } from 'react'

const names = ['Tim', 'Joe', 'Bel', 'Lee']

export default function Page() {
  const [results, setResults] = useState()

  return (
    <div>
      <input
        type="text"
        placeholder="Rechercher"
        onChange={async (e) => {
          const { value } = e.currentTarget
          // Charge dynamiquement fuse.js
          const Fuse = (await import('fuse.js')).default
          const fuse = new Fuse(names)

          setResults(fuse.search(value))
        }}
      />
      <pre>Résultats : {JSON.stringify(results, null, 2)}</pre>
    </div>
  )
}

Ajout d'un composant de chargement personnalisé

app/page.js
'use client'

import dynamic from 'next/dynamic'

const WithCustomLoading = dynamic(
  () => import('../components/WithCustomLoading'),
  {
    loading: () => <p>Chargement...</p>,
  }
)

export default function Page() {
  return (
    <div>
      {/* Le composant de chargement sera affiché pendant que <WithCustomLoading/> se charge */}
      <WithCustomLoading />
    </div>
  )
}

Importation d'exports nommés

Pour importer dynamiquement un export nommé, vous pouvez le retourner depuis la Promise renvoyée par la fonction import() :

components/hello.js
'use client'

export function Hello() {
  return <p>Bonjour !</p>
}
app/page.js
import dynamic from 'next/dynamic'

const ClientComponent = dynamic(() =>
  import('../components/hello').then((mod) => mod.Hello)
)

On this page