SPA Mode
SPA 模式
从一开始,Remix 就一直认为您拥有自己的服务器架构。这就是为什么 Remix 建立在 Web Fetch API 之上,并且可以通过内置或社区提供的适配器在任何现代 运行时 上运行。虽然我们相信拥有服务器可以为大多数应用提供最佳的用户体验/性能/SEO/等。但不可否认的是,在现实世界中,单页应用程序存在大量有效的用例:
- 您不想管理服务器,而更愿意通过 Github Pages 或其他 CDN 上的静态文件部署您的应用
- 您不想运行 Node.js 服务器
- 您想 将 React Router 应用 迁移到 Remix
- 您正在开发一种无法通过服务器呈现的特殊嵌入式应用
您的老板根本不在乎 SPA 架构的 UX 上限,也不会给您的开发团队时间/能力来重新架构事物
- Kent C. Dodds
这就是为什么我们在 2.5.0 (RFC) 中添加了对 SPA 模式 的支持,它很大程度上建立在 客户端数据 API 之上。
什么是SPA模式?
SPA 模式基本上就是你使用 createBrowserRouter
/RouterProvider
设置自己的 React Router + Vite 时所获得的,此外还附带一些额外的 Remix 好东西:
- 基于文件的路由(或通过
routes()
基于配置的路由) - 通过
route.lazy
自动进行基于路由的代码拆分 <Link prefetch>
支持快速预取路由模块- 通过 Remix
<Meta>
/<Links>
API 进行<head>
管理
SPA 模式告诉 Remix 您不打算在运行时运行 Remix 服务器,并且您希望在构建时生成静态index.html
文件,并且您将仅使用客户端数据 API 进行数据加载和变异。
index.html
是从 root.tsx
路由中的 HydrateFallback
组件生成的。生成 index.html
的初始渲染
将不包含任何比根更深的路由。这确保了 index.html
文件可以为 /
(即 /about
)以外的路径提供/水合(如果您配置了 CDN/服务器)。
用法
您可以使用 repo 中的 SPA 模式模板快速开始:
或者,你可以在 Remix Vite 插件配置中设置 ssr: false
,在 Remix+Vite 应用中手动选择加入 SPA 模式:
发展
在 SPA 模式下,您可以像开发传统 Remix SSR 应用一样进行开发,并且实际上使用正在运行的 Remix 开发服务器来启用 HMR/HDR:
生产
当您在 SPA 模式下构建应用程序时,Remix 将调用 /
路由的服务器处理程序,并将呈现的 HTML 保存在 index.html
文件中,与客户端资产一起保存(默认情况下为 build/client/index.html
)。
预览
您可以使用 vite preview 在本地预览生产版本:
vite preview
不适用于生产服务器
部署
要部署,您可以从您选择的任何 HTTP 服务器为您的应用提供服务。服务器应配置为从单个根 /index.html
文件提供多个路径(通常称为SPA 回退
)。如果服务器不直接支持此功能,则可能需要其他步骤。
举一个简单的例子,你可以使用 sirv-cli:
或者,如果您通过快速
服务器提供服务(尽管此时您可能只想考虑在 SSR 模式下运行 Remix 😉):
为 div 添加水份,而不是整个文档
如果您不想对整个 HTML文档
进行补充,您可以选择使用 SPA 模式,只对文档的某个子部分(如<div id="app">
)进行补充,并进行一些小的更改。
1. 添加 index.html
文件
由于 Remix 不会呈现 HTML 文档,因此您需要在 Remix 之外提供该 HTML。最简单的方法是保留一个 app/index.html
文档,其中包含一个占位符,您可以在构建时用 Remix 呈现的 HTML 替换该占位符以生成最终的 index.html
我们将用 Remix HTML 替换 <!-- Remix SPA -->
HTML 注释。
div
中的任何空格,否则你会遇到 React 水化问题
2. 更新 root.tsx
更新你的根路由以仅渲染 <div id="app">
的内容:
3. 更新 entry.server.tsx
在您的 app/entry.server.tsx
文件中,您需要获取 Remix 渲染的 HTML 并将其插入到您的静态 app/index.html
文件占位符中。您还需要停止像默认的 entry.server.tsx
文件那样预先添加 <!DOCTYPE html>
声明,因为它应该在您的 app/index.html
文件中)。
app/entry.server.tsx
文件,则可能需要运行 npx remix reveal
4. 更新 entry.client.tsx
更新 app/entry.client.tsx
以补充 <div id="app">
而不是文档:
app/entry.client.tsx
文件,则可能需要运行 npx remix reveal
注意事项
-
SPA 模式仅在使用 Vite 和 Remix Vite 插件 时有效
-
您不能使用服务器 API,例如
headers
、loader
和action
——如果您导出它们,构建将引发错误 -
您只能在 SPA 模式下从
root.tsx
导出HydrateFallback
——如果您从任何其他路由导出一个HydrateFallback
,构建将引发错误。 -
您无法从
clientLoader
/clientAction
方法中调用serverLoader
/serverAction
,因为没有正在运行的服务器——如果调用它们,将引发运行时错误
服务器构建
值得注意的是,Remix SPA 模式通过在构建期间在服务器上执行根路由的预渲染
来生成index.html
文件
- 这意味着在创建 SPA 时,您仍然有一个
服务器构建
和服务器渲染
步骤,因此您需要小心使用引用仅客户端方面的依赖项,例如document
、window
、localStorage
等。 - 一般来说,解决这些问题的方法是从
entry.client.tsx
导入任何仅浏览器的库,这样它们就不会出现在服务器构建中 - 否则,您通常可以通过使用
React.lazy
或remix-utils
中的<ClientOnly>
组件来解决这些问题
CJS/ESM 依赖问题
如果您的应用程序依赖项遇到 ESM/CJS 问题,您可能需要使用 Vite ssr.noExternal 选项将某些依赖项包含在您的服务器包中:
这些问题通常是由于依赖项的发布代码未针对 CJS/ESM 进行正确配置所致。通过在 ssr.noExternal
中包含特定依赖项,Vite 会将依赖项捆绑到服务器构建中,并有助于避免在运行服务器时出现运行时导入问题。
如果您有相反的用例,并且您特别想将依赖项保留在包外部,则可以使用相反的 ssr.external
选项。
从 React Router 迁移
我们还希望 SPA 模式能够帮助人们将现有的 React 路由器应用迁移到 Remix 应用(无论是否是 SPA!)。
迁移的第一步是让您当前的 React Router 应用在 vite
上运行,这样您就可以获得非 JS 代码所需的任何插件(即 CSS、SVG 等)。
如果您目前正在使用BrowserRouter
一旦您使用 vite,您应该能够按照本指南 中的步骤将您的 BrowserRouter
应用程序放入一个 catch-all Remix 路由中。
如果你目前正在使用 RouterProvider
如果您当前正在使用RouterProvider
,那么最好的方法是将您的路由移动到单独的文件并通过route.lazy
加载它们:
- 根据 Remix 文件约定命名这些文件,以便更轻松地迁移到 Remix (SPA)
- 将路由组件导出为命名的
组件
导出(用于 RR)以及默认
导出(最终供 Remix 使用)
将所有路由存储在各自的文件中后,您可以:
- 将这些文件移至 Remix
app/
目录 - 启用 SPA 模式
- 将所有
loader
/action
函数重命名为clientLoader
/clientAction
- 将您的 React Router
index.html
文件替换为导出default
组件和HydrateFallback
的app/root.tsx
路由