本文档提供了使用 Classic Remix 编译器 从 v1 迁移到 v2 的指南。有关迁移到 Vite 的更多指南,请参阅 Remix Vite 文档。
所有 v2 API 和行为在 v1 中都可通过 Future Flags 获得。可以一次启用一个,以避免项目开发中断。启用所有标志后,升级到 v2 应该是一次不间断的升级。
如果您遇到问题,请参阅 Troubleshooting 部分。
要快速了解一些常见的升级问题,请查看 🎥 2 分钟升级到 v2。
remix dev
有关配置选项,请参阅 remix dev
docs。
remix-serve
如果您使用的是 Remix App Server (remix-serve
),请启用 v2_dev
:
就是这样!
自定义应用服务器
如果您使用自己的应用服务器(server.js
),
请查看我们的 模板,了解如何与 v2_dev
集成的示例
或按照以下步骤操作:
- 启用
v2_dev
:
- 更新
package.json
中的 scripts
:
- 用
remix dev
替换所有 remix watch
- 删除多余的
NODE_ENV=development
- 使用
-c
/ --command
运行您的应用服务器
例如:
- 应用运行时,向 Remix 编译器发送
ready
消息
- (可选)
--manual
如果您依赖于 require
缓存清除,则可以使用 --manual
标志继续执行此操作:
查看 手动模式指南 了解更多详情。
从 v1 升级到 v2 后
在 v1 中启用 future.v2_dev
标志并使其正常工作后,您就可以升级到 v2 了。
如果您刚刚将 v2_dev
设置为 true
,则可以将其删除,一切应该都可以正常工作。
如果您正在使用 v2_dev
配置,则需要将其移动到 dev
配置字段:
文件系统路由约定
升级而不更改文件
如果您现在不想进行更改(或者永远不想,这只是一个约定,您可以使用您喜欢的任何文件组织),那么即使升级到 v2 后,您也可以继续使用旧约定 @remix-run/v1-route-convention
。
升级到新约定
- 路由嵌套现在由文件名中的点(
.
)创建,而不是文件夹嵌套
- 段中的
后缀_
下划线选择退出嵌套,使用可能匹配的父路由,而不是点(.
)。
- 段中的
_前缀
下划线创建没有路径的布局路由,而不是 __double
下划线前缀。
_index.tsx
文件创建索引路由,而不是 index.tsx
在 v1 中,路由文件夹如下所示:
使用 v2_routeConvention
后变为这样:
请注意,父路由现在被分组在一起,而不是在它们之间有几十个路由(如 auth 路由)。具有相同路径但嵌套不同的路由(如 dashboard
和 dashboard_
)也分组在一起。
使用新约定,任何路由都可以是一个目录,其中包含一个 route.tsx
文件来定义路由模块。这使得模块与它们所使用的路由可以共置:
例如,我们可以将 _public.tsx
移动到 _public/route.tsx
,然后将路由使用的模块共置:
有关此更改的更多背景信息,请参阅 原始 扁平路由
提案。
在 Remix v2 中,路由 headers
函数的行为略有变化。您可以通过 remix.config.js
中的 future.v2_headers
标志提前选择加入此新行为。
在 v1 中,Remix 仅使用叶子 渲染
路由 headers
函数的结果。您有责任向每个潜在叶子添加 headers
函数并相应地合并到 parentHeaders
中。这会很快变得乏味,而且当您添加新路由时也很容易忘记添加 headers
函数,即使您希望它只与其父路由共享相同的标头。
在 v2 中,Remix 现在使用它在渲染的路由中找到的最深 headers
函数。这让您可以更轻松地跨来自共同祖先的路由共享标头。然后,您可以根据需要将 headers
函数添加到更深的路由(如果它们需要特定行为)。
在 Remix v2 中,路由 meta
函数的签名以及 Remix 在后台处理元标记的方式已发生变化。
现在,您将返回一个描述符数组并自行管理合并,而不是从 meta
返回对象。这使 meta
API 更接近 links
,并且允许更灵活地控制元标记的呈现方式。
此外,<Meta />
将不再为层次结构中的每个路由呈现元数据。只会呈现从叶路由中的 meta
返回的数据。您仍然可以通过访问 函数参数中的 matches
选择从父路由包含元数据。
有关此更改的更多背景信息,请参阅 原始 v2 meta
提案。
您可以使用 @remix-run/v1-meta
包更新您的 meta
导出以继续使用 v1 约定。
使用 metaV1
函数,您可以传入 meta
函数的参数和它当前返回的相同对象。此函数将使用相同的合并逻辑将叶路由的元数据与其 直接父路由 元数据合并,然后将其转换为可在 v2 中使用的元描述符数组。
需要注意的是,默认情况下,此函数不会在整个层次结构中合并元数据。这是因为您可能有一些路由直接返回对象数组,而没有 metaV1
函数,这可能会导致不可预测的行为。如果您想在整个层次结构中合并元数据,请对所有路由的元数据导出使用 metaV1
函数。
parentsData
参数
在 v2 中,meta
函数不再接收 parentsData
参数。这是因为 meta
现在可以通过 matches
参数 访问您的所有路由匹配项,其中包括每个匹配项的加载器数据。
为了复制 parentsData
的 API,@remix-run/v1-meta
包提供了一个 getMatchesData
函数。它返回一个对象,其中每个匹配项的数据都由路由的 ID 键入。
变成:
matches
参数
请注意,在 v1 中,嵌套路由返回的对象都已合并,您现在需要使用 matches
自行管理合并:
meta 文档有更多关于合并路由元数据的提示。
CatchBoundary
和 ErrorBoundary
在 v1 中,抛出的 Response
会渲染最近的 CatchBoundary
,而所有其他未处理的异常都会渲染 ErrorBoundary
。在 v2 中没有 CatchBoundary
,所有未处理的异常都会渲染 ErrorBoundary
、响应或其他。
此外,错误不再作为 props 传递给 ErrorBoundary
,而是通过 useRouteError
钩子访问。
变成:
多个 API 返回提交的 formMethod
。在 v1 中,它们返回方法的小写版本,但在 v2 中,它们返回大写版本。这是为了使其符合 HTTP 和 fetch
规范。
useTransition
此钩子现在称为 useNavigation
,以避免与最近同名的 React 钩子混淆。它也不再具有 type
字段,并将 submission
对象扁平化为 navigation
对象本身。
您可以使用以下示例推导先前的 transition.type
。请记住,可能有一种更简单的方法来获得相同的行为,通常检查 navigation.state
、navigation.formData
或使用 useActionData
的操作返回的数据可以获得您想要的用户体验。欢迎在 Discord 中向我们提问,我们会帮助您 :D
关于 GET 提交的说明
在 Remix v1 中,GET 提交(例如 <Form method="get">
或 submit({}, { method: 'get' })
)在 transition.state
中从 idle -> submitting -> idle
变为了。这在语义上并不完全正确,因为即使您正在 提交
表单,您也正在执行 GET 导航并且只执行加载程序(而不是操作)。从功能上讲,它与 <Link>
或 navigate()
没有什么不同,只是用户可能通过输入指定搜索参数值。
在 v2 中,GET 提交更准确地反映为加载导航,因此变为 idle -> loading -> idle
,以使 navigation.state
与正常链接的行为保持一致。如果您的 GET 提交来自 <Form>
或 submit()
,则将填充 useNavigation.form*
,因此您可以根据需要进行区分。
useFetcher
与 useNavigation
类似,useFetcher
也扁平化了 submission
并删除了 type
字段。
您可以使用以下示例推导先前的 fetcher.type
。请记住,可能有一种更简单的方法来获得相同的行为,通常检查 fetcher.state
、fetcher.formData
或从 fetcher.data
上的操作返回的数据可以获得您想要的用户体验。欢迎在 Discord 中询问我们,我们会帮助您 :D
关于 GET 提交的说明
在 Remix v1 中,GET 提交(例如 <fetcher.Form method="get">
或 fetcher.submit({}, { method: 'get' })
)在 fetcher.state
中从 idle -> submitting -> idle
变为了。这在语义上并不完全正确,因为即使您正在 提交
表单,您也正在执行 GET 请求并且只执行加载器(而不是操作)。从功能上讲,它与 fetcher.load()
没有什么不同,只是用户可能通过输入指定搜索参数值。
在 v2 中,GET 提交更准确地反映为加载请求,因此进入 idle -> loading -> idle
以使 fetcher.state
与正常 fetcher 加载的行为保持一致。如果您的 GET 提交来自 <fetcher.Form>
或 fetcher.submit()
,则 fetcher.form*
将被填充,因此您可以根据需要进行区分。
链接 imagesizes
和 imagesrcset
路由 links
属性应全部为 React 驼峰式大小写值,而不是 HTML 小写值。这两个值在 v1 中以小写形式出现。在 v2 中,只有驼峰式大小写版本有效:
browserBuildDirectory
在您的 remix.config.js
中,将 browserBuildDirectory
重命名为 assetsBuildDirectory
。
devServerBroadcastDelay
从您的 remix.config.js
中删除 devServerBroadcastDelay
,因为需要此选项的竞争条件已在 v2 或 v2_dev
中消除。
devServerPort
在您的 remix.config.js
中,将 devServerPort
重命名为 future.v2_dev.port
。
从 v1 升级到 v2 后,这将变为 根级 dev
配置。
serverBuildDirectory
在 remix.config.js
中,将 serverBuildDirectory
重命名为 serverBuildPath
,并指定模块路径,而不是目录。
Remix 过去会为服务器创建多个模块,但现在会创建一个文件。
serverBuildTarget
无需指定构建目标,而是使用 remix.config.js
选项来生成服务器目标所需的服务器构建。此更改允许 Remix 部署到更多 JavaScript 运行时、服务器和主机,而无需 Remix 源代码了解它们。
以下配置应替换您当前的 serverBuildTarget
:
arc
cloudflare-pages
cloudflare-workers
deno
node-cjs
版本
默认服务器模块输出格式已从 cjs
更改为 esm
。您可以在 v2 中继续使用 CJS,但应用中的许多依赖项可能与 ESM 不兼容。
在 remix.config.js
中,您应该指定 serverModuleFormat: "cjs"
以保留现有行为,或指定 serverModuleFormat: "esm"
以选择新行为。
browserNodeBuiltinsPolyfill
默认情况下,浏览器不再提供 Node.js 内置模块的 Polyfill。在 Remix v2 中,您需要根据需要明确重新引入任何 polyfill(或空白 polyfill):
尽管我们建议明确说明浏览器软件包中允许使用哪些 polyfill,尤其是由于某些 polyfill 可能非常大,但您可以使用以下配置快速恢复 Remix v1 中的全套 polyfill:
serverNodeBuiltinsPolyfill
对于非 Node.js 服务器平台,不再默认提供 Node.js 内置模块的 Polyfill。
如果您的目标是非 Node.js 服务器平台,并且想要选择 v1 中的新默认行为,则在 remix.config.js
中,您应该首先通过为 serverNodeBuiltinsPolyfill.modules
明确提供一个空对象来删除所有服务器 polyfill:
然后,您可以根据需要重新引入任何 polyfill(或空白 polyfill)。
作为参考,可以手动指定 v1 中的完整默认 polyfill 集,如下所示:
installGlobals
为了准备使用 Node 内置的 fetch 实现,安装 fetch 全局变量现在是应用服务器的责任。如果您使用的是 remix-serve
,则无需执行任何操作。如果您使用的是自己的应用服务器,则需要自行安装全局变量。
删除导出的 polyfill
Remix v2 也不再从 @remix-run/node
导出这些 polyfill 实现,而您应该只使用全局命名空间中的实例。一个可能出现并需要更改的地方是您的 app/entry.server.tsx
文件,您还需要通过 createReadableStreamFromReadable
将 Node PassThrough
转换为 web ReadableStream
:
source-map-support
源地图支持现在是应用服务器的责任。如果您使用的是 remix-serve
,则无需执行任何操作。如果您使用的是自己的应用服务器,则需要自行安装 source-map-support
。
Netlify 适配器
@remix-run/netlify
运行时适配器已被弃用,取而代之的是
@netlify/remix-adapter
和 @netlify/remix-edge-adapter
并且从 Remix v2 开始已被删除。请更新您的代码,将所有 @remix-run/netlify
导入更改为
@netlify/remix-adapter
。
请记住,@netlify/remix-adapter
需要 @netlify/functions@^1.0.0
,与 @remix-run/netlify
中当前支持的 @netlify/functions
版本相比,这是一个重大变化。
由于删除了此适配器,我们还删除了我们的 Netlify 模板,转而使用
官方 Netlify 模板。
Vercel 适配器
@remix-run/vercel
运行时适配器已被弃用,转而使用开箱即用的 Vercel 功能,并且现在已从 Remix v2 中删除。请通过从您的
package.json
中删除 @remix-run/vercel
和 @vercel/node
、删除您的 server.js
/server.ts
文件以及从您的 remix.config.js
中删除 server
和 serverBuildPath
选项来更新您的代码。
由于删除了此适配器,我们还删除了我们的 Vercel 模板,转而使用
官方 Vercel 模板。
内置 PostCSS/Tailwind 支持
在 v2 中,如果您的项目中存在 PostCSS 和 / 或 Tailwind 配置文件,则这些工具会自动在 Remix 编译器中使用。
如果您在 Remix 之外有自定义 PostCSS 和 / 或 Tailwind 设置,并且希望在迁移到 v2 时保留这些设置,则可以在 remix.config.js
中禁用这些功能。
故障排除
ESM / CommonJS 错误
请参阅 serverModuleFormat
部分。