Render Modes (SPA, SSR, SSG, HTML-only)
对于每个页面,我们可以选择不同的渲染模式:
- SPA
- SSR
- HTML-only
- Pre-rendering (aka SSG)
比如,我们可以使用 SPA 渲染管理员面板,而使用 SSR 渲染营销页面
"SPA", "SSR", "HTML-only" and "SSG" 的含义以及应该选择哪个,在 SPA vs SSR (and more) 中讲解
vite-plugin-ssr
模板默认选择 SSR,这是适用于大多数应用程序的合理默认设置。
SPA
SPA 意味着页面仅在浏览器中加载和渲染
为此:
- 定义
.page.client.js
替代 .page.js
- 调整
render()
hook
1. .page.js
=> .page.client.js
通过定义 /pages/about.page.client.js
替代 /pages/about.page
,以确保页面仅在浏览器中加载
例子:
2. render()
hook (仅 SPA)
如果我们只有 SPA 页面,那么可以像下面一样调整 render()
hook
客户端 render()
hook:
// /renderer/_default.page.client.js
// Environment: Browser
import { renderToDom } from "some-ui-framework";
export { render };
async function render(pageContext) {
const { Page } = pageContext;
// UI 框架通常会有 `renderToDom()` 和 `hydrateDom()` 两个方法
// 注意使用 `renderToDom()` 而非 `hydrateDom()`
await renderToDom(document.getElementById("root"), Page);
}
参考 What is Hydration?,以理解 "rendering to the DOM" 和 "hydrating the DOM" 之间的不同
也需调整服务端 render()
hook:
// /renderer/_default.page.server.js
// Environment: Node.js
import { escapeInject } from "vite-plugin-ssr";
export function render() {
// 注意 `div#root` 是空的
return escapeInject`<html>
<body>
<div id="root"></div>
</body>
</html>`;
}
这是 SPA 和 SSR 之间主要的区别:在 SPA 中,div#root
是空的,而在 SSR 中,div#root
包含了渲染页面 HTML 的根组件 pageContext.Page
这意味着,针对 SPA,我们使用服务器端 render()
hook 只是为了生成一个空的 HTML:HTML 不包含页面的内容
生产环境中,我们通常希望 pre-render SPA 页面的 HTML,以便消除生产环境对 Node.js 服务器的需求
我们也可以使用服务端 render()
hook 来渲染 <head>
:
// /renderer/_default.page.server.js
// Environment: Node.js
import { escapeInject } from "vite-plugin-ssr";
export function render(pageContext) {
const { title, description } = pageContext.exports.meta;
// 尽管只在浏览器中加载和渲染页面组件,
// 我们也应在服务端定义页面的 `<title>` 和 `<meta name="description">`
return escapeInject`<html>
<head>
<title>${title}</title>
<meta name="description" content="${description}" />
</head>
<body>
<div id="root"></div>
</body>
</html>`;
}
指南 > 自定义 Exports/Hooks 中讲解了 pageContext.exports
// /pages/about.page.js
export const meta = {
title: "About | My App",
description: "My App is ...",
};
请注意,我们在 about.page.js
中定义 pageContext.exports.meta
,而不是 about.page.client.js
。因为我们需要在服务端访问 pageContext.exports.meta
也就是说,我们同时定义了 about.page.js
(定义 meta 数据) 和 about.page.client.js
(定义页面的根组件 pageContext.Page
)
2. render()
hooks (SPA + SSR)
如果同时存在 SPA 和 SSR,那么我们需要像以下调整 render()
hook:
// /renderer/_default.page.server.js
// Environment: Node.js
import { escapeInject, dangerouslySkipEscape } from "vite-plugin-ssr";
import { renderToHtml } from "some-ui-framework";
export function render(pageContext) {
let pageHtml;
if (pageContext.Page) {
// For SSR pages
pageHtml = renderToHtml(pageContext.Page);
} else {
// For SPA pages
pageHtml = "";
}
return escapeInject`<html>
<body>
<div id="root">${dangerouslySkipEscape(pageHtml)}</div>
</body>
</html>`;
}
如果我们在 .page.client.js
而不是 .page.js
中定义页面的根组件(pageContext.Page
),那么 pageContext.Page
仅在浏览器中渲染
// /renderer/_default.page.client.js
// Environment: Browser
import { renderToDom, hydrateDom } from "some-ui-framework";
export async function render(pageContext) {
const { Page } = pageContext;
const root = document.getElementById("root");
if (
// 我们通过使用 `innerHTML === ''` 来探测SPA的首次渲染
root.innerHTML === "" ||
// 使用客户端路由导航时, vite-plugin-ssr 将 `pageContext.isHydration` 设置为 `false`
!pageContext.isHydration
) {
// - SPA 页面没有 hydration 步骤:它们需要完全渲染
// - SSR 的页面导航也需要完全渲染(如果使用客户端路由)
await renderToDom(root, Page);
} else {
// SSR 页面的第一次渲染只是 hydration(而不是完全渲染)
await hydrateDom(root, Page);
}
}
React example: /examples/render-modes/.
Vue Example: GitHub > AaronBeaudoin/vite-plugin-ssr-example
.
SSR
vite-plugin-ssr
模板和文档默认使用 SSR
所以,如果我们的页面都是 SSR 渲染,那么我们只需:按照模板/文档简单操作
如果同时拥有 SSR 和 SPA 页面, 请参考 the SPA section
预渲染
参考 指南 > 预渲染 (SSG).
HTML-only
使用现代UI框架(React/Vue/...)将页面渲染为纯HTML是一种新技术,应被视为实验性的
将页面渲染为纯 HTML:
- 定义
.page.server.js
替代 .page.js
.
- (可选) 定义
.page.client.js
(例如: 添加极少的 JavaScript 注入交互性)
- 将
includeAssetsImportedByServer
设置为 true
// /pages/about.page.server.js
// Environment: Node.js
// 通常在 `*.page.js` 中定义 `Page`,但对于 HTML-only
// 我们在 `*.page.server.js` 中定义 `Page`
export { Page };
function Page() {
return (
<>
<h1>HTML-only page</h1>
<p>
This page is rendered only to HTML. (It's not loaded/rendered in the
browser-side.)
</p>
</>
);
}
// /pages/about.page.client.js
// Environment: Browser
// 此文件代表了整个浏览器端的 JavaScript
// 我们可以省略定义 `.page.client.js`,在这种情况下浏览器端就完全没有 JavaScript 了
console.log("I'm the page's only browser-side JavaScript line.");
// vite.config.js
import { ssr } from "vite-plugin-ssr/plugin";
export default {
plugins: [
ssr({
includeAssetsImportedByServer: true,
}),
],
};
Examples: