レンダリングモード

Nuxtで利用できるさまざまなレンダリングモードについて学びます。

Nuxtは、さまざまなレンダリングモード、ユニバーサルレンダリングクライアントサイドレンダリングをサポートしていますが、ハイブリッドレンダリングや、CDNエッジサーバーでアプリケーションをレンダリングする可能性も提供しています。

ブラウザとサーバーの両方がJavaScriptコードを解釈して、Vue.jsコンポーネントをHTML要素に変換できます。このステップはレンダリングと呼ばれます。Nuxtはユニバーサルクライアントサイドの両方のレンダリングをサポートしています。2つのアプローチには利点と欠点があり、それらについて説明します。

デフォルトでは、Nuxtはより良いユーザーエクスペリエンス、パフォーマンス、検索エンジンインデックスの最適化のためにユニバーサルレンダリングを使用しますが、1行の設定でレンダリングモードを切り替えることができます。

ユニバーサルレンダリング

このステップは、PHPやRubyアプリケーションによって実行される従来のサーバーサイドレンダリングに似ています。ブラウザがユニバーサルレンダリングが有効なURLをリクエストすると、Nuxtはサーバー環境でJavaScript (Vue.js) コードを実行し、完全にレンダリングされたHTMLページをブラウザに返します。Nuxtは、事前にページが生成されている場合、キャッシュから完全にレンダリングされたHTMLページを返すこともあります。ユーザーは、クライアントサイドレンダリングとは異なり、アプリケーションの初期コンテンツ全体を即座に入手できます。

HTMLドキュメントがダウンロードされると、ブラウザはこれを解釈し、Vue.jsがドキュメントを制御します。サーバー上で一度実行されたのと同じJavaScriptコードが、今度はクライアント(ブラウザ)上でバックグラウンドで再度実行され、リスナーをHTMLにバインドすることでインタラクティビティを可能にします(これがユニバーサルレンダリング)。これをハイドレーションと呼びます。ハイドレーションが完了すると、ページは動的なインターフェースやページ遷移といった利点を享受できます。

ユニバーサルレンダリングにより、Nuxtアプリケーションは、クライアントサイドレンダリングの利点を維持しながら、高速なページ読み込み時間を提供できます。さらに、コンテンツがすでにHTMLドキュメントに存在するため、クローラーはオーバーヘッドなしでコンテンツをインデックス化できます。

サーバーでレンダリングされるものとクライアントでレンダリングされるもの

ユニバーサルレンダリングモードでVueファイルの部分がサーバーとクライアントのどちらで実行されるかを尋ねるのは自然なことです。

app/app.vue
<script setup lang="ts">
const counter = ref(0) // executes in server and client environments

const handleClick = () => {
  counter.value++ // executes only in a client environment
}
</script>

<template>
  <div>
    <p>Count: {{ counter }}</p>
    <button @click="handleClick">
      Increment
    </button>
  </div>
</template>

最初のリクエストでは、<p>タグ内でレンダリングされるため、counter refはサーバーで初期化されます。handleClickの内容はここでは実行されません。ブラウザでのハイドレーション中に、counter refが再初期化されます。handleClickは最終的にボタンにバインドされます。したがって、handleClickの本体は常にブラウザ環境で実行されると推測するのが妥当です。

ミドルウェアページは、ハイドレーション中にサーバーとクライアントの両方で実行されます。プラグインは、サーバーまたはクライアント、あるいはその両方でレンダリングできます。コンポーネントもクライアント側のみで実行するように強制できます。コンポーザブルユーティリティは、その使用コンテキストに基づいてレンダリングされます。

サーバーサイドレンダリングの利点

  • パフォーマンス: ブラウザはJavaScriptで生成されたコンテンツよりも静的コンテンツをはるかに高速に表示できるため、ユーザーはページのコンテンツにすぐにアクセスできます。同時に、Nuxtはハイドレーションプロセス中にウェブアプリケーションのインタラクティブ性を維持します。
  • 検索エンジン最適化: ユニバーサルレンダリングは、従来のサーバーアプリケーションと同様に、ページのHTMLコンテンツ全体をブラウザに配信します。Webクローラーはページのコンテンツを直接インデックスできるため、ユニバーサルレンダリングは迅速にインデックスしたいコンテンツにとって優れた選択肢となります。

