【9章】Next.jsのチュートリアルをやってみた

Next.js

 

これはNext.jsの公式チュートリアルの9. Streaming に関するメモです

本章では、データ取得が遅い時にもUXを向上させる方法を学びます

 

前章のメモ

 

Next.jsの公式チュートリアルの該当ページ

 

Learn Next.js: Streaming | Next.js
Improve your application's loading experience with streaming and loading skeletons.

 

学ぶこと

 

  • ストリーミングとは何か?いつ使うのか?
  • loading.tsxSuspenseを使用してストリーミングを実装する方法
  • スケルトンスクリーンとはなにか?
  • ルートグループとは何か?いつ使うのか?
  • Suspenseの境界をどこにするか?

 

ストリーミングとは何か?

 

ストリーミングとは、

  • ページを小さな塊(チャンク)に分割し、準備できたものから表示していく技術
  • コンポーネント単位でチャンクに分割。
  • 処理が遅いコンポーネントの影響でページが表示されないことを防ぐ。

 

実装方法としては

  • ページに対しては、loading.tsx を使う
  • コンポーネントに対しては、<Suspense> を使う

の2通り。

 

ページ全体をストリーミング

loading.tsx

 

ダッシュボードページでストリーミングを使ってみます!

ページ全体に適用させたいので /app/dashboard/loading.tsx を作成します

 

ローカルサーバを起動して http://localhost:3000/dashboard にアクセスするとLoadingと表示

数秒後、今までのダッシュボード画面が表示されるようになりました

 

ポイントとしては

  1. ページ読み込み中にloading.tsxで定義したUIが表示される
  2. <SideNav>は静的なので、データ取得中でも表示される
  3. ページ読み込み中でも別のページに遷移できる

 

スケルトンスクリーンの実装

 

スケルトンスクリーンとは、ページ読み込み中に灰色のコンテンツが表示されるもの。

Youtubeとかで使われています(読み込み中はサムネが灰色になっていますね)

 

今回のチュートリアルでは、すでにこのUI(DashboardSkeleton)が用意されています

/app/dashboard/loading.tsx で DashboardSkeletonをインポートして使ってみましょう

 

http://localhost:3000/dashboard をリロードしてみると、、、

 

スケルトンスクリーンが実装されていますね!

 

スケルトンスクリーンをダッシュボード画面でのみ使用する

 

現在の /app/dashboard/ 配下は↓。

 

/app/dashboard/loading.tsx でスケルトンスクリーンを実装したんですが、

  • /app/dashboard/

だけでなく、

  • /app/dashboard/customers/
  • /app/dashboard/invoices/ 

にも適用されるんですね。これが。

 

本アプリでは、/app/dashboard/ だけに適用したいのでRoute Groupsという機能を使います

これは、フォルダ名を () で囲うことでパスに影響を与えず、フォルダ構成をいじれます

 

今回の場合だと、/app/dashboard/ 配下に (overview) というフォルダを作成します。

そこに page.tsx loading.tsx を入れます(↓参照)

 

(overview) フォルダはパスに影響を与えないので、

http://localhost:3000/dashboard にアクセスすると /app/dashboard/(overview)/page.tsxを表示。

 

このようにパスを変更せずにファイルの影響範囲をコントロールできるのがRoute Groups

 

コンポーネントをストリーミング

Suspense

 

ページ全体ではなく特定のコンポーネントをストリーミングするにはSuspenseを使います

 

現状 fetchRevenue() の処理を重たくしているので

このデータを使っている<RevenueChart>にSuspenseを使ってストリーミング

コードとしては、

  1. <RevenueChart>で fetchRevenue() を実行するように変更
  2. <Suspense>を<RevenueChart>に適用

Suspenseは囲ったコンポーネントに効くので

そのコンポーネント内で重たい処理をさせないと意味がないのかなと。

なので、fetchRevenue() の実行場所を移しています

 

