Skip to content

服务器端渲染(SSR)

Note

SSR专门指的是前端框架(例如React,preeact,Vue和Svelte),该框架支持在Node.js中运行相同应用程序的前端框架,并将其预先呈现给HTML,最后在客户端上进行水合。如果您正在寻找与传统服务器端框架集成,请查看“后端集成指南”

以下指南还假定您选择的框架中与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.html将需要参考entry-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是一个连接实例,可以用作任何连接兼容的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. Read index.html let
template
=
fs
.
readFileSync
(
path
.
resolve
(
__dirname
, 'index.html'),
'utf-8', ) // 2. Apply Vite HTML transforms. This injects the Vite HMR client, // and also applies HTML transforms from Vite plugins, e.g.全球的 // preambles from @vitejs/plugin-react
template
= await
vite
.transformIndexHtml(
url
,
template
)
// 3。加载服务器条目。 SSRLOADMODULE自动转换 // ESM source code to be usable in Node.js! There is no bundling // 需要,并提供类似于HMR的有效无效。 const {
render
} = await
vite
.ssrLoadModule('/src/entry-server.js')
// 4. render the app HTML. This assumes entry-server.js's exported // // 例如ReactDOMServer.renderToString() const
appHtml
= await
render
(
url
)
// 5. Inject the app-rendered HTML into the template. const
html
=
template
.
replace
(`<!--ssr-outlet-->`, () =>
appHtml
)
// 6. Send the rendered HTML back.
res
.status(200).set({ 'Content-Type': 'text/html' }).end(
html
)
} catch (
e
) {
// 如果捕获错误,请让Vite修复堆栈跟踪,以便将其映射 // 到您的实际源代码。
vite
.ssrFixStacktrace(
e
)
next
(
e
)
} })

还应更改package.json中的dev脚本以使用服务器脚本:

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

建造生产

要运送SSR项目进行生产,我们需要:

  1. 产生客户的构建正常;
  2. 产生一个可以直接通过import()加载的SSR构建,因此我们不必经过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.js中,我们需要通过检查process.env.NODE_ENV :

  • 而不是读取root index.html ,而是将dist/client/index.html用作模板,因为它包含了与客户端构建的正确资产链接。

  • 而不是await vite.ssrLoadModule('/src/entry-server.js')使用import('./dist/server/entry-server.js') (此文件是SSR构建的结果)。

  • vite dev服务器的创建和所有用法移动在仅开发的条件分支后面,然后添加静态文件中的中间用水中的静态文件以从dist/client中提供文件。

请参阅示例项目以进行工作设置。

生成预紧力指令

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函数。这将为我们提供足够的信息来渲染针对异步路线使用的文件的预紧指令!有关完整示例,请参见演示源。您也可以将此信息用于103个早期提示

预渲染 / SSG

如果提前知道某些路由所需的路由和数据,我们可以使用与生产SSR相同的逻辑将这些路由预渲染到静态HTML中。这也可以被视为一种静态位点产生(SSG)的形式。有关工作示例,请参见演示预渲染脚本

SSR外部

依赖性在运行SSR时默认情况下从Vite的SSR变换模块系统中“外部化”。这加快了开发和构建。

例如,如果需要通过Vite的管道来转换依赖关系,因为在其中未转移的Vite特征,则可以将其添加到ssr.noExternal

对于链接的依赖关系,默认情况下,它们不被外部化以利用Vite的HMR。例如,如果不需要,例如测试依赖项,就好像未链接一样,您可以将其添加到[ssr.external]../config/ssr-options.md#ssr-external) 。

Working with Aliases

如果您配置了将一个软件包重定向到另一个软件包的别名,则可能需要使实际的node_modules软件包别名,以使其适用于SSR外部化依赖关系。纱线PNPM都通过npm:前缀支持混叠。

SSR特异性插件逻辑

基于客户端与SSR,一些框架(例如VUE或Svelte编译组件)成不同的格式。为了支持有条件的变换,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之前,它被告知使用位置ssr param而不是使用options对象的插件钩。所有主要的框架和插件都将更新,但您可能会使用以前的API找到过时的帖子。

SSR目标

SSR构建的默认目标是节点环境,但是您也可以在Web Worker中运行服务器。每个平台的包装输入分辨率都不同。您可以使用ssr.target设置为'webworker'目标将目标配置为Web Worker。

SSR捆绑包

在某些情况下,例如webworker运行时间,您可能需要将SSR构建捆绑到一个JavaScript文件中。您可以通过设置ssr.noExternaltrue来启用此行为。这将做两件事:

  • 将所有依赖性视为noExternal
  • 如果导入任何node.js indin-ins,就会丢下错误

SSR解决条件

默认情况下,软件包输入分辨率将使用SSR构建resolve.conditions中设置的条件。您可以使用ssr.resolve.conditionsssr.resolve.externalConditions自定义此行为。

Vite CLI

CLI命令$ vite dev$ vite preview也可以用于SSR应用程序。您可以configureServer SSR MiddleWares添加到开发服务器中,并使用configurePreviewServer添加到预览服务器中。

Note

使用帖子挂钩,以便您的SSR中间件在Vite的中间Wares之后运行。

Released under the MIT License. (dev)