サーバーサイドレンダリングの欠点

  • 開発上の制約: サーバー環境とブラウザ環境では提供されるAPIが異なるため、両側でシームレスに実行できるコードを書くのは難しい場合があります。幸いにも、Nuxtはコードの実行場所を特定するのに役立つガイドラインと特定の変数を提供しています。
  • コスト: ページをオンザフライでレンダリングするには、サーバーを稼働させる必要があります。これは、従来のサーバーと同様に月々のコストが発生します。ただし、ブラウザがクライアントサイドナビゲーションを引き継ぐユニバーサルレンダリングのおかげで、サーバー呼び出しは大幅に削減されます。エッジサイドレンダリングを活用することで、コスト削減が可能です。

ユニバーサルレンダリングは非常に汎用性が高く、ほとんどすべてのユースケースに適合し、特にコンテンツ指向のウェブサイト(ブログ、マーケティングウェブサイト、ポートフォリオ、Eコマースサイト、マーケットプレイス)に適しています。

ハイドレーションの不一致なしにVueコードを記述するその他の例については、以下を参照してください。Vueドキュメント.
ブラウザAPIに依存し、副作用を持つライブラリをインポートする場合は、それをインポートするコンポーネントがクライアント側でのみ呼び出されるようにしてください。バンドラーは副作用を含むモジュールのインポートをツリーシェイクしません。

クライアントサイドレンダリング

箱から出してすぐに、従来のVue.jsアプリケーションはブラウザ(またはクライアント)でレンダリングされます。その後、Vue.jsは、現在のインターフェースを作成するための指示を含むすべてのJavaScriptコードをブラウザがダウンロードして解析した後、HTML要素を生成します。

クライアントサイドレンダリングの利点

  • 開発速度: クライアントサイドだけで作業する場合、たとえばwindowオブジェクトのようなブラウザ専用のAPIを使用しても、コードのサーバー互換性を心配する必要はありません。
  • 安価: サーバーを実行すると、JavaScriptをサポートするプラットフォームで実行する必要があるため、インフラストラクチャのコストが増加します。クライアント専用アプリケーションは、HTML、CSS、およびJavaScriptファイルを任意の静的サーバーでホストできます。
  • オフライン: コードは完全にブラウザで実行されるため、インターネットが利用できない場合でも問題なく動作し続けます。

クライアントサイドレンダリングの欠点

  • パフォーマンス: ユーザーは、ブラウザがJavaScriptファイルをダウンロード、解析、実行するのを待つ必要があります。ダウンロード部分はネットワークに、解析と実行はユーザーのデバイスに依存するため、時間がかかり、ユーザーエクスペリエンスに影響を与える可能性があります。
  • 検索エンジン最適化: クライアントサイドレンダリングによって配信されるコンテンツのインデックス作成と更新は、サーバーでレンダリングされたHTMLドキュメントよりも時間がかかります。これは、検索エンジンのクローラーがページをインデックスするために最初の試行でインターフェースが完全にレンダリングされるのを待たないため、前述のパフォーマンスの欠点に関連しています。純粋なクライアントサイドレンダリングでは、コンテンツが検索結果ページに表示され、更新されるまでに時間がかかります。

クライアントサイドレンダリングは、インデックス作成を必要としない、またはユーザーが頻繁にアクセスする、非常にインタラクティブなWebアプリケーションに適しています。SaaS、バックオフィスアプリケーション、オンラインゲームなどの場合、後続のアクセスでダウンロードフェーズをスキップするためにブラウザのキャッシュを活用できます。

nuxt.config.tsでNuxtのクライアントサイドレンダリングのみを有効にできます

nuxt.config.ts
export default defineNuxtConfig({
  ssr: false,
})
ssr: falseを使用する場合、~/spa-loading-template.htmlにHTMLファイルも配置する必要があります。これは、アプリがハイドレーションされるまでレンダリングされるローディング画面に使用するHTMLです。
詳細については、SPAローディングテンプレートを参照してください。