まずは、/app/dashboard/(overview)/page.tsx からfetchRevenue() を削除

 

<RevenueChart>で fetchRevenue() を実行します。

また、引数で受け取る必要がなくなったので削除します

 

最後に引数を削除したのでそれも対応しつつ<RevenueChart>を<Suspense>で囲みます

Suspenseのfallbackには子コンポーネントが表示されるまでのUIを設定。

 

ローカルサーバを立ち上げて

http://localhost:3000/dashboard にアクセスして左のグラフがスケルトンスクリーンならOK!

よく見ると初めの方はすべてスケルトンスクリーンになっているので、
/app/dashboard/(overview)/loading.tsx も効いていますね!!

 

<LatestInvoices> もストリーミングしてみる

 

流れはさっきといっしょ。

  1. <LatestInvoices>で fetchLatestInvoices() を実行するように変更
  2. <Suspense>を<LatestInvoices>に適用

 

まずは、/app/dashboard/(overview)/page.tsx からfetchLatestInvoices() を削除

 

<LatestInvoices>で fetchLatestInvoices() を実行します。

また、引数で受け取る必要がなくなったので削除します

 

最後に引数を削除したのでそれも対応しつつ<LatestInvoices>を<Suspense>で囲みます

Suspenseのfallbackには子コンポーネントが表示されるまでのUIを設定。

 

スケルトンスクリーンを実装できたかわかりやすくするためにfetchLatestInvoices() を変更。

fetchRevenue() よりも実行時間が長くなるようにしておく。

 

ローカルサーバを立ち上げて

http://localhost:3000/dashboard にアクセスしてグラフの右側がスケルトンスクリーンならOK!

 

コンポーネントのグループ化

 

つづいて<Card>コンポーネントをStreamingしたいのですが、コンポーネントが複数あります。

個々にSuspenseで囲うと表示できるようになったコンポーネントから順に表示され

ユーザが不快に感じる可能性があります。

 

また、複数のコンポーネントを一気にSuspenseで囲っても大丈夫です。

が、今回は同じコンポーネントであり、fetchCardData() が複数回はしるのがよろしくない。

 

そんな時はラッパーコンポーネントを使っていきます

つまり、<Card>を4つ含む新しいコンポーネントを作成しそれをSuspenseで囲います

 

コードとしては

  1. <CardWrapper>のコメントアウトを外し fetchCardData() を実行するように変更
  2. <Card>を削除して、<Suspense>を<CardWrapper>に適用

 

今回のアプリではラッパーコンポーネントである<CardWrapper>があるので、

その中身のコメントアウトを外します

また、fetchCardData() を実行するようにコードを追加します

 

/app/dashboard/page.tsx から<Card>をすべて削除。

代わりに<Suspense>で囲った<CardWrapper>を追加

 

スケルトンスクリーンを実装できたかわかりやすくするためにfetchCardData() を変更。

fetchRevenue() よりも実行時間が長くなるようにしておく。

 

ローカルサーバを立ち上げて

http://localhost:3000/dashboard にアクセスして画面上部のカードがスケルトンスクリーンならOK!

 

Streamingはページ全体?単一もしくは複数コンポーネント?

 

Streamingをページ全体やコンポーネント単位に適用させる方法を見てきました。

では、どう使い分ければいいのでしょうか??

 

データを取得する時間やUXなど各アプリごとに要件が異なるので一概には定義できません

結局はメリデメを把握して自分で選択できるようになるしかないのかなと。

 

今回だと

  • ページ全体:loading.tsxで一気に定義できるが処理が長いコンポーネントに引っ張られる
  • 単一のコンポーネント:個別に制御できるが個々に表示されるようになる
  • 複数のコンポーネント:段階的に表示できるが、ラッパーコンポーネントが必要

 

いろいろ試してみてその時の最適解を探してみましょう!!

 

次章のメモ

 

コメント

タイトルとURLをコピーしました