Skip to content

サーバー側のレンダリング(SSR)

Note

SSRは、node.jsで同じアプリケーションの実行をサポートし、それをHTMLに事前にレンダリングし、最終的にクライアントに水分補給するフロントエンドフレームワーク(React、Preact、Vue、Svelteなど)を特に指します。従来のサーバー側のフレームワークとの統合を探している場合は、代わりにバックエンド統合ガイドをご覧ください。

また、次のガイドでは、選択したフレームワークでSSRを使用した以前の経験も想定しており、Vite固有の統合の詳細にのみ焦点を当てます。

Low-level API

これは、ライブラリおよびフレームワークの著者向けの低レベルAPIです。目標がアプリケーションを作成することである場合は、最初にAwesome Vite SSRセクションで高レベルのSSRプラグインとツールをチェックしてください。とはいえ、多くのアプリケーションは、Viteのネイティブ低レベルAPIの上に直接構築されています。

現在、Viteは環境APIを使用して改良されたSSR APIに取り組んでいます。詳細については、リンクをご覧ください。

プロジェクトの例

Viteは、サーバー側のレンダリング(SSR)の組み込みサポートを提供します。 create-vite-extraこのガイドの参照として使用できるSSRセットアップの例が含まれています。

これらのプロジェクトをローカルで実行して、 create-viteを実行して、フレームワークオプションでOthers > create-vite-extra選択することもできます。

ソース構造

典型的なSSRアプリケーションには、次のソースファイル構造があります。

- index.html
- server.js # main application server
- src/
  - main.js          # exports env-agnostic (universal) app code
  - entry-client.js  # mounts the app to a DOM element
  - entry-server.js  # renders the app using the framework's SSR API

index.htmlentry-client.js参照し、サーバーレンダリングされたマークアップを挿入する必要があるプレースホルダーを含める必要があります。

index.html
html
<div id="app"><!--ssr-outlet--></div>
<script type="module" src="/src/entry-client.js"></script>

正確に置き換えることができる限り、 <!--ssr-outlet-->の代わりに希望するプレースホルダーを使用できます。

条件付きロジック

SSR対クライアントに基づいて条件付きロジックを実行する必要がある場合は、使用できます

