|
| 1 | +# chapter18 (Next/8th) |
| 2 | +## 静的レンダリングと動的レンダリング |
| 3 | + |
| 4 | +これまでのチャプターでは,ダッシュボードの概要ページに必要なデータを取得しました.前の章でも触れましたが,現在の実装では次の2点の制約があります. |
| 5 | +1. データリクエストが無意識的にwaterfallを形成している |
| 6 | +2. ダッシュボードは静的で,データが更新されてもアプリケーション上の表示は変化しない |
| 7 | + |
| 8 | +これを踏まえて,この章では |
| 9 | +- 静的レンダリングとは何か,パフォーマンス向上につながる理由 |
| 10 | +- 動的レンダリングとは何か,いつ用いるべきか |
| 11 | +- dashboardを動的なものにする他のアプローチ |
| 12 | +- データ取得の遅延とその影響 |
| 13 | + |
| 14 | +を学びます. |
| 15 | + |
| 16 | +## 静的レンダリングとは |
| 17 | +静的レンダリングを用いる場合,データの取得とレンダリングはサーバー上でのビルド時(デプロイ時)や[データを再検証するタイミング][link:reValidation]に行われます.データの取得結果は[CDN(Content Delivery Network)][link:CDN]上に配布され,キャッシュされます. |
| 18 | + |
| 19 | +![img:cache_data] |
| 20 | + |
| 21 | +ユーザがアプリケーションを訪れたときは,キャッシュされたコンテンツが表示されます. |
| 22 | + |
| 23 | +静的レンダリングの利点をまとめると次のようになります. |
| 24 | + |
| 25 | +- アプリの高速化 |
| 26 | + - プリレンダリングされたコンテンツはキャッシュされ,グローバルに配布されます.このおかげで,世界中の人間がアプリを見れるし.読み込みが速くなる. |
| 27 | +- サーバの応答回数を減らす |
| 28 | + - コンテンツがキャッシュされているので,サーバーはユーザのリクエスト毎に直接コンテンツを生成する必要がなくなる. |
| 29 | +- SEO |
| 30 | + - あらかじめレンダリングされたコンテンツはページ読み込み時にはすでに利用可能となっているため,検索エンジンのcrawlerがインデックス付けしやすく,検索結果の上位にひょじされやすくなります. |
| 31 | + |
| 32 | +静的レンダリングは**データがない**若しくは**ユーザ間で共通のデータ**を扱うUIにとっては便利です.しかし今回のdashboardのような日常的に更新されうるようなユーザ個人との結びつきの強いデータを扱うケースにとっては不向きです. |
| 33 | + |
| 34 | +静的レンダリングの対になるのが動的レンダリングです. |
| 35 | + |
| 36 | +## 動的レンダリングとは |
| 37 | +動的レンダリングを用いると,コンテンツはサーバー上で**ユーザがページを訪れた瞬間**にレンダリングされます.動的レンダリングを用いることの利点は次のようです. |
| 38 | +- リアルタイムのデータ |
| 39 | + - 動的レンダリングではリアルタイムであったり,頻繁に変化するようなデータを表示することがてきます.頻繁にデータが更新されるようなアプリケーションでは,動的レンダリングを採用するのが最適です. |
| 40 | +- ユーザー固有のコンテンツ |
| 41 | + - dashboardsやユーザプロファイルなど,ユーザ個人と紐づいたデータを提供したり.ユーザのインタラクションに応じてデータを更新したりすることが容易になります. |
| 42 | +- リクエスト時の情報 |
| 43 | + - 動的レンダリングはcookiesやURLの検索パラメータなど,ユーザがページを訪れたときにしか取得できないような情報を利用することができます. |
| 44 | + |
| 45 | +## dashboardページを動的にするアプローチ |
| 46 | +デフォルトでは`@vercel/postgres`はそれ自身のきゃしんぐセマンティクスを設定していません.このため,フレームワークは独自の静的及び動的動作を設定できるようになります. |
| 47 | + |
| 48 | +Nezxt.jsの`unstable_noStore`APIを使用することで,サーバーコンポーネントやデータ取得関数内で静的なレンダリングを無効にすることができます. |
| 49 | + |
| 50 | +> `unstable_noStore`は開発段階で,今後変更される可能性があります. |
| 51 | +
|
| 52 | +`app/lib/data.ts`内で`next/cache/unstable_noStore`をインポートし,データ取得を行う関数の頭で呼び出します. |
| 53 | +```diff ts |
| 54 | + // ... |
| 55 | ++ import { unstable_noStore as noStore } from 'next/cache'; |
| 56 | + |
| 57 | + export async function fetchRevenue() { |
| 58 | + // Add noStore() here to prevent the response from being cached. |
| 59 | + // This is equivalent to in fetch(..., {cache: 'no-store'}). |
| 60 | ++ noStore(); |
| 61 | + |
| 62 | + // ... |
| 63 | + } |
| 64 | + |
| 65 | + export async function fetchLatestInvoices() { |
| 66 | ++ noStore(); |
| 67 | + // ... |
| 68 | + } |
| 69 | + |
| 70 | + export async function fetchCardData() { |
| 71 | ++ noStore(); |
| 72 | + // ... |
| 73 | + } |
| 74 | + |
| 75 | + export async function fetchFilteredInvoices( |
| 76 | + query: string, |
| 77 | + currentPage: number, |
| 78 | + ) { |
| 79 | ++ noStore(); |
| 80 | + // ... |
| 81 | + } |
| 82 | + |
| 83 | + export async function fetchInvoicesPages(query: string) { |
| 84 | ++ noStore(); |
| 85 | + // ... |
| 86 | + } |
| 87 | + |
| 88 | + export async function fetchFilteredCustomers(query: string) { |
| 89 | ++ noStore(); |
| 90 | + // ... |
| 91 | + } |
| 92 | + |
| 93 | + export async function fetchInvoiceById(query: string) { |
| 94 | ++ noStore(); |
| 95 | + // ... |
| 96 | + } |
| 97 | +``` |
| 98 | +## データ取得が遅延した場合をシミュレーションする |
| 99 | +ダッシュボードを動的レンダリングするようにしました.が,前の章で問題になった,ある1つのデータ取得が他のものより遅かった場合を考えてみます. |
| 100 | + |
| 101 | +`/app/lib/data.ts`にて,コメントアウトを解除して疑似的にデータ取得の遅延を生じさせます. |
| 102 | + |
| 103 | +```diff ts |
| 104 | + export async function fetchRevenue() { |
| 105 | + try { |
| 106 | + // We artificially delay a response for demo purposes. |
| 107 | + // Don't do this in production :) |
| 108 | ++ console.log('Fetching revenue data...'); |
| 109 | ++ await new Promise((resolve) => setTimeout(resolve, 3000)); |
| 110 | + |
| 111 | + const data = await sql<Revenue>`SELECT * FROM revenue`; |
| 112 | + |
| 113 | ++ console.log('Data fetch completed after 3 seconds.'); |
| 114 | + |
| 115 | + return data.rows; |
| 116 | + } catch (error) { |
| 117 | + console.error('Database Error:', error); |
| 118 | + throw new Error('Failed to fetch revenue data.'); |
| 119 | + } |
| 120 | + } |
| 121 | +``` |
| 122 | +実際にdashboardページに飛んでみると,3秒後に画面が更新されます.このように,動的レンダリングを用いた場合,アプリケーションの動作速度は**最も遅いデータ取得に依存**します. |
| 123 | + |
| 124 | + |
| 125 | +[link:reValidation]: https://nextjs.org/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating#revalidating-data |
| 126 | + |
| 127 | +[link:CDN]: https://developer.mozilla.org/ja/docs/Glossary/CDN |
| 128 | + |
| 129 | +[img:cache_data]: ./static_render.png |
0 commit comments