静的クライアントレンダリングアプリのデプロイ

nuxt generateまたはnuxt build --prerenderコマンドでアプリを静的ホスティングにデプロイすると、デフォルトでNuxtはすべてのページを個別の静的HTMLファイルとしてレンダリングします。

nuxt generateまたはnuxt build --prerenderコマンドでアプリをプリレンダリングすると、出力フォルダにサーバーが含まれないため、サーバーエンドポイントを使用できません。サーバー機能が必要な場合は、代わりにnuxt buildを使用してください。

純粋なクライアントサイドレンダリングを使用している場合、これは不要かもしれません。単一のindex.htmlファイルと、静的Webホストがすべてのリクエストに対応するように指示できる200.htmlおよび404.htmlフォールバックが必要なだけかもしれません。

これを実現するために、ルートのプリレンダリング方法を変更できます。これをnuxt.config.tshooksに追加するだけです。

nuxt.config.ts
export default defineNuxtConfig({
  hooks: {
    'prerender:routes' ({ routes }) {
      routes.clear() // Do not generate any routes (except the defaults)
    },
  },
})

これにより3つのファイルが生成されます

  • index.html
  • 200.html
  • 404.html

200.html404.htmlは、ご利用のホスティングプロバイダーにとって有用かもしれません。

クライアントフォールバック生成のスキップ

クライアントレンダリングされたアプリをプリレンダリングする際、Nuxtはデフォルトでindex.html200.html404.htmlファイルを生成します。ただし、これらのファイルのいずれか(またはすべて)がビルドで生成されるのを防ぐ必要がある場合は、Nitro'prerender:generate'フックを使用できます。

nuxt.config.ts
export default defineNuxtConfig({
  ssr: false,
  nitro: {
    hooks: {
      'prerender:generate' (route) {
        const routesToSkip = ['/index.html', '/200.html', '/404.html']
        if (routesToSkip.includes(route.route)) {
          route.skip = true
        }
      },
    },
  },
})

ハイブリッドレンダリング

ハイブリッドレンダリングは、ルートルールを使用してルートごとに異なるキャッシュルールを許可し、特定のURLへの新しいリクエストに対してサーバーがどのように応答すべきかを決定します。

以前は、Nuxtアプリケーションのすべてのルート/ページとサーバーは、ユニバーサルレンダリングまたはクライアントサイドレンダリングのいずれかの同じレンダリングモードを使用する必要がありました。さまざまなケースで、一部のページはビルド時に生成できる一方、他のページはクライアントサイドでレンダリングする必要がありました。たとえば、管理セクションのあるコンテンツWebサイトを考えてみましょう。すべてのコンテンツページは主に静的で一度生成されるべきですが、管理セクションは登録が必要で、より動的なアプリケーションのように動作します。

Nuxtには、ルートルールとハイブリッドレンダリングのサポートが含まれています。ルートルールを使用すると、Nuxtルートのグループのルールを定義したり、レンダリングモードを変更したり、ルートに基づいてキャッシュ戦略を割り当てたりできます!

Nuxtサーバーは、対応するミドルウェアを自動的に登録し、キャッシュハンドラーを使用してルートをラップします。Nitroキャッシュレイヤー.

nuxt.config.ts
export default defineNuxtConfig({
  routeRules: {
    // Homepage pre-rendered at build time
    '/': { prerender: true },
    // Products page generated on demand, revalidates in background, cached until API response changes
    '/products': { swr: true },
    // Product pages generated on demand, revalidates in background, cached for 1 hour (3600 seconds)
    '/products/**': { swr: 3600 },
    // Blog posts page generated on demand, revalidates in background, cached on CDN for 1 hour (3600 seconds)
    '/blog': { isr: 3600 },
    // Blog post page generated on demand once until next deployment, cached on CDN
    '/blog/**': { isr: true },
    // Admin dashboard renders only on client-side
    '/admin/**': { ssr: false },
    // Add cors headers on API routes
    '/api/**': { cors: true },
    // Redirects legacy urls
    '/old-page': { redirect: '/new-page' },
  },
})

ルートルール

使用できるプロパティは以下のとおりです

  • redirect: string - サーバーサイドのリダイレクトを定義します。
  • ssr: boolean - アプリケーションのセクションのHTMLのサーバーサイドレンダリングを無効にし、ssr: falseでブラウザでのみレンダリングさせます
  • cors: boolean - cors: trueでCORSヘッダーを自動的に追加します。出力はheadersでオーバーライドしてカスタマイズできます。
  • headers: object - サイトのセクション(例:アセット)に特定のヘッダーを追加します。
  • swr: number | boolean - サーバー応答にキャッシュヘッダーを追加し、設定可能なTTL(Time To Live)でサーバーまたはリバースプロキシでキャッシュします。Nitroのnode-serverプリセットは、完全な応答をキャッシュできます。TTLが期限切れになると、キャッシュされた応答が送信され、ページはバックグラウンドで再生成されます。trueが使用された場合、MaxAgeなしのstale-while-revalidateヘッダーが追加されます。
  • isr: number | boolean - 動作はswrと同じですが、対応するプラットフォーム(現在NetlifyまたはVercel)で応答をCDNキャッシュに追加できます。trueが使用された場合、コンテンツはCDN内に次のデプロイまで永続化されます。
  • prerender: boolean - ビルド時にルートをプリレンダリングし、静的アセットとしてビルドに含めます
  • noScripts: boolean - サイトのセクションに対するNuxtスクリプトとJSリソースヒントのレンダリングを無効にします。
  • appMiddleware: string | string[] | Record<string, boolean> - アプリケーションのVueアプリ部分(Nitroルートではない)内のページパスで実行すべきミドルウェアを定義できます。

可能な限り、ルートルールは最適なパフォーマンスのためにデプロイプラットフォームのネイティブルールに自動的に適用されます(現在、NetlifyとVercelがサポートされています)。

nuxt generateを使用する場合、ハイブリッドレンダリングは利用できないことに注意してください。

Nuxt Vercel ISR

Vercelにデプロイされたハイブリッドレンダリングを使用したNuxtアプリケーションの例。

エッジサイドレンダリング

エッジサイドレンダリング (ESR) は、Nuxtに導入された強力な機能で、CDN (コンテンツ配信ネットワーク) のエッジサーバーを介して、Nuxtアプリケーションをユーザーにより近い場所でレンダリングできます。ESRを活用することで、パフォーマンスの向上と遅延の削減を確保し、ユーザーエクスペリエンスを向上させることができます。

ESRでは、レンダリングプロセスがネットワークの「エッジ」、つまりCDNのエッジサーバーにプッシュされます。ESRは実際のレンダリングモードというよりも、デプロイターゲットであることに注意してください。

ページの要求が行われたとき、オリジンサーバーまで行く代わりに、最も近いエッジサーバーがそれをインターセプトします。このサーバーがページのHTMLを生成し、ユーザーに送り返します。このプロセスにより、データが移動する物理的な距離が最小限に抑えられ、遅延が短縮され、ページがより速く読み込まれます

エッジサイドレンダリングは、以下の技術によって可能になります。Nitroは、Nuxtを動かすサーバーエンジンです。Node.js、Deno、Cloudflare Workersなど、クロスプラットフォームをサポートしています。

ESRを活用できる現在のプラットフォームは次のとおりです。

  • Cloudflare Pagesgit統合とnuxt buildコマンドを使用すれば、設定なしで利用できます
  • Vercel Edge Functionsnuxt buildコマンドとNITRO_PRESET=vercel-edge環境変数を使用します
  • Netlify Edge Functionsnuxt buildコマンドとNITRO_PRESET=netlify-edge環境変数を使用します

ルートルールを使用したエッジサイドレンダリングを使用する場合、ハイブリッドレンダリングも使用できることに注意してください。

上記で述べたプラットフォームのいくつかでデプロイされたオープンソースの例を探索できます。

Nuxt Todos Edge

ユーザー認証、SSR、SQLiteを備えたTodoアプリケーション。

Atinotes

Cloudflare KVに基づいたユニバーサルレンダリングを備えた編集可能なウェブサイト。