js
import 'vite/client'
//  - -カット - -
if (import.meta.
env
.
SSR
) {
// ...サーバーのみロジック }

これは、ビルド中に静的に置き換えられるため、未使用の枝のツリーを揺るぎます。

開発サーバーのセットアップ

SSRアプリを構築するときは、メインサーバーを完全に制御し、生産環境からViteを分離することをお勧めします。したがって、ミドルウェアモードでViteを使用することをお勧めします。 Express (V4)の例は次のとおりです。

server.js
js
import 
fs
from 'node:fs'
import
path
from 'node:path'
import {
fileURLToPath
} from 'node:url'
import
express
from 'express'
import {
createServer
as
createViteServer
} from 'vite'
const
__dirname
=
path
.
dirname
(
fileURLToPath
(import.meta.
url
))
async function
createServer
() {
const
app
=
express
()
// Create Vite server in middleware mode and configure the app type as // 'custom', disabling Vite's own HTML serving logic so parent server // can take control const
vite
= await
createViteServer
({
server
: {
middlewareMode
: true },
appType
: 'custom'
}) // Use vite's connect instance as middleware. If you use your own // express router (express.Router()), you should use router.use // When the server restarts (for example after the user modifies // vite.config.js), `vite.middlewares` is still going to be the same // reference (with a new internal stack of Vite and plugin-injected // middlewares). The following is valid even after restarts.
app
.
use
(
vite
.
middlewares
)
app
.
use
('*', async (
req
,
res
) => {
// serve index.html - we will tackle this next })
app
.
listen
(5173)
}
createServer
()

ここでvitevitedevserverのインスタンスです。 vite.middlewares 、Connect互換node.jsフレームワークでミドルウェアとして使用できる接続インスタンスです。

次のステップは、サーバーレンダリングHTMLを提供するために*ハンドラーを実装することです。

server.js
js
import 
fs
from 'node:fs'
import
path
from 'node:path'
import {
fileURLToPath
} from 'node:url'
/** @Type {Import( 'Express')。Express} */ var
app
/** @type {import( 'vite')。vitedevserver} */ var
vite
// - -カット - -
app
.use('*', async (
req
,
res
,
next
) => {
const
url
=
req
.originalUrl
try { // 1. index.htmlを読み取ります let
template
=
fs
.
readFileSync
(
path
.
resolve
(
__dirname
, 'index.html'),
'utf-8', ) // 2. Vite HTML変換を適用します。これにより、Vite HMRクライアントが注入されます。 // また、ViteプラグインからのHTML変換も適用します。グローバル // @vitejs/プラグイン反応からのプリアンブル
template
= await
vite
.transformIndexHtml(
url
,
template
)
// 3.サーバーエントリをロードします。 SSRLoadModuleは自動的に変換されます // node.jsで使用できるESMソースコード!バンドルはありません // 必須であり、HMRと同様の効率的な無効化を提供します。 const {
render
} = await
vite
.ssrLoadModule('/src/entry-server.js')
// 4.アプリHTMLをレンダリングします。これは、Entry-Server.jsのエクスポートを想定しています // `render`関数は適切なフレームワークSSR APIを呼び出します、 // 例えばReactdomserver.rendertostring() const
appHtml
= await
render
(
url
)
// 5.アプリレンダリングされたHTMLをテンプレートに挿入します。 const
html
=
template
.
replace
(`<!--ssr-outlet-->`, () =>
appHtml
)
// 6.レンダリングされたHTMLを送信します。
res
.status(200).set({ 'Content-Type': 'text/html' }).end(
html
)
} catch (
e
) {
// エラーが発生した場合は、viteがスタックトレースを修正して、マップして戻します。 // 実際のソースコードに。
vite
.ssrFixStacktrace(
e
)
next
(
e
)
} })

代わりにサーバースクリプトを使用するには、 package.jsondevスクリプトも変更する必要があります。

package.json
diff
  "scripts": {
-   "dev": "vite"
+   "dev": "node server"
  }

生産のための建物

生産のためにSSRプロジェクトを出荷するには、次のことが必要です。

  1. 通常どおりクライアントビルドを作成します。
  2. SSRビルドを生成します。これは、 import()で直接ロードできるため、ViteのssrLoadModuleを通過する必要はありません。

package.jsonのスクリプトは次のようになります:

package.json
json
{
  "scripts": {
    "dev": "node server",
    "build:client": "vite build --outDir dist/client",
    "build:server": "vite build --outDir dist/server --ssr src/entry-server.js"
  }
}

これがSSRビルドであることを示す--ssrフラグに注意してください。また、SSRエントリを指定する必要があります。

次に、 server.jsprocess.env.NODE_ENVチェックして、いくつかの生産固有のロジックを追加する必要があります。

  • ルートindex.html読み取る代わりに、 dist/client/index.htmlテンプレートとして使用します。クライアントビルドへの正しいアセットリンクが含まれているためです。

  • await vite.ssrLoadModule('/src/entry-server.js')の代わりに、 import('./dist/server/entry-server.js')使用します(このファイルはSSRビルドの結果です)。

  • DEVのみの条件付きブランチの背後にあるvite DEVサーバーの作成とすべての使用を移動し、 dist/clientからファイルを提供するためにMiddleWaresを提供する静的ファイルを追加します。

ワーキングセットアップのプロジェクトのサンプルを参照してください。

プリロードディレクティブの生成

vite buildビルド出力ディレクトリで.vite/ssr-manifest.jsonフラグを生成する--ssrManifestフラグをサポートします。

diff
- "build:client": "vite build --outDir dist/client",
+ "build:client": "vite build --outDir dist/client --ssrManifest",

上記のスクリプトは、クライアントビルド用にdist/client/.vite/ssr-manifest.json生成します(はい、SSRマニフェストは、モジュールIDをクライアントファイルにマッピングするため、クライアントビルドから生成されます)。マニフェストには、関連するチャンクおよびアセットファイルへのモジュールIDのマッピングが含まれています。

マニフェストを活用するために、フレームワークは、サーバーレンダリング呼び出し中に使用されたコンポーネントのモジュールIDを収集する方法を提供する必要があります。

@vitejs/plugin-vueこれを箱から出してサポートし、使用されたコンポーネントモジュールIDを関連するVUE SSRコンテキストに自動的に登録します。

src/entry-server.js
js
const ctx = {}
const html = await vueServerRenderer.renderToString(app, ctx)
// ctx.modulesは、レンダリング中に使用されたモジュールIDのセットになりました

server.jsの生産ブランチでは、マニフェストを読み取り、 src/entry-server.jsがエクスポートしたrender関数に渡す必要があります。これにより、Asyncルートで使用されるファイルのプリロードディレクティブをレンダリングするのに十分な情報が提供されます!完全な例については、デモソースを参照してください。この情報を103の初期ヒントに使用することもできます。

プレレンダリング / SSG

特定のルートに必要なルートとデータが事前に知られている場合、これらのルートをProduction SSRと同じロジックを使用して静的HTMLに事前にレンダリングできます。これは、静的サイト生成の形式(SSG)と見なすこともできます。動作の例については、デモの事前レンダースクリプトを参照してください。

SSR外部

依存関係は、SSRを実行するときにデフォルトでViteのSSR変換モジュールシステムから「外部化」されます。これにより、開発とビルドの両方が高速化されます。

たとえば、Viteのパイプラインによって依存関係を変換する必要がある場合、Vite機能はそれらに翻訳されていないために使用されるため、 ssr.noExternalに追加できます。

リンクされた依存関係の場合、デフォルトではViteのHMRを利用するために外部化されません。たとえば、依存関係をリンクしていないかのようにテストするためにこれが望まれていない場合は、 ssr.externalに追加できます。

Working with Aliases

1つのパッケージを別のパッケージにリダイレクトするエイリアスを構成した場合、SSR外部依存関係のために機能させるために、実際のnode_modulesパッケージを代わりにエイリアスすることをお勧めします。 YARNPNPMの両方が、 npm:プレフィックスを介してエイリアシングをサポートします。

SSR固有のプラグインロジック

VueやSvelteコンパントなどの一部のフレームワークは、クライアントとSSRに基づいてコンポーネントをさまざまな形式にします。条件付き変換をサポートするために、Viteは次のプラグインフックのoptionsオブジェクトに追加のssrプロパティを渡します。

  • resolveId
  • load
  • transform

例:

js
/** @Type {()=> Import( 'Vite')。プラグイン} */
//  - -カット - -
export function 
mySSRPlugin
() {
return {
name
: 'my-ssr',
transform
(
code
,
id
,
options
) {
if (
options
?.ssr) {
// SSR固有の変換を実行します... } }, } }

loadtransformのオプションオブジェクトはオプションであり、ロールアップは現在このオブジェクトを使用していませんが、これらのフックを将来追加のメタデータで拡張する場合があります。

Note

Vite 2.7の前に、これはoptionsオブジェクトを使用する代わりに、位置ssr PARAMでプラグインフックを通知されました。すべての主要なフレームワークとプラグインが更新されますが、以前のAPIを使用して時代遅れの投稿を見つけることができます。

SSRターゲット

SSRビルドのデフォルトのターゲットはノード環境ですが、Webワーカーでサーバーを実行することもできます。パッケージのエントリ解像度は、プラットフォームごとに異なります。 'webworker'に設定されたssr.targetを使用して、ターゲットをWebワーカーに設定できます。

SSRバンドル

場合によっては、 webworkerランタイムなど、SSRビルドを単一のJavaScriptファイルにバンドルすることをお勧めします。 ssr.noExternal true設定することで、この動作を有効にすることができます。これは2つのことを行います。

  • すべての依存関係をnoExternalとして扱います
  • node.jsビルトインがインポートされている場合は、エラーを投げます

SSRは条件を解決します

デフォルトでは、パッケージエントリ解像度は、SSRビルドにresolve.conditionsに設定された条件を使用します。 ssr.resolve.conditionsssr.resolve.externalConditions使用して、この動作をカスタマイズできます。

Vite Cli

CLIコマンド$ vite devおよび$ vite preview 、SSRアプリにも使用できます。 SSRミドルウェアをconfigureServerの開発サーバーに、 configurePreviewServerでプレビューサーバーに追加できます。

Note

SSRミドルウェアがViteのMiddlewaresの後に実行されるように、ポストフックを使用します。

Released under the MIT License. (dev)