对于不需要 SSR 来实现 SEO、爬虫或性能的应用程序,可能需要向用户提供静态 HTML,其中包含应用程序的“外壳”(甚至特定路由的预渲染 HTML),其中包含必要的 html、head 和 body 标签,以便仅在客户端引导应用程序。
没有 SSR 不意味着放弃服务器端功能! SPA 模式实际上与服务器端功能(如服务器函数和/或服务器路由,甚至其他外部 API)配合得非常好。它只是意味着初始文档在客户端使用 JavaScript 渲染之前,不会包含应用程序完全渲染的 HTML。
启用 SPA 模式后,运行 Start 构建将随后进行额外的预渲染步骤以生成外壳。这是通过以下方式完成的:
注意
其他路由也可以预渲染,建议在 SPA 模式下尽可能多地预渲染,但这不是 SPA 模式工作的必需条件。
要配置 SPA 模式,您可以在 Start 插件的选项中添加一些选项
// vite.config.ts
export default defineConfig({
plugins: [
TanStackStart({
spa: {
enabled: true,
},
}),
],
})
// vite.config.ts
export default defineConfig({
plugins: [
TanStackStart({
spa: {
enabled: true,
},
}),
],
})
将纯客户端 SPA 部署到主机或 CDN 通常需要使用重定向,以确保 URL 正确重写到 SPA 外壳。任何部署的目标都应按此顺序包含这些优先级:
让我们使用 Netlify 的 _redirects 文件将所有 404 请求重写到 SPA 外壳。
# Catch all other 404 requests and rewrite them to the SPA shell
/* /_shell.html 200
# Catch all other 404 requests and rewrite them to the SPA shell
/* /_shell.html 200
同样,使用 Netlify 的 _redirects 文件,我们可以允许特定子路径通过服务器路由。
# Allow requests to /_serverFn/* to be routed through to the server (If you have configured your server function base path to be something other than /_serverFn, use that instead)
/_serverFn/* /_serverFn/:splat 200
# Allow any requests to /api/* to be routed through to the server (Server routes can be created at any path, so you must ensure that any server routes you want to use are under this path, or simply add additional redirects for each server route base you want to expose)
/api/* /api/:splat 200
# Catch all other 404 requests and rewrite them to the SPA shell
/* /_shell.html 200
# Allow requests to /_serverFn/* to be routed through to the server (If you have configured your server function base path to be something other than /_serverFn, use that instead)
/_serverFn/* /_serverFn/:splat 200
# Allow any requests to /api/* to be routed through to the server (Server routes can be created at any path, so you must ensure that any server routes you want to use are under this path, or simply add additional redirects for each server route base you want to expose)
/api/* /api/:splat 200
# Catch all other 404 requests and rewrite them to the SPA shell
/* /_shell.html 200
用于生成 SPA 外壳的默认路径名是 /。我们称之为外壳掩码路径。由于不包含匹配的路由,用于生成外壳的路径名大多不相关,但它仍然可配置。
注意
建议将 / 的默认值保留为外壳掩码路径。
// vite.config.ts
export default defineConfig({
plugins: [
tanstackStart({
spa: {
maskPath: '/app',
},
}),
],
})
// vite.config.ts
export default defineConfig({
plugins: [
tanstackStart({
spa: {
maskPath: '/app',
},
}),
],
})
预渲染选项用于配置 SPA 外壳的预渲染行为,并接受与我们预渲染指南中相同的预渲染选项。
默认情况下,以下 prerender 选项已设置:
这意味着默认情况下,外壳不会被爬取以查找要额外预渲染的链接,并且不会重试预渲染失败。
您始终可以通过提供自己的预渲染选项来覆盖这些选项。
// vite.config.ts
export default defineConfig({
plugins: [
TanStackStart({
spa: {
prerender: {
outputPath: '/custom-shell',
crawlLinks: true,
retryCount: 3,
},
},
}),
],
})
// vite.config.ts
export default defineConfig({
plugins: [
TanStackStart({
spa: {
prerender: {
outputPath: '/custom-shell',
crawlLinks: true,
retryCount: 3,
},
},
}),
],
})
自定义 SPA 外壳的 HTML 输出很有用,如果您想:
为了简化此过程,可以在 router 实例上找到 isShell() 函数
// src/routes/root.tsx
export default function Root() {
const isShell = useRouter().isShell()
if (isShell) console.log('Rendering the shell!')
}
// src/routes/root.tsx
export default function Root() {
const isShell = useRouter().isShell()
if (isShell) console.log('Rendering the shell!')
}
您可以使用此布尔值根据当前路由是否为外壳来有条件地渲染不同的 UI,但请记住,在水合外壳后,路由器将立即导航到第一个路由,并且 isShell() 将返回 false。如果处理不当,这可能会导致未样式化内容的闪烁。
由于外壳是使用应用程序的 SSR 构建预渲染的,因此在预渲染过程中,您的根路由上定义的任何 loader 或服务器特定功能都将运行,并且数据将包含在外壳中。
这意味着您可以通过使用 loader 或服务器特定功能在外壳中使用动态数据。
// src/routes/__root.tsx
export const RootRoute = createRootRoute({
loader: async () => {
return {
name: 'Tanner',
}
},
component: Root,
})
export default function Root() {
const { name } = useLoaderData()
return (
<html>
<body>
<h1>Hello, {name}!</h1>
<Outlet />
</body>
</html>
)
}
// src/routes/__root.tsx
export const RootRoute = createRootRoute({
loader: async () => {
return {
name: 'Tanner',
}
},
component: Root,
})
export default function Root() {
const { name } = useLoaderData()
return (
<html>
<body>
<h1>Hello, {name}!</h1>
<Outlet />
</body>
</html>
)
}
您的每周 JavaScript 资讯。每周一免费发送给超过 10 万开发者。