react-router
)@react-router/*
)此页面列出了自 v6.0.0
以来 React Router 的所有发布版本/发布说明。对于 v6 之前的版本,请参阅 Github 发布页面。
我们在此文件中管理发布说明,而不是在分页的 Github 发布页面上,原因有二:
日期:2025-08-07
loaderData
值是否曾注意到框架提供给你的 loader 数据值存在差异?比如,在你的组件 props 中我们称之为 loaderData
,但在你的匹配项中却是 match.data
?是的,我们也注意到了——以及一些眼尖的 React Router 用户在一份提案中提出了这个问题。我们已经在一些遗留的地方添加了新的 loaderData
字段,与现有的 data
字段并存,以与新的 Route.*
API 中使用的 loaderData
命名保持一致。
7.8.0
中最大的一组变更是针对 unstable_middleware
API 的,因为我们正朝着稳定这些 API 的方向迈进。如果您已经采纳了中间件 API 进行早期测试,请仔细阅读下面的中间件变更。我们希望尽快稳定这些 API,所以请就 API 当前的状态向我们提供任何反馈!
react-router
- 向 Links
和 PrefetchPageLinks
添加 nonce
属性 (#14048)react-router
- 在现有的 data
参数/属性旁边添加 loaderData
参数/属性,以在整个框架中提供 loaderData
和 actionData
之间的一致性和清晰度 (#14047)Route.MetaArgs
, Route.MetaMatch
, MetaArgs
, MetaMatch
, Route.ComponentProps.matches
, UIMatch
data
属性添加了 @deprecated
警告,以引导用户使用新的 loaderData
属性,为在未来的主要版本中移除 data
属性做准备。react-router
- 在 fetcher.submit
重新验证期间进行导航时,防止出现 "Did not find corresponding fetcher result" 控制台错误 (#14114)
react-router
- 将懒加载路由发现清单 URL 的生成切换为使用独立的 URLSearchParams
实例,而不是 URL.searchParams
,以避免在 Chrome 中出现严重的性能瓶颈 (#14084)
react-router
- 调整内部 RSC 对 React.use
的使用,以避免在使用 React 18 时出现 Webpack 编译错误 (#14113)
react-router
- 从 TypeScript 声明文件中移除对 @types/node
的依赖 (#14059)
react-router
- 修复 UIMatch
的类型,以反映 loaderData
/data
属性可能是 undefined
(#12206)
当渲染 ErrorBoundary
时,并非所有活动匹配项都有可用的 loader 数据,因为它可能是它们的 loader
抛出错误触发了边界。
UIMatch.data
类型没有正确处理这种情况,并且总是反映数据的存在,导致在渲染 ErrorBoundary
时出现意外的运行时错误。
⚠️ 这可能会导致您的代码中出现一些未受保护的 match.data
访问的类型错误 - 在这些情况下,您应该适当地为 undefined
值进行保护。
// app/root.tsx
export function loader() {
someFunctionThatThrows(); // ❌ Throws an Error
return { title: "My Title" };
}
export function Layout({ children }: { children: React.ReactNode }) {
let matches = useMatches();
let rootMatch = matches[0] as UIMatch<Awaited<ReturnType<typeof loader>>>;
// ^ rootMatch.data is currently incorrectly typed here, so TypeScript does
// not complain if you do the following which throws an error at runtime:
let { title } = rootMatch.data; // 💥
return <html>...</html>;
}
@react-router/dev
- 修复 Vite 插件中没有 mkdir 的重命名操作 (#14105)
⚠️ 不稳定功能不建议在生产环境中使用
RSC
react-router
- 修复数据模式问题:当 shouldRevalidate
返回 false
时,返回错误的路由会被替换为 <Outlet />
(#14071)react-router
- 代理来自操作的服务器操作副作用重定向,用于文档和 callServer
请求 (#14131)中间件
react-router
- 更改 RouterProvider
、HydratedRouter
和 unstable_RSCHydratedRouter
上的 unstable_getContext
签名,使其返回一个 unstable_RouterContextProvider
实例,而不是用于在内部构造实例的 Map
(#14097)
unstable_getContext
属性,这是一个破坏性变更。react-router
- 即使没有 loader 存在,也在客户端导航上运行客户端中间件 (#14106)
react-router
- 将内部中间件实现转换为使用新的 unstable_generateMiddlewareResponse
API (#14103)
react-router
- 确保在启用中间件的情况下,资源路由错误通过 handleError
处理 (#14078)
react-router
- 如果 next
未被调用,则传播从服务器中间件返回的 Response
(#14093)
react-router
- 允许服务器中间件返回 data()
值,这些值将被转换为 Response
(#14093, #14128)
react-router
- 更新中间件错误处理,使 next
函数永不抛出错误,而是在适当的 ErrorBoundary
处理任何中间件错误,并通过祖先 next
函数返回 Response
(#14118)
try
/catch
中包装 next
调用,您应该可以移除它们。react-router
- 将 next
之前的客户端中间件错误冒泡到适当的祖先错误边界 (#14138)
react-router
- 当中间件启用时,将 context
参数设为只读 (Readonly<unstable_RouterContextProvider>
),这样 TypeScript 将不允许您在 loader、action 或中间件中向其写入任意字段。 (#14097)
react-router
- 重命名并更改 staticHandler.query
/staticHandler.queryRoute
中 unstable_respond
API 的签名/功能 (#14103)
这仅影响在非框架模式 SSR 期间使用 createStaticHandler()
进行手动数据加载的用户。
该 API 已重命名为 unstable_generateMiddlewareResponse
以提高清晰度。
主要的功能变化是,我们现在将一个 query
/queryRoute
函数作为参数传递,您可以在回调函数中执行 loader/action,从而完全访问预处理和错误处理,而不是在调用 unstable_respond
之前运行 loader/action 并将结果交给您。
API 的 query
版本现在具有 (query: (r: Request) => Promise<StaticHandlerContext | Response>) => Promise<Response>
的签名。
API 的 queryRoute
版本现在具有 (queryRoute: (r: Request) => Promise<Response>) => Promise<Response>
的签名。
这允许更高级的用法,例如在调用 query
之前/之后运行逻辑,以及直接处理从 query 抛出的错误。
⚠️ 如果您已采纳 staticHandler
的 unstable_respond
API,这是一个破坏性变更。
let response = await staticHandler.query(request, {
requestContext: new unstable_RouterContextProvider(),
async unstable_generateMiddlewareResponse(query) {
try {
// At this point we've run middleware top-down so we need to call the
// handlers and generate the Response to bubble back up the middleware
let result = await query(request);
if (isResponse(result)) {
return result; // Redirects, etc.
}
return await generateHtmlResponse(result);
} catch (error: unknown) {
return generateErrorResponse(error);
}
},
});
@react-router/{architect,cloudflare,express,node}
- 当 future.unstable_middleware
启用时,更改 getLoadContext
签名 (type GetLoadContextFunction
),使其返回一个 unstable_RouterContextProvider
实例,而不是用于在内部构造实例的 Map
(#14097)
type unstable_InitialContext
导出。getLoadContext
函数的自定义服务器,这是一个破坏性变更。create-react-router
react-router
@react-router/architect
@react-router/cloudflare
@react-router/dev
@react-router/express
@react-router/fs-routes
@react-router/node
@react-router/remix-config-routes-adapter
@react-router/serve
完整更新日志: v7.7.1...v7.8.0
日期:2025-07-24
@react-router/dev
- 在运行 react-router reveal --no-typescript
时,更新到 Prettier v3 进行格式化 (#14049)⚠️ 不稳定功能不建议在生产环境中使用
react-router
- RSC 数据模式:修复当 shouldRevalidate
返回 false
时,有错误的路由没有被强制重新验证的 bug (#14026)react-router
- RSC 数据模式:修复当错误边界被渲染时出现的 `Matched leaf route at location "/..." does not have an element or Component` 警告 (#14021)完整更新日志: v7.7.0...v7.7.1
日期:2025-07-16
我们很高兴通过以下新 API 在数据模式中引入对 RSC 的实验性支持:
unstable_RSCHydratedRouter
unstable_RSCStaticRouter
unstable_createCallServer
unstable_getRSCStream
unstable_matchRSCServerRequest
unstable_routeRSCServerRequest
create-react-router
- 添加 Deno 作为支持和可检测的包管理器。请注意,此检测仅适用于 Deno 2.0.5 及以上版本。如果您使用的是旧版 Deno,则必须将 --package-manager CLI 标志设置为 deno
。 (#12327)@react-router/remix-config-routes-adapter
- 与 DefineRoutesFunction
一同导出 DefineRouteFunction
类型 (#13945)react-router
- 在验证 cookie 签名时处理 InvalidCharacterError
(#13847)
react-router
- 将 searchParams
的副本传递给 setSearchParams
回调函数,以避免内部 searchParams
实例的突变 (#12784)
searchParams
,这会导致错误,因为内部实例会与 useLocation().search
不同步。react-router
- 在 turbo-stream
v2 分支中支持无效的 Date
(#13684)
react-router
- 从 patchRoutesOnNavigation
的 path
参数中剥离搜索参数,用于 fetcher 调用 (#13911)
react-router
- 跳过 useRevalidator()
调用时的滚动恢复,因为它们不是新位置 (#13671)
react-router
- 在 ssr
设置为 false
的情况下,支持预渲染配置中未编码的 UTF-8 路由 (#13699)
react-router
- 如果 URL 哈希不是有效的 URI 组件,则不抛出错误 (#13247)
react-router
- 从单一获取响应中移除 Content-Length
标头 (#13902)
react-router
- 修复了在引入中间件功能时 createRoutesStub
出现的回归问题 (#13946)
作为这项工作的一部分,我们更改了签名以与新的中间件 API 对齐,但没有使其与之前的 AppLoadContext
API 向后兼容。
这使得如果您选择使用中间件和更新的 context
类型,createRoutesStub
可以工作,但对于尚未选择使用中间件的用户,createRoutesStub
出现了问题。
我们已经恢复了这一变更,并以一种两种用户都可以利用的方式重新实现了它。
⚠️ 如果您已经采用了不稳定的中间件功能,并且正在使用更新后的 API 的 `createRoutesStub`,这可能是一个破坏性的 bug。
// If you have not opted into middleware, the old API should work again
let context: AppLoadContext = {
/*...*/
};
let Stub = createRoutesStub(routes, context);
// If you have opted into middleware, you should now pass an instantiated
// `unstable_routerContextProvider` instead of a `getContext` factory function.
let context = new unstable_RouterContextProvider();
context.set(SomeContext, someValue);
let Stub = createRoutesStub(routes, context);
@react-router/dev
- 更新 vite-node
到 ^3.2.2
以支持 Vite 7 (#13781)
@react-router/dev
- 在开发模式下正确处理 https
协议 (#13746)
@react-router/dev
- 修复当 Vite 的 build.cssCodeSplit
选项被禁用时样式丢失的问题 (#13943)
@react-router/dev
- 允许路由配置文件使用 .mts
和 .mjs
扩展名 (#13931)
@react-router/dev
- 修复当 cwd
与项目根目录不同时预渲染文件的位置问题 (#13824)
@react-router/dev
- 在构建期间找不到块时,改进块错误日志记录 (#13799)
@react-router/dev
- 修复了错误配置的 externalConditions
,该配置为外部模块启用了 module
条件,并破坏了某些包(如 Emotion)的构建 (#13871)
⚠️ 不稳定功能不建议在生产环境中使用
create-react-router
react-router
@react-router/architect
@react-router/cloudflare
@react-router/dev
@react-router/express
@react-router/fs-routes
@react-router/node
@react-router/remix-config-routes-adapter
@react-router/serve
完整更新日志: v7.6.3...v7.7.0
日期:2025-06-27
react-router
- 不要为 useRouteLoaderData<typeof clientLoader>
序列化类型 (#13752)
为了让类型区分 clientLoader
和 serverLoader
,您必须为 clientLoader
参数添加注解。
// 👇 annotation required to skip serializing types
export function clientLoader({}: Route.ClientLoaderArgs) {
return { fn: () => "earth" };
}
function SomeComponent() {
const data = useRouteLoaderData<typeof clientLoader>("routes/this-route");
const planet = data?.fn() ?? "world";
return <h1>Hello, {planet}!</h1>;
}
@react-router/cloudflare
- 从 peerDependencies
中移除 tsup
(#13757)
@react-router/dev
- 添加 Vite 7 支持 (#13748)
@react-router/dev
- 当提供了自定义的 entry.server.(j|t)sx
文件时,跳过 package.json
解析检查 (#13744)
@react-router/dev
- 为路由 id 不为 'root' 的情况添加验证 (#13792)
@react-router/fs-routes
@react-router/remix-config-routes-adapter
- 使用 replaceAll
来规范化 Windows 文件系统斜杠 (#13738)
@react-router/node
- 移除旧的 "install" 包导出 (#13762)
完整更新日志: v7.6.2...v7.6.3
日期:2025-06-03
create-react-router
- 更新 tar-fs
(#13675)
react-router
- (内部) 轻微重构内部 headers()
函数处理,以用于 RSC (#13639)
react-router
@react-router/dev
- 通过将路由模块组件属性逻辑从 Vite 插件移至 react-router
,避免在框架模式下产生额外的 with-props
块 (#13650)
@react-router/dev
- 当启用 future.unstable_viteEnvironmentApi
并配置了绝对的 Vite base
时,确保在开发期间正确处理关键 CSS (#13598)
@react-router/dev
- 更新 vite-node
(#13673)
@react-router/dev
- 修复非 {.js,.jsx,.ts,.tsx} 路由(如 .mdx)的类型生成问题 (#12453)
@react-router/dev
- 修复可选动态参数的 href 类型问题 (#13725)
7.6.1 引入了对可选静态段的 href
修复,但这些修复导致了 7.6.0 中可选动态参数工作方式的回归。
// 7.6.0
href("/users/:id?"); // ✅
href("/users/:id?", { id: 1 }); // ✅
// 7.6.1
href("/users/:id?"); // ❌
href("/users/:id?", { id: 1 }); // ❌
现在,可选静态段会扩展为不同的 href
路径,但可选动态参数则不会。这样,href
就可以明确地引用一个确切的 URL 路径,同时将路径选项的数量保持在最低限度。
// 7.6.2
// path: /users/:id?/edit?
href("
// ^ suggestions when cursor is here:
//
// /users/:id?
// /users/:id?/edit
此外,您可以从组件属性传递 params
,而无需手动进行类型收窄。
declare const params: { id?: number };
// 7.6.0
href("/users/:id?", params);
// 7.6.1
href("/users/:id?", params); // ❌
"id" in params ? href("/users/:id", params) : href("/users"); // works... but is annoying
// 7.6.2
href("/users/:id?", params); // restores behavior of 7.6.0
完整更新日志: v7.6.1...v7.6.2
日期:2025-05-25
react-router
- 部分还原在 7.1.4
中为减少对 matchRoutes
调用而添加的优化,因为它暴露了其他问题 (#13562)
react-router
- 更新 Route.MetaArgs
以反映 data
可能为 undefined
的情况 (#13563)
loader
向其自身的 ErrorBoundary
抛出错误的情况,但它也出现在 404 错误的情况下,该错误会渲染根 ErrorBoundary
/meta
,但由于没有路由匹配,根 loader
并未运行。react-router
- 避免在懒加载路由发现被导航中断时,初始 fetcher 执行 404 错误 (#13564)
react-router
- 正确地用 href
替换通配符 *
(#13593)
href("/products/*", { "*": "/1/edit" }); // -> /products/1/edit
@react-router/architect
- 将 @architect/functions
从 ^5.2.0
更新至 ^7.0.0
(#13556)
@react-router/dev
- 防止对 app/
目录之外的路由文件进行类型生成 (#12996)
@react-router/dev
- 在清理服务器构建中的资源时,向 build
命令输出添加额外的日志记录 (#13547)
@react-router/dev
- 当 Vite 配置中启用了 build.ssrEmitAssets
时,不要从服务器构建中清理资源 (#13547)
@react-router/dev
- 修复当同一路由在多个路径中使用时的类型生成问题 (#13574)
例如,routes/route.tsx
在这里被用于 4 个不同的路径。
import { type RouteConfig, route } from "@react-router/dev/routes";
export default [
route("base/:base", "routes/base.tsx", [
route("home/:home", "routes/route.tsx", { id: "home" }),
route("changelog/:changelog", "routes/route.tsx", { id: "changelog" }),
route("splat/*", "routes/route.tsx", { id: "splat" }),
]),
route("other/:other", "routes/route.tsx", { id: "other" }),
] satisfies RouteConfig;
以前,类型生成会任意选择其中一个路径作为“获胜者”,并基于该路径为路由模块生成类型。
现在,类型生成会根据需要为同一路由文件的备用路径创建联合类型。
@react-router/dev
- 更好的 params
类型 (#13543)
例如:
// routes.ts
import { type RouteConfig, route } from "@react-router/dev/routes";
export default [
route("parent/:p", "routes/parent.tsx", [
route("route/:r", "routes/route.tsx", [
route("child1/:c1a/:c1b", "routes/child1.tsx"),
route("child2/:c2a/:c2b", "routes/child2.tsx"),
]),
]),
] satisfies RouteConfig;
以前,routes/route
的 params
被计算为 { p: string, r: string }
。
这错误地忽略了可能来自子路由的参数。
如果访问 /parent/1/route/2/child1/3/4
,传递给 routes/route
的实际参数类型将是 { p: string, r: string, c1a: string, c1b: string }
。
现在,params
能够感知子路由,并且自动完成将包括子参数作为可选参数。
params.|
// ^ cursor is here and you ask for autocompletion
// p: string
// r: string
// c1a?: string
// c1b?: string
// c2a?: string
// c2b?: string
您还可以缩小 params
的类型,因为它被实现为包含 routes/route
的每个页面的参数的规范化联合类型。
if (typeof params.c1a === 'string') {
params.|
// ^ cursor is here and you ask for autocompletion
// p: string
// r: string
// c1a: string
// c1b: string
}
@react-router/dev
- 修复可选段的 href
(#13595)
类型生成现在会将带有可选段的路径扩展为其对应的非可选路径。
例如,路径 /user/:id?
被扩展为 /user
和 /user/:id
,以更紧密地模拟可访问的 URL。
然后,href
使用这些扩展的(非可选)路径来为您的应用程序构建类型安全的路径。
// original: /user/:id?
// expanded: /user & /user/:id
href("/user"); // ✅
href("/user/:id", { id: 1 }); // ✅
对于静态可选路径,这变得更加重要,因为之前没有好的方法来指示是否应在结果路径中包含可选段。
// original: /products/:id/detail?
// before
href("/products/:id/detail?"); // ❌ How can we tell `href` to include or omit `detail?` segment with a complex API?
// now
// expanded: /products/:id & /products/:id/detail
href("/product/:id"); // ✅
href("/product/:id/detail"); // ✅
⚠️ 不稳定功能不建议在生产环境中使用
@react-router/dev
- 将内部的 react-router/route-module
导出重命名为 react-router/internal
(#13543)@react-router/dev
- 从生成的 +types/*
文件中移除了 Info
导出 (#13543)@react-router/dev
- 在生成 SRI 清单时,跨 Node 版本规范化 dirent 条目路径 (#13591)完整更新日志: v7.6.0...v7.6.1
日期:2025-05-08
routeDiscovery
配置选项我们在 7.6.0
中添加了一个新的配置选项,让您能更好地控制懒加载路由发现功能。如果您在同一台服务器上运行多个 RR 应用程序,现在可以配置 /__manifest
路径;或者,如果您的应用程序足够小且不需要该功能,您也可以完全禁用它。
// react-router.config.ts
export default {
// You can modify the manifest path used:
routeDiscovery: { mode: "lazy", manifestPath: "/custom-manifest" }
// Or you can disable this feature entirely and include all routes in the
// manifest on initial document load:
routeDiscovery: { mode: "initial" }
// If you don't specify anything, the default config is as follows, which enables
// Lazy Route Discovery and makes manifest requests to the `/__manifest` path:
// routeDiscovery: { mode: "lazy", manifestPath: "/__manifest" }
} satisfies Config;
一些未来标志会改变 React Router 中类型的工作方式。以前,您必须记住手动选择加入新类型。例如,对于 future.unstable_middleware
// react-router.config.ts
// Step 1: Enable middleware
export default {
future: {
unstable_middleware: true,
},
};
// Step 2: Enable middleware types
declare module "react-router" {
interface Future {
unstable_middleware: true; // 👈 Enable middleware types
}
}
保持运行时未来标志与这些标志的类型同步是您的责任。这既令人困惑又容易出错。
现在,React Router 将自动为未来标志启用类型。这意味着您只需要指定运行时未来标志。
// react-router.config.ts
// Step 1: Enable middleware
export default {
future: {
unstable_middleware: true,
},
};
// No step 2! That's it!
在幕后,React Router 会将相应的 declare module
生成到 .react-router/types
中。目前这是在 .react-router/types/+register.ts
中完成的,但这是一个实现细节,将来可能会改变。
react-router
- 在 react-router.config.ts
中添加了一个新的 routeDiscovery
选项,用于配置懒加载路由发现行为 (#13451)
react-router
- 在 createRoutesStub
中添加对路由组件属性的支持 (#13528)
这允许您使用属性而不是钩子来对路由组件进行单元测试。
let RoutesStub = createRoutesStub([
{
path: "/",
Component({ loaderData }) {
let data = loaderData as { message: string };
return <pre data-testid="data">Message: {data.message}</pre>;
},
loader() {
return { message: "hello" };
},
},
]);
render(<RoutesStub />);
await waitFor(() => screen.findByText("Message: hello"));
@react-router/dev
- 未来标志的自动类型 (#13506)
您可能会注意到这个列表比平时要长一些!团队上周埋头苦干,修复了大量 bug,以减少自 v7 发布以来激增的问题数量。
react-router
- 修复 react-router
在 NodeNext
下的模块增强问题 (#13498)react-router
- 不要在 react-router/dom
的 CJS 导出中捆绑 react-router
(#13497)react-router
- 修复了一个 bug,即如果一个正在重新验证的 loader
进行了重定向,一个正在提交的 fetcher
会卡在 loading
状态 (#12873)react-router
- 修复如果服务器 loader
返回 undefined
时的水合错误 (#13496)react-router
- 修复数据模式下初始加载 404 的场景 (#13500)react-router
- 稳定 useRevalidator
的 revalidate
函数 (#13542)react-router
- 如果 clientAction
在框架模式下抛出 data()
结果,则保留状态码 (#13522)react-router
- 增强对路径中前导双斜杠的防御,以避免 URL 构造函数抛出 Invalid URL
错误 (#13510)new URL("//", window.location.origin)
抛出的错误。react-router
- 移除 navigator.connection.saveData
的 Navigator
声明,以避免在用户代码中干扰除 saveData
之外的任何其他类型 (#13512)react-router
- 修复对于最后一个 URL 段是动态参数的路由,在 .data
请求中 handleError
的 params
值不正确的问题 (#13481)react-router
- 在懒加载路由发现中检测到清单版本不匹配时,不再在重新加载前触发 ErrorBoundary
界面 (#13480)react-router
- 内联 turbo-stream@2.4.1
依赖项,并修复 Map
/Set
实例的解码顺序问题 (#13518)react-router
- 仅在开发模式下渲染开发警告 (#13461)react-router
- 在 dataStrategy
请求被中止时,短路后续处理 (#13521)Cannot read properties of undefined (reading 'result')
的非用户可见的控制台错误。@react-router/dev
- 支持项目根目录中没有 package.json
,只要它存在于父目录中即可 (#13472)@react-router/dev
- 当通过 CLI 的 --config
/-c
标志提供自定义 Vite 配置文件路径时,如果未明确提供项目根目录,则默认为包含 Vite 配置文件的目录 (#13472)@react-router/dev
- 在 routes.ts
上下文中,确保 import.meta.env.MODE
能够遵循 --mode
标志 (#13485)routes.ts
上下文中的 import.meta.env.MODE
对于 dev
和 typegen --watch
命令总是为 "development"
,否则解析为 "production"
。这些默认值仍然有效,但如果提供了 --mode
标志,现在它将具有更高的优先级。@react-router/dev
- 确保 CLI 命令中项目根目录解析逻辑的一致性 (#13472)@react-router/dev
- 当使用 vite-node
执行 react-router.config.ts
和 routes.ts
时,确保忽略 PostCSS 配置文件 (#13489)@react-router/dev
- 在开发期间提取关键 CSS 时,确保从客户端环境加载,以避免与那些对 SSR 环境处理方式不同的插件产生问题 (#13503)@react-router/dev
- 修复在使用 HTTPS 的开发模式下出现的“Status message is not supported by HTTP/2”错误 (#13460)@react-router/dev
- 在开发过程中创建或删除 react-router.config.ts
时更新配置 (#12319)@react-router/dev
- 在 Vite 构建开始前,跳过不必要的 routes.ts
评估 (#13513)@react-router/dev
- 修复由生成的类型引起的 TS2300: Duplicate identifier
(重复标识符)错误 (#13499)href
生成的类型(.react-router/types/+register.ts
)中出现重复条目,从而导致类型检查错误。⚠️ 不稳定功能不建议在生产环境中使用
react-router
- 修复了在中间件用例中错误冒泡的一些错误 (#13538)@react-router/dev
- 当启用 future.unstable_viteEnvironmentApi
时,确保在未配置 environments.client.build.assetsDir
的情况下,Vite 配置中的 build.assetsDir
得到遵循 (#13491)create-react-router
react-router
@react-router/architect
@react-router/cloudflare
@react-router/dev
@react-router/express
@react-router/fs-routes
@react-router/node
@react-router/remix-config-routes-adapter
@react-router/serve
完整变更日志: v7.5.3...v7.6.0
日期: 2025-04-28
react-router
- 修复了冒泡的 action 错误会导致在处理该错误的 ErrorBoundary
路由中清除 loaderData
的错误 (#13476)react-router
- 处理来自 clientLoader.hydrate
初始加载执行的重定向 (#13477)完整变更日志: v7.5.2...v7.5.3
日期: 2025-04-24
修复了 2 个安全漏洞,这些漏洞可能因发送专用于构建时 SPA 模式和预渲染的特定头信息而导致缓存中毒攻击(GHSA-f46r-rw29-r322, GHSA-cpj6-fhp6-mr6j)。
react-router
- 调整通过头信息实现预渲染/SPA 模式的方法 (#13453)react-router
- 更新单次获取(Single Fetch)以处理 Remix v2 中用于 ?_data
请求的 204 重定向 (#13364)express
/hono
中间件)触发对 .data
请求的重定向。.data
请求中返回如下响应:X-Remix-Redirect: <new-location>
头信息X-Remix-Replace: true
或 X-Remix-Reload-Document: true
头信息以模拟 replace()
/redirectDocument()
功能完整变更日志: v7.5.1...v7.5.2
日期: 2025-04-17
react-router
- 当使用基于对象的 route.lazy
API 时,HydrateFallback
和 hydrateFallbackElement
属性在水合后懒加载路由时现在会被跳过 (#13376)
如果您将这些属性的代码移到一个单独的文件中,由于水合属性已经未使用(如果路由在水合期间不存在),您可以完全避免下载它们。例如:
createBrowserRouter([
{
path: "/show/:showId",
lazy: {
loader: async () => (await import("./show.loader.js")).loader,
Component: async () =>
(await import("./show.component.js")).Component,
HydrateFallback: async () =>
(await import("./show.hydrate-fallback.js")).HydrateFallback,
},
},
]);
react-router
- 修复了单次获取的一个错误,该错误导致在向上导航到重用的父路由时不会发出重新验证请求 (#13253)
react-router
- 在使用 ssr:false
+ prerender
配置时,当参数值改变时,正确地重新验证预渲染的路径 (#13380)
react-router
- 修复了当 loader 返回重定向时的预渲染问题 (#13365)
react-router
- 如果路由没有 loader,不要自动向 staticHandler.query()
的 context.loaderData
添加 null
(#13223)
undefined
,我们之前 loaderData[routeId] !== undefined
的检查已经不再足够,并已更改为 routeId in loaderData
的检查——这些 null
值可能会对这个新检查造成问题。createStaticHandler()
/<StaticRouterProvider>
进行手动 SSR,并使用 context.loaderData
来控制客户端上的 <RouterProvider>
水合行为,这可能对您来说是一个“破坏性错误修复”。⚠️ 不稳定功能不建议在生产环境中使用
react-router
- 当 getLoadContext
未更新以返回一个 Map
时,添加了更好的错误消息 (#13242)react-router
- 当启用中间件时,更新 LoaderFunctionArgs
/ActionFunctionArgs
的上下文类型 (#13381)react-router
- 向 dataStrategy
添加一个新的 unstable_runClientMiddleware
参数,以在自定义 dataStrategy
实现中启用中间件执行 (#13395)react-router
- 在 dataStrategy
中增加了对新的 unstable_shouldCallHandler
/unstable_shouldRevalidateArgs
API 的支持 (#13253)完整变更日志: v7.5.0...v7.5.1
日期: 2025-04-04
route.lazy
对象 API我们引入了一个新的 route.lazy
API,它让您对路由属性的懒加载有更精细的控制,这是使用 route.lazy()
函数签名无法实现的。这对于框架模式和性能关键的库模式应用程序非常有用。
createBrowserRouter([
{
path: "/show/:showId",
lazy: {
loader: async () => (await import("./show.loader.js")).loader,
action: async () => (await import("./show.action.js")).action,
Component: async () => (await import("./show.component.js")).Component,
},
},
]);
⚠️ 如果您采用了 route.unstable_lazyMiddleware
API,这将是一个破坏性变更,因为它已被移除,取而代之的是 route.lazy.unstable_middleware
。更多信息请参见下面的“不稳定变更”部分。
react-router
- 为 route.lazy
添加了基于对象的细粒度 API,以支持单个路由属性的懒加载 (#13294)@react-router/dev
- 更新可选的 wrangler
对等依赖项范围,以支持 wrangler
v4 (#13258)@react-router/dev
- 在子编译器中恢复依赖项优化,以修复在使用 vite-plugin-cloudflare
并导入 Node.js 内置模块时出现的 depsOptimizer is required in dev mode
错误 (#13317)⚠️ 不稳定功能不建议在生产环境中使用
react-router
- 引入 future.unstable_subResourceIntegrity
标志,该标志为浏览器将要加载的脚本生成带有 integrity
的 importmap
(#13163)react-router
- 移除了对 route.unstable_lazyMiddleware
属性的支持 (#13294)route.lazy.unstable_middleware
API。@react-router/dev
- 当启用 future.unstable_viteEnvironmentApi
时,确保在配置了自定义 Vite base
的情况下,开发环境中的关键 CSS 也能正常工作 (#13305)create-react-router
react-router
@react-router/architect
@react-router/cloudflare
@react-router/dev
@react-router/express
@react-router/fs-routes
@react-router/node
@react-router/remix-config-routes-adapter
@react-router/serve
完整变更日志: v7.4.1...v7.5.0
日期: 2025-03-28
修复了一个安全漏洞,该漏洞由于端口清理不充分,允许通过 Host
和 X-Forwarded-Host
头信息进行 URL 操作和潜在的缓存污染 (GHSA-4q56-crqp-v477/CVE-2025-31137)。
react-router
- 对 route.lazy
函数的调用进行去重处理 (#13260)@react-router/dev
- 修复预渲染错误消息中的路径问题 (#13257)@react-router/dev
- 当 moduleDetection
设置为 force
时,修复虚拟模块的类型生成问题 (#13267)@react-router/express
- 改进对 x-forwarded-host
头信息的验证,以防止潜在的安全问题 (#13309)⚠️ 不稳定功能不建议在生产环境中使用
react-router
- 修复 unstable_MiddlewareFunction
的类型,以避免当中间件不返回值时出现类型错误 (#13311)react-router
- 增加对 route.unstable_lazyMiddleware
函数的支持,以允许懒加载中间件逻辑 (#13210)route.lazy
返回 unstable_middleware
,这可能是一个破坏性变更。route.lazy
的返回值中不再支持 route.unstable_middleware
属性。route.unstable_lazyMiddleware
。@react-router/dev
- 当同时启用 future.unstable_middleware
和 future.unstable_splitRouteModules
时,尽可能将 unstable_clientMiddleware
路由导出拆分到单独的块中 (#13210)@react-router/dev
- 通过确保仅在定义了 unstable_clientMiddleware
时,路由模块才在中间件阶段阻塞,从而提高 future.unstable_middleware
的性能 (#13210)完整变更日志: v7.4.0...v7.4.1
日期: 2025-03-19
@react-router/dev
- 为 virtual:react-router/server-build
模块生成类型 (#13152)react-router
- 修复 SPA 模式下初始加载重定向时的根 loader 数据问题 (#13222)react-router
- 在懒加载路由发现中为向上的非即时发现路由加载祖先的无路径/索引路由 (#13203)react-router
- 修复 ssr:true
应用中仅有 clientLoader
的路由的 shouldRevalidate
行为 (#13221)@react-router/dev
- 修复与其他使用 configureServer
和/或 configurePreviewServer
钩子的 Vite 插件的冲突 (#13184)⚠️ 不稳定功能不建议在生产环境中使用
react-router
- 如果中间件抛出错误,确保我们只通过 next()
冒泡错误本身,不再泄露 MiddlewareError
实现细节 (#13180)catch
由 next()
函数抛出的错误,这可能是一个破坏性变更。react-router
- 修复启用中间件时 RequestHandler
的 loadContext
参数类型 (#13204)react-router
- 将 Route.unstable_MiddlewareFunction
的返回值从 Response | void
更新为 Response | undefined
(#13199)@react-router/dev
- 当 future.unstable_splitRouteModules
设置为 "enforce"
时,允许可拆分和不可拆分的根路由导出,因为它总是在一个单独的块中 (#13238)@react-router/dev
- 当启用 future.unstable_viteEnvironmentApi
时,允许覆盖默认 SSR 环境的插件(如 @cloudflare/vite-plugin
)放在 React Router 插件之前或之后 (#13183)create-react-router
react-router
@react-router/architect
@react-router/cloudflare
@react-router/dev
@react-router/express
@react-router/fs-routes
@react-router/node
@react-router/remix-config-routes-adapter
@react-router/serve
完整变更日志: v7.3.0...v7.4.0
日期: 2025-03-06
fetcherKey
作为参数添加到 patchRoutesOnNavigation
(#13061)react-router
- 在活动会话期间的新部署中检测并处理清单偏移问题 (#13061)fetcher
调用时,这种不匹配将触发当前路径的文档重新加载。react-router
- 在 SPA 模式下,跳过开发服务器中的资源路由流程 (#13113)react-router
- 修复使用 basename
时单次获取的 _root.data
请求 (#12898)react-router
- 修复包含 Record
的 loaderData
和 actionData
的类型 (#13139)unstable_SerializesTo
的用户,这是一个破坏性变更——更多信息请参见下面“不稳定变更”部分的说明。@react-router/dev
- 修复对自定义客户端 build.rollupOptions.output.entryFileNames
的支持 (#13098)@react-router/dev
- 修复当配置了 serverBundles
选项或由预设(例如,来自 @vercel/react-router
的 vercelPreset
)提供时,prerender
选项的使用问题 (#13082)@react-router/dev
- 修复对自定义 build.assetsDir
的支持 (#13077)@react-router/dev
- 移除未使用的依赖项 (#13134)@react-router/dev
- 在“SPA 模式”服务器构建中,为除根路由外的所有路由创建存根,以避免当路由模块或其依赖项导入非 SSR 友好模块时出现问题 (#13023)@react-router/dev
- 移除未使用的 Vite 文件系统监视器 (#13133)@react-router/dev
- 修复当配置了 serverBundles
选项时,对自定义 SSR 构建输入的支持 (#13107)future.unstable_viteEnvironmentApi
和 serverBundles
选项的消费者,服务器包 ID 中不再支持连字符,因为它们也需要是有效的 Vite 环境名称。@react-router/dev
- 通过从开发服务器请求中剥离 HTTP/2 伪头信息,修复使用 HTTPS 时的开发服务器问题 (#12830)@react-router/dev
- 当使用 cloudflareDevProxy
Vite 插件时,在第一次开发服务器请求时懒加载 Cloudflare 平台代理,以避免创建不必要的 workerd
进程 (#13016)@react-router/dev
- 修复布局路由及其相应索引路由在类型生成中的重复条目问题 (#13140)@react-router/express
- 更新 express
的 peerDependency
以包含 v5 (https://github.com/remix-run/react-router/pull/13064) (#12961)⚠️ 不稳定功能不建议在生产环境中使用
react-router
- 为客户端数据路由器添加 context
支持(不稳定)(#12941)react-router
- 支持路由上的中间件(不稳定)(#12941)@react-router/dev
- 修复当 SSR 环境被另一个插件配置为自定义 Vite.DevEnvironment
而不是默认的 Vite.RunnableDevEnvironment
时,使用 future.unstable_viteEnvironmentApi
出现的错误 (#13008)@react-router/dev
- 当启用 future.unstable_viteEnvironmentApi
并且 SSR 环境的 optimizeDeps.noDiscovery
被禁用时,定义 optimizeDeps.entries
和 optimizeDeps.include
(#13007)context
(不稳定)您的应用程序 clientLoader
/clientAction
函数(或库模式下的 loader
/action
)现在将在客户端接收一个 context
参数。这是一个 unstable_RouterContextProvider
的实例,您可以与类型安全的上下文(类似于 React.createContext
)一起使用,它与相应的 unstable_clientMiddleware
API 结合使用时最为有用。
import { unstable_createContext } from "react-router";
type User = {
/*...*/
};
const userContext = unstable_createContext<User>();
const sessionMiddleware: Route.unstable_ClientMiddlewareFunction = async ({
context,
}) => {
let user = await getUser();
context.set(userContext, user);
};
export const unstable_clientMiddleware = [sessionMiddleware];
export function clientLoader({ context }: Route.ClientLoaderArgs) {
let user = context.get(userContext);
let profile = await getProfile(user.id);
return { profile };
}
与服务器端请求类似,每次导航(或 fetcher
调用)都会创建一个新的 context
。如果您有希望为每个请求在上下文中填充的初始数据,您可以在应用程序的根部提供一个 unstable_getContext
函数。
createBrowserRouter(routes, { unstable_getContext })
<HydratedRouter unstable_getContext>
此函数应返回一个类型为 unstable_InitialContext
的值,这是一个 Map<unstable_RouterContext, unknown>
,包含上下文和初始值。
const loggerContext = unstable_createContext<(...args: unknown[]) => void>();
function logger(...args: unknown[]) {
console.log(new Date.toISOString(), ...args);
}
function unstable_getContext() {
let map = new Map();
map.set(loggerContext, logger);
return map;
}
中间件是在 future.unstable_middleware
标志后面实现的。要启用它,您必须在 react-router.config.ts
文件中启用该标志和类型。
import type { Config } from "@react-router/dev/config";
import type { Future } from "react-router";
declare module "react-router" {
interface Future {
unstable_middleware: true; // 👈 Enable middleware types
}
}
export default {
future: {
unstable_middleware: true, // 👈 Enable middleware
},
} satisfies Config;
⚠️ 中间件是不稳定的,不应在生产环境中采用。对于 clientMiddleware
,在路由模块加载方面至少有一个已知的性能退化,我们将在稳定发布前解决这个问题。
⚠️ 启用中间件包含对传递给您的 loader
/action
函数的 context
参数的破坏性变更——更多信息请参见下文。
启用后,路由可以定义一个中间件函数数组,这些函数将在路由处理程序运行之前顺序执行。这些函数接受与 loader
/action
相同的参数,外加一个额外的 next
参数来运行剩余的数据管道。这允许中间件在处理程序执行前后执行逻辑。
// Framework mode
export const unstable_middleware = [serverLogger, serverAuth]; // server
export const unstable_clientMiddleware = [clientLogger]; // client
// Library mode
const routes = [
{
path: "/",
// Middlewares are client-side for library mode SPA's
unstable_middleware: [clientLogger, clientAuth],
loader: rootLoader,
Component: Root,
},
];
这是一个简单的客户端日志记录中间件示例,可以放在根路由上:
const clientLogger: Route.unstable_ClientMiddlewareFunction = async (
{ request },
next,
) => {
let start = performance.now();
// Run the remaining middlewares and all route loaders
await next();
let duration = performance.now() - start;
console.log(`Navigated to ${request.url} (${duration}ms)`);
};
请注意,在上面的示例中,next
/middleware
函数不返回任何内容。这是设计使然,因为在客户端没有像在服务器上运行的中间件那样需要通过网络发送的“响应”。数据都由有状态的 router
在幕后处理。
对于服务器端中间件,next
函数将返回 React Router 将通过网络发送的 HTTP Response
,从而让您有机会根据需要进行更改。您可以抛出一个新的响应来短路并立即响应,或者您可以返回一个新的或修改过的响应来覆盖 next()
返回的默认响应。
const serverLogger: Route.unstable_MiddlewareFunction = async (
{ request, params, context },
next,
) => {
let start = performance.now();
// 👇 Grab the response here
let res = await next();
let duration = performance.now() - start;
console.log(`Navigated to ${request.url} (${duration}ms)`);
// 👇 And return it here (optional if you don't modify the response)
return res;
};
您可以从中间件中抛出一个 redirect
来短路任何剩余的处理。
import { sessionContext } from "../context";
const serverAuth: Route.unstable_MiddlewareFunction = (
{ request, params, context },
next,
) => {
let session = context.get(sessionContext);
let user = session.get("user");
if (!user) {
session.set("returnTo", request.url);
throw redirect("/login", 302);
}
};
请注意,在这种情况下,您不需要进行任何后处理,因此不需要调用 next
函数或返回 Response
。
这是另一个使用服务器中间件检测 404 并检查 CMS 以获取重定向的示例:
const redirects: Route.unstable_MiddlewareFunction = async ({
request,
next,
}) => {
// attempt to handle the request
let res = await next();
// if it's a 404, check the CMS for a redirect, do it last
// because it's expensive
if (res.status === 404) {
let cmsRedirect = await checkCMSRedirects(request.url);
if (cmsRedirect) {
throw redirect(cmsRedirect, 302);
}
}
return res;
};
有关 middleware
API/设计的更多信息,请参阅决策文档。
context
参数当启用中间件时,您的应用程序将在 loader 和 action 中使用不同类型的 context
参数,以提供更好的类型安全性。context
将不再是 AppLoadContext
,而是一个 ContextProvider
的实例,您可以与类型安全的上下文(类似于 React.createContext
)一起使用。
import { unstable_createContext } from "react-router";
import { Route } from "./+types/root";
import type { Session } from "./sessions.server";
import { getSession } from "./sessions.server";
let sessionContext = unstable_createContext<Session>();
const sessionMiddleware: Route.unstable_MiddlewareFunction = ({
context,
request,
}) => {
let session = await getSession(request);
context.set(sessionContext, session);
// ^ must be of type Session
};
// ... then in some downstream middleware
const loggerMiddleware: Route.unstable_MiddlewareFunction = ({
context,
request,
}) => {
let session = context.get(sessionContext);
// ^ typeof Session
console.log(session.get("userId"), request.method, request.url);
};
// ... or some downstream loader
export function loader({ context }: Route.LoaderArgs) {
let session = context.get(sessionContext);
let profile = await getProfile(session.get("userId"));
return { profile };
}
如果您正在使用带有 getLoadContext
函数的自定义服务器,从服务器适配器层传递的初始上下文值的返回值不再是一个对象,而应返回一个 unstable_InitialContext
(Map<RouterContext, unknown>
)。
let adapterContext = unstable_createContext<MyAdapterContext>();
function getLoadContext(req, res): unstable_InitialContext {
let map = new Map();
map.set(adapterContext, getAdapterContext(req));
return map;
}
unstable_SerializesTo
unstable_SerializesTo
添加了一种为其他库和框架作者(如 Apollo)在单次获取中注册自定义序列化类型的方法。它是通过一个品牌类型实现的,其品牌属性被设为可选,以便于转换任意值。
// without the brand being marked as optional
let x1 = 42 as unknown as unstable_SerializesTo<number>;
// ^^^^^^^^^^
// with the brand being marked as optional
let x2 = 42 as unstable_SerializesTo<number>;
然而,这破坏了 loaderData
和 actionData
中任何 Record
类型的类型推断,因为这些类型现在会(错误地)匹配 unstable_SerializesTo
。这影响了所有用户,而不仅仅是那些依赖 unstable_SerializesTo
的用户。为了修复这个问题,unstable_SerializesTo
的品牌属性被标记为必需而不是可选。
对于使用 unstable_SerializesTo
的库和框架作者,您可能需要在转换为 unstable_SerializesTo
之前添加 as unknown
类型转换。
create-react-router
react-router
@react-router/architect
@react-router/cloudflare
@react-router/dev
@react-router/express
@react-router/fs-routes
@react-router/node
@react-router/remix-config-routes-adapter
@react-router/serve
完整变更日志: v7.2.0...v7.3.0
日期: 2025-02-18
href
工具在框架模式下,我们现在为您提供了一个完全类型安全的 href
工具,为您的应用程序中的链接提供路径自动完成和参数验证的温馨体验。
import { href } from "react-router";
export default function Component() {
const link = href("/blog/:slug", { slug: "my-first-post" });
// ^ type-safe! ^ Also type-safe!
return (
<main>
<Link to={href("/products/:id", { id: "asdf" })} />
<NavLink to={href("/:lang?/about", { lang: "en" })} />
</main>
);
}
如果您传递了错误的路径值或错误的参数值,现在会得到类型错误。
const badPath = href("/not/a/valid/path");
// ^ Error!
const badParam = href("/blog/:slug", { oops: "bad param" });
// ^ Error!
此版本增强了在使用 ssr:false
进行预渲染时,将预渲染路径与以“SPA 模式”运行的其他路径结合使用的能力。
ssr:false
但没有 prerender
配置,这被视为“SPA 模式”,生成的 index.html
文件将只渲染到根路由,并能够为任何有效的应用程序路径进行水合。ssr:false
并带有 prerender
配置,但*不*包括 /
路径(即 prerender: ['/blog/post']
),那么我们仍然会生成一个“SPA 模式”的 index.html
文件,该文件可以为应用程序中的任何路径进行水合。ssr:false
并在 prerender
配置中包含 /
路径,则生成的 index.html
文件将特定于根索引路由,因此我们现在还将在 __spa-fallback.html
中生成一个单独的“SPA 模式”文件,您可以为非预渲染路径提供服务/水合。更多信息,请参阅预渲染文档。
loader
SPA 模式过去禁止在所有路由中使用 loader,以便我们可以为应用程序中的任何路径进行水合。然而,由于根路由总是在构建时渲染,我们可以为根路由解除这个限制。
为了在预渲染期间使用您的构建时 loader 数据,我们现在还将 loaderData
作为路由上 HydrateFallback
组件的可选属性公开。
HydrateFallback
是因为*子*路由正在加载而渲染,这个属性就会被定义。HydrateFallback
是因为路由本身有自己的水合 clientLoader
而渲染,这个属性将是 undefined
。react-router
- 新的类型安全的 href
工具,保证链接指向您应用中的实际路径 (#13012)@react-router/dev
- 当使用 ssr:false
预渲染 /
路由时,生成一个“SPA 回退”HTML 文件 (#12948)@react-router/dev
- 在 SPA 模式下允许根路由中有 loader
,因为它可以在构建时被调用/服务器渲染 (#12948)Route.HydrateFallbackProps
现在也接收 loaderData
。react-router
- 禁用所有 ssr:false
应用的懒加载路由发现,而不仅仅是“SPA 模式”,因为没有运行时服务器来提供搜索参数配置的 __manifest
请求 (#12894)ssr:false
的应用。prerender
场景中,我们会预渲染 /__manifest
文件,但这做了一些关于静态文件服务器行为的不必要假设。react-router
- 在 SPA 模式下不应用单次获取重新验证的性能退化,因为没有服务器 HTTP 请求 (#12948)react-router
- 正确处理跨越预渲染/SPA 边界的重新验证 (#13021).data
请求,因为请求会返回 404。ssr:false
模式下,所有 loader
数据都是静态的,因为它是在构建时生成的。clientLoader
来做任何动态的事情。loader
而没有 clientLoader
,我们默认禁用重新验证,因为没有新的数据可以检索。dataStrategy
中没有 shouldLoad=true
的服务器 loader,我们会短路并跳过单次获取的 .data
请求逻辑。.data
请求。react-router
- 当设置 ssr:false
时,使开发服务器行为与静态文件服务器行为保持一致 (#12948)prerender
配置时,仅 SSR 到根 HydrateFallback
(SPA 模式)。prerender
配置但当前路径未被预渲染时,仅 SSR 到根 HydrateFallback
(SPA 回退)。.data
请求,返回 404。react-router
- 提高框架模式下 CSS 副作用的预取性能 (#12889)react-router
- 在懒加载路由发现中正确处理中断的清单请求 (#12915)@react-router/dev
- 处理 Vite 配置中的自定义 envDir
(#12969)@react-router/dev
- 修复 CLI 解析,以允许无参数的 npx react-router
用法 (#12925)@react-router/dev
- 当使用 prerender:true
时跳过仅有 action 的资源路由 (#13004)@react-router/dev
- 增强使用 ssr:false
时的无效导出检测 (#12948)ssr:false
的路由中禁止使用 headers
/action
函数,因为没有运行时服务器来运行它们。loader
函数则更细微,取决于给定的路由是否被预渲染。ssr:false
且没有 prerender
配置时,只有 root
路由可以有 loader
。ssr:false
并有 prerender
配置时,只有与 prerender
路径匹配的路由可以有 loader
。@react-router/dev
- 在 ssr:false
+ prerender
应用中,对以下边缘情况在构建时报错:(#13021)loader
(没有 clientLoader
)。loaderData
,因为没有服务器来运行 loader
。clientLoader
或预渲染子路径来解决。clientLoader
,在非预渲染路径上调用 serverLoader()
将会抛出 404。@react-router/dev
- 将预渲染的资源路由 .data
文件限制为仅目标路由 (#13004)@react-router/dev
- 修复二进制文件的预渲染问题 (#13039)@react-router/dev
- 修复重复参数的类型生成 (#13012)/a/:id/b/:id?/c/:id
这样的路径模式,最后一个 :id
将设置 useParams
和 params
属性中 id
的值。/a/1/b/2/c/3
在运行时将导致值为 { id: 3 }
。/a/1/b/2/c/3
生成的类型类似 { id: [1,2,3] }
。/a/1/b/2/c/3
现在生成的类型类似 { id: 3 }
。@react-router/dev
- 修复加载 package.json
以执行 react-router --version
的路径问题 (#13012)⚠️ 不稳定功能不建议在生产环境中使用
react-router
- 为库作者添加 unstable_SerializesTo
品牌类型,以注册可被 React Router 流式格式(turbo-stream
)序列化的类型 (#12264)@react-router/dev
- 通过 future.unstable_splitRouteModules
为框架模式添加对拆分路由模块的不稳定支持 (#11871)@react-router/dev
- 添加 future.unstable_viteEnvironmentApi
标志以启用实验性的 Vite 环境 API 支持 (#12936)⚠️ 此功能目前是不稳定的,通过
future.unstable_splitRouteModules
标志启用。我们欢迎任何感兴趣的用户在本地试用并提供反馈,但我们不建议在生产环境中使用它。如果您确实选择在生产环境中采用此标志,请确保对您的生产构建进行充分测试,以确保优化按预期工作。
路由模块 API 的便利之一是路由所需的一切都在一个文件中。不幸的是,在使用 clientLoader
、clientAction
和 HydrateFallback
API 时,这在某些情况下会带来性能成本。
作为一个基本示例,请考虑这个路由模块:
import { MassiveComponent } from "~/components";
export async function clientLoader() {
return await fetch("https://example.com/api").then((response) =>
response.json(),
);
}
export default function Component({ loaderData }) {
return <MassiveComponent data={loaderData} />;
}
在此示例中,我们有一个最小的 clientLoader
导出,它进行一个基本的 fetch 调用,而默认的组件导出则要大得多。这对性能是一个问题,因为这意味着如果我们想在客户端导航到此路由,必须在客户端加载器开始运行之前下载整个路由模块。
将其可视化为时间线:
Get Route Module: |--=======|
Run clientLoader: |-----|
Render: |-|
相反,我们希望将其优化为以下内容:
Get clientLoader: |--|
Get Component: |=======|
Run clientLoader: |-----|
Render: |-|
为了实现此优化,React Router 将在生产构建过程中将路由模块拆分为多个较小的模块。在这种情况下,我们最终将得到两个独立的虚拟模块——一个用于客户端加载器,一个用于组件及其依赖项。
export async function clientLoader() {
return await fetch("https://example.com/api").then((response) =>
response.json(),
);
}
import { MassiveComponent } from "~/components";
export default function Component({ loaderData }) {
return <MassiveComponent data={loaderData} />;
}
💡 此优化在框架模式下自动应用,但您也可以通过
route.lazy
在库模式下实现它,并将您的路由编写在多个文件中,如我们关于懒加载路由模块的博客文章中所述。
现在这些可作为单独的模块使用,客户端加载器和组件可以并行下载。这意味着客户端加载器可以在准备好后立即执行,而不必等待组件。
当使用更多路由模块 API 时,此优化效果更为显著。例如,当使用 clientLoader
、clientAction
和 HydrateFallback
时,单个路由模块在客户端导航期间的时间线可能如下所示:
Get Route Module: |--~~++++=======|
Run clientLoader: |-----|
Render: |-|
这将被优化为以下内容:
Get clientLoader: |--|
Get clientAction: |~~|
Get HydrateFallback: SKIPPED
Get Component: |=======|
Run clientLoader: |-----|
Render: |-|
请注意,此优化仅在被拆分的路由模块 API 不在同一文件中共享代码时才有效。例如,以下路由模块无法被拆分:
import { MassiveComponent } from "~/components";
const shared = () => console.log("hello");
export async function clientLoader() {
shared();
return await fetch("https://example.com/api").then((response) =>
response.json(),
);
}
export default function Component({ loaderData }) {
shared();
return <MassiveComponent data={loaderData} />;
}
此路由仍然可以工作,但由于客户端加载器和组件都依赖于在同一文件中定义的 shared
函数,它将被降级为一个单一的路由模块。
为避免这种情况,您可以将导出之间共享的任何代码提取到一个单独的文件中。例如:
export const shared = () => console.log("hello");
然后您可以在路由模块中导入此共享代码,而不会触发降级:
import { MassiveComponent } from "~/components";
import { shared } from "./shared";
export async function clientLoader() {
shared();
return await fetch("https://example.com/api").then((response) =>
response.json(),
);
}
export default function Component({ loaderData }) {
shared();
return <MassiveComponent data={loaderData} />;
}
由于共享代码在其自己的模块中,React Router 现在能够将此路由模块拆分为两个独立的虚拟模块:
import { shared } from "./shared";
export async function clientLoader() {
shared();
return await fetch("https://example.com/api").then((response) =>
response.json(),
);
}
import { MassiveComponent } from "~/components";
import { shared } from "./shared";
export default function Component({ loaderData }) {
shared();
return <MassiveComponent data={loaderData} />;
}
如果您的项目对性能特别敏感,您可以将 unstable_splitRouteModules
未来标志设置为 "enforce"
。
export default {
future: {
unstable_splitRouteModules: "enforce",
},
};
此设置将在任何路由模块无法被拆分时引发错误。
Error splitting route module: routes/example/route.tsx
- clientLoader
This export could not be split into its own chunk because it shares code with other exports. You should extract any shared code into its own module and then import it within the route module.
create-react-router
react-router
@react-router/architect
@react-router/cloudflare
@react-router/dev
@react-router/express
@react-router/fs-routes
@react-router/node
@react-router/remix-config-routes-adapter
@react-router/serve
完整变更日志: v7.1.5...v7.2.0
日期: 2025-01-31
react-router
- 修复在 7.1.4
版本中通过 #12800 引入的回归问题,该问题导致使用懒加载路由发现(patchRoutesOnNavigation
)的应用程序在 splat 路由内导航到哈希路由时出现问题 (#12927)完整变更日志: v7.1.4...v7.1.5
日期: 2025-01-30
@react-router/dev
- 在使用 unstable_optimizeDeps
未来标志时,正确解析 Windows 文件路径以扫描 Vite 的依赖优化 (#12637)@react-router/dev
- 修复使用自定义服务器时的预渲染问题 - 以前我们最终尝试导入用户自定义服务器,而实际上我们想导入虚拟服务器构建模块 (#12759)react-router
- 在单次获取响应中正确处理不能有主体的状态码(204 等)(#12760)react-router
- 当抛出 data()
结果时,正确地将头信息作为 errorHeaders
冒泡 (#12846)headers
返回,则避免 Set-Cookie
头信息的重复react-router
- 停止对返回原始字符串/对象的资源路由报错,而是将它们序列化为 text/plain
或 application/json
响应 (#12848).data
扩展名的资源路由访问时.data
请求访问时,它们仍将通过 turbo-stream
进行编码react-router
- 优化懒加载路由发现的路径发现,优先使用在 body
级别的单个 querySelectorAll
调用,而不是在子树级别的多次调用 (#12731)react-router
- 通过在可能的情况下跳过冗余的 matchRoutes
调用来优化路由匹配 (#12800, #12882)react-router
- 内部重组以清理一些重复的路由模块类型 (#12799)完整变更日志: v7.1.3...v7.1.4
日期: 2025-01-17
@react-router/dev
- 修复 reveal
和 routes
CLI 命令 (#12745)完整变更日志: v7.1.2...v7.1.3
日期: 2025-01-16
react-router
- 修复在 fetcher 卸载时数据层中 fetcher 数据清理的问题 (#12681)react-router
- 不再依赖 symbol
来过滤 loader 数据中的 redirect
响应 (#12694)error TS4058: Return type of exported function has or is using name 'redirectSymbol' from external module "node_modules/..." but cannot be named.
redirect
响应类型不再使用 symbol
,这些错误应该不再出现。@react-router/dev
- 修复 Vite v6 中的默认外部条件 (#12644)@react-router/dev
- 修复路径缺少前导斜杠时预渲染 html/data 文件的匹配问题 (#12684)@react-router/dev
- 在运行时启用时,使用 module-sync
服务器条件。这修复了在使用对 React Router 有对等依赖的库时,在 Node 22.10.0+ 开发过程中的 React 上下文不匹配问题(例如 useHref() may be used only in the context of a <Router> component.
)(#12729)@react-router/dev
- 修复 react-refresh
的 source map (#12686)完整变更日志: v7.1.1...v7.1.2
日期: 2024-12-23
@react-router/dev
- 修复当可选参数传递给 CLI 时发生的崩溃问题 (#12609)完整变更日志: v7.1.0...v7.1.1
日期: 2024-12-20
react-router
- 抛出未包装的单次获取 redirect
,以与单次获取之前的行为保持一致 (#12506)react-router
- 在推断 loader 数据类型时忽略重定向 (#12527)react-router
- 移除 <Link prefetch>
警告,该警告在懒加载路由发现的世界中容易出现误报 (#12485)create-react-router
- 修复缺失的 fs-extra
依赖 (#12556)@react-router/dev
/@react-router/serve
- 如果 NODE_ENV
尚未设置,则正确初始化,以兼容 React 19 (#12578)@react-router/dev
- 从 ServerRouter
中移除剩余/未使用的 abortDelay
属性,并更新默认的 entry.server.tsx
以使用新的 streamTimeout
值用于单次获取 (#12478)abortDelay
功能已在 v7 中移除,因为它与 Remix v2 的 defer
实现耦合,但此属性的移除被遗漏了。entry.server
文件中使用此属性,您的应用可能没有按预期中止流,您将需要采用随单次获取引入的新的 streamTimeout
值。@react-router/fs-routes
- 如果路由目录缺失,则在 flatRoutes
中抛出错误 (#12407)create-react-router
react-router
@react-router/architect
@react-router/cloudflare
@react-router/dev
@react-router/express
@react-router/fs-routes
@react-router/node
@react-router/remix-config-routes-adapter
@react-router/serve
完整变更日志: v7.0.2...v7.1.0
日期: 2024-12-02
react-router
- 临时在 export map 中只使用一个构建,以便包可以对 react router 有一个对等依赖 (#12437)@react-router/dev
- 支持 moduleResolution
Node16
和 NodeNext
(#12440)@react-router/dev
- 为子路由生成更宽泛的 matches
和 params
类型 (#12397)matches
包括子路由匹配,params
包括子路由路径参数。matches
和 params
中生成类型。完整变更日志: v7.0.1...v7.0.2
日期: 2024-11-22
@react-router/dev
- 确保在 Vite 开发服务器重启时清理类型生成文件监视器 (#12331)@react-router/dev
- 将路由 error
作为属性传递给 ErrorBoundary
(#12338)完整变更日志: v7.0.0...v7.0.1
日期: 2024-11-21
react-router-dom
、@remix-run/react
、@remix-run/server-runtime
和 @remix-run/router
已合并到 react-router
包中。react-router-dom
,作为 react-router
所有内容的重新导出。@remix-run/cloudflare-pages
和 @remix-run/cloudflare-workers
已合并到 @react-router/cloudflare
包中。react-router-dom-v5-compat
和 react-router-native
包从 v7 开始被移除。Remix v2 过去常常通过各种运行时包(node
、cloudflare
、deno
)重新导出所有常见的 @remix-run/server-runtime
API,这样您就不需要在 package.json
中添加额外的 @remix-run/server-runtime
依赖。随着包合并到 react-router
中,这些常见的 API 现在不再通过运行时适配器重新导出。您应该从 react-router
导入所有常见的 API,并仅从运行时包导入特定于运行时的 API。
// Runtime-specific APIs
import { createFileSessionStorage } from "@react-router/node";
// Runtime-agnostic APIs
import { redirect, useLoaderData } from "react-router";
以下 API 已在 React Router v7 中移除:
json
defer
unstable_composeUploadHandlers
unstable_createMemoryUploadHandler
unstable_parseMultipartFormData
React Router v7 需要以下最低版本
node@20
installGlobals
方法来填充 fetch
API。react@18
, react-dom@18
Remix 和 React Router 遵循API 开发策略,利用“未来标志”来避免在主要版本中引入大量破坏性变更。相反,破坏性变更在次要版本中通过标志引入,允许用户自行选择加入。在下一个主要版本中,所有未来标志的行为都成为默认行为。
以下先前标记的行为现在是 React Router v7 中的默认行为:
future.v7_relativeSplatPath
future.v7_startTransition
future.v7_fetcherPersist
future.v7_normalizeFormMethod
future.v7_partialHydration
future.v7_skipActionStatusRevalidation
future.v3_fetcherPersist
future.v3_relativeSplatPath
future.v3_throwAbortReason
future.v3_singleFetch
future.v3_lazyRouteDiscovery
future.v3_optimizeDeps
Remix Vite 插件是使用 React Router v7 构建全栈 SSR 应用的正确方式。以前基于 esbuild
的编译器已不再可用。
重命名 vitePlugin
和 cloudflareDevProxyVitePlugin
对于从 Remix 迁移到 React Router 的消费者,vitePlugin
和 cloudflareDevProxyVitePlugin
导出已被重命名并移动 (#11904)
-import {
- vitePlugin as remix,
- cloudflareDevProxyVitePlugin,
-} from "@remix/dev";
+import { reactRouter } from "@react-router/dev/vite";
+import { cloudflareDevProxy } from "@react-router/dev/vite/cloudflare";
移除 manifest
选项
对于从 Remix 迁移到 React Router 的消费者,Vite 插件的 manifest
选项已被移除。manifest
选项已被更强大的 buildEnd
钩子取代,因为它接收 buildManifest
参数。如果需要,您仍然可以将构建清单写入磁盘,但您可能会发现将任何依赖于构建清单的逻辑写在 buildEnd
钩子本身内更方便。(#11573)
如果您曾使用 manifest
选项,可以将其替换为将清单写入磁盘的 buildEnd
钩子,如下所示:
// react-router.config.ts
import { type Config } from "@react-router/dev/config";
import { writeFile } from "node:fs/promises";
export default {
async buildEnd({ buildManifest }) {
await writeFile(
"build/manifest.json",
JSON.stringify(buildManifest, null, 2),
"utf-8"
);
},
} satisfies Config;
由于 React 19 将对在渲染过程中处理 Promise 提供一流的支持(通过 React.use
和 useAction
),我们现在可以放心地暴露先前返回 undefined
的 API 的 Promise。
useNavigate()
useSubmit()
useFetcher().load
useFetcher().submit
useRevalidator().revalidate()
routes.ts
当使用 React Router Vite 插件时,路由在 app/routes.ts
中定义。路由配置通过 routes
导出,符合 RouteConfig
类型。提供了路由辅助函数 route
、index
和 layout
,以使声明式类型安全的路由定义更容易。
// app/routes.ts
import {
type RouteConfig,
route,
index,
layout,
} from "@react-router/dev/routes";
export const routes: RouteConfig = [
index("./home.tsx"),
route("about", "./about.tsx"),
layout("./auth/layout.tsx", [
route("login", "./auth/login.tsx"),
route("register", "./auth/register.tsx"),
]),
route("concerts", [
index("./concerts/home.tsx"),
route(":city", "./concerts/city.tsx"),
route("trending", "./concerts/trending.tsx"),
]),
];
对于从 Remix 迁移到 React Router 的消费者,您仍然可以使用 @react-router/fs-routes
包在 routes.ts
中配置基于文件系统的路由。一个重现默认 Remix 设置的最小路由配置如下所示:
// app/routes.ts
import { type RouteConfig } from "@react-router/dev/routes";
import { flatRoutes } from "@react-router/fs-routes";
export const routes: RouteConfig = flatRoutes();
如果您想从基于文件系统的路由迁移到基于配置的路由,您可以通过将异步 flatRoutes
函数的结果扩展到基于配置的路由数组中来混合使用这两种方法。
// app/routes.ts
import { type RouteConfig, route } from "@react-router/dev/routes";
import { flatRoutes } from "@react-router/fs-routes";
export const routes: RouteConfig = [
// Example config-based route:
route("/hello", "./routes/hello.tsx"),
// File system routes scoped to a different directory:
...(await flatRoutes({
rootDirectory: "fs-routes",
})),
];
如果您曾使用 Remix 的 routes
选项来使用替代的文件系统路由约定,您可以使用 @react-router/remix-config-routes-adapter
将它们适配到新的 RouteConfig
格式。
例如,如果您在 Remix v2 中使用Remix v1 路由约定,您可以将 @react-router/remix-config-routes-adapter
与 @remix-run/v1-route-convention
结合使用,以将其适配到 React Router。
// app/routes.ts
import { type RouteConfig } from "@react-router/dev/routes";
import { remixConfigRoutes } from "@react-router/remix-config-routes-adapter";
import { createRoutesFromFolders } from "@remix-run/v1-route-convention";
export const routes: RouteConfig = remixConfigRoutes(async (defineRoutes) => {
return createRoutesFromFolders(defineRoutes, {
ignoredFilePatterns: ["**/.*", "**/*.css"],
});
});
另请注意,如果您曾使用 Remix 的 routes
选项来定义基于配置的路由,您也可以使用 @react-router/remix-config-routes-adapter
以最少的代码更改将它们适配到新的 RouteConfig
格式。虽然这提供了一条快速的迁移路径,但我们建议将任何基于配置的路由从 Remix 迁移到新的 RouteConfig
格式,因为这是一个相当直接的迁移。
// app/routes.ts
-import { type RouteConfig } from "@react-router/dev/routes";
+import { type RouteConfig, route } from "@react-router/dev/routes";
-import { remixConfigRoutes } from "@react-router/remix-config-routes-adapter";
-export const routes: RouteConfig = remixConfigRoutes(async (defineRoutes) => {
- defineRoutes((route) => {
- route("/parent", "./routes/parent.tsx", () => [
- route("/child", "./routes/child.tsx"),
- ]);
- });
-});
+export const routes: RouteConfig = [
+ route("/parent", "./routes/parent.tsx", [
+ route("/child", "./routes/child.tsx"),
+ ]),
+];
React Router 现在为您的每个路由模块生成类型,并将类型化的 props 传递给路由模块组件的导出 (#11961, #12019)。您可以通过从 ./+types/<route filename without extension>
导入它们来访问这些类型。
更多详情请参阅操作指南 > 路由模块类型安全和解释 > 类型安全。
React Router v7 在 Vite 插件中包含了一个新的 prerender
配置,以支持 SSG 用例。这将在构建时预渲染您的 .html
和 .data
文件,以便您可以在运行时从正在运行的服务器或 CDN 静态提供它们 (#11539)。
export default defineConfig({
plugins: [
reactRouter({
async prerender({ getStaticPaths }) {
let slugs = await fakeGetSlugsFromCms();
return [
...getStaticPaths(),
...slugs.map((slug) => `/product/${slug}`),
];
},
}),
tsconfigPaths(),
],
});
async function fakeGetSlugsFromCms() {
await new Promise((r) => setTimeout(r, 1000));
return ["shirt", "hat"];
}
react-router
)defer
实现,转而通过单次获取和 turbo-stream
使用原生 Promise (#11744)defer
AbortedDeferredError
type TypedDeferredData
UNSAFE_DeferredData
UNSAFE_DEFERRED_SYMBOL
react-router
中 (#11505)@remix-run/router
react-router-dom
@remix-run/server-runtime
@remix-run/testing
react-router-dom
包得以保留以简化迁移,但它只是重新导出 react-router
的所有 API。future.v7_startTransition
标志 (#11696)future.v7_normalizeFormMethod
未来标志 (#11697)@remix-run/router
公共 API 的导出:AgnosticDataIndexRouteObject
AgnosticDataNonIndexRouteObject
AgnosticDataRouteMatch
AgnosticDataRouteObject
AgnosticIndexRouteObject
AgnosticNonIndexRouteObject
AgnosticRouteMatch
AgnosticRouteObject
TrackedPromise
unstable_AgnosticPatchRoutesOnMissFunction
Action
-> 通过 react-router
导出为 NavigationType
Router
导出为 RemixRouter
,以区别于 RR 的 <Router>
getToPathname
(@private
)joinPaths
(@private
)normalizePathname
(@private
)resolveTo
(@private
)stripBasename
(@private
)createBrowserHistory
-> 推荐使用 createBrowserRouter
createHashHistory
-> 推荐使用 createHashRouter
createMemoryHistory
-> 推荐使用 createMemoryRouter
createRouter
createStaticHandler
-> 推荐使用 RR Dom 中的包装器 createStaticHandler
getStaticContextFromError
react-router
公共 API 的导出:Hash
Pathname
Search
@remix-run/router
包中移除 future.v7_prependBasename
(#11726)@remix-run/router
包中移除 future.v7_throwAbortReason
(#11728)exports
字段 (#11675)RemixContext
重命名为 FrameworkContext
(#11705)PrefetchPageDescriptor
被 PageLinkDescriptor
替换 (#11960)future.v7_partialHydration
标志 (#11725)<RouterProvider fallbackElement>
属性。fallbackElement
移动到根路由的 hydrateFallbackElement
/HydrateFallback
上。future.v7_partialHydration
(当使用 fallbackElement
时),state.navigation
在初始加载期间被填充。future.v7_partialHydration
时,state.navigation
在初始加载期间保持 "idle"
状态。future.v7_relativeSplatPath
未来标志 (#11695)v7_skipActionErrorRevalidation
v3_fetcherPersist
, v3_relativeSplatPath
, v3_throwAbortReason
createRemixStub
重命名为 createRoutesStub
(#11692)@remix-run/router
已弃用的 detectErrorBoundary
选项,推荐使用 mapRouteProperties
(#11751)react-router/dom
子路径导出,以正确地将 react-dom
作为可选的 peerDependency
(#11851)ReactDOM.flushSync()
而在 <RouterProvider>
中盲目地 import ReactDOM from "react-dom"
,因为这会破坏在非 DOM 环境中使用 createMemoryRouter
的情况。react-router/dom
导入,以获取使 ReactDOM.flushSync()
可用的正确组件。entry.client.tsx
中使用它:import { HydratedRouter } from 'react-router/dom'
createBrowserRouter
/createHashRouter
:import { RouterProvider } from "react-router/dom"
future.v7_fetcherPersist
标志 (#11731)undefined
(#11680, #12057)entry.client
中使用 createRemixRouter
/RouterProvider
,而不是 RemixBrowser
(#11469)json
工具 (#12146)Response.json
。@react-router/*
)future.v3_singleFetch
标志 (#11522)installGlobals()
,因为这应该不再需要。exports
字段 (#11675)react-router
的 API (#11702)crypto
全局变量。react-router
而不是特定平台的包中提供的:(#11837)createCookie
createCookieSessionStorage
createMemorySessionStorage
createSessionStorage
@remix-run/node
中的 installGlobals
函数已更新为定义 globalThis.crypto
,使用 Node 的 require('node:crypto').webcrypto
实现。createCookieFactory
createSessionStorageFactory
createCookieSessionStorageFactory
createMemorySessionStorageFactory
@remix-run/router
、@remix-run/server-runtime
和 @remix-run/react
中重复的类型,因为它们现在都存在于 react-router
中 (#12177)LoaderFunction
, LoaderFunctionArgs
, ActionFunction
, ActionFunctionArgs
, DataFunctionArgs
, RouteManifest
, LinksFunction
, Route
, EntryRoute
RouteManifest
类型现在稍微更严格,因为它使用的是以前的 @remix-run/router
的 RouteManifest
。Record<string, Route> -> Record<string, Route | undefined>
AppData
类型,转而在其使用的少数位置内联 unknown
。ServerRuntimeMeta*
类型,替换为它们所复制的 Meta*
类型Route.*
类型Route.*
类型useFetcher
之前有一个可选的泛型(主要由 Remix v2 使用),它期望的是数据类型typeof loader
/typeof action
)useFetcher<LoaderData>()
useFetcher<typeof loader>()
cookie
依赖更新至 ^1.0.1
- 请参阅 发布说明 了解任何破坏性变更 (#12172)@react-router/cloudflare
- 对于迁移到 React Router 的 Remix 用户,现在 @react-router/cloudflare
包中为 React Router 用户提供了所有来自 @remix-run/cloudflare-pages
的导出。不再有单独的 Cloudflare Pages 包。 (#11801)@react-router/cloudflare
- @remix-run/cloudflare-workers
包已被弃用。迁移到 React Router 的 Remix 用户应直接使用 @react-router/cloudflare
包。关于如何在 Cloudflare Workers 环境中使用 @react-router/cloudflare
的指导,请参考 Cloudflare Workers 模板。 (#11801)@react-router/dev
- 对于迁移到 React Router 的 Remix 用户,vitePlugin
和 cloudflareDevProxyVitePlugin
导出项已被重命名和移动。 (#11904)@react-router/dev
- 对于使用 Vite 插件的 buildEnd
钩子并迁移到 React Router 的 Remix 用户,解析出的 reactRouterConfig
对象不再包含 publicPath
属性,因为这属于 Vite,而不是 React Router (#11575)@react-router/dev
- 对于迁移到 React Router 的 Remix 用户,Vite 插件的 manifest
选项已被移除 (#11573)@react-router/dev
- 将默认的 isbot
版本更新到 v5,并放弃对 isbot@3
的支持 (#11770)package.json
中有 isbot@4
或 isbot@5
package.json
中有 isbot@3
并且您的仓库中有自己的 entry.server.tsx
文件isbot
升级到 @5
package.json
中有 isbot@3
并且您的仓库中没有自己的 entry.server.tsx
文件package.json
中将 isbot
升级到 @5
@react-router/dev
- 对于迁移到 React Router 的 Remix 用户,Vite 清单(即 .vite/manifest.json
)现在被写入每个构建子目录中,例如 build/client/.vite/manifest.json
和 build/server/.vite/manifest.json
,而不是 build/.vite/client-manifest.json
和 build/.vite/server-manifest.json
。这意味着构建输出现在更接近于一个典型的 Vite 项目。 (#11573)build/.vite
目录,以避免在生产环境中意外提供它们,特别是来自客户端构建的。后来通过添加额外的逻辑对此进行了改进,该逻辑在构建过程结束时删除这些 Vite 清单文件,除非在应用程序的 Vite 配置中启用了 Vite 的 build.manifest
。这大大降低了在生产环境中意外提供 Vite 清单的风险,因为它们仅在明确要求时才存在。因此,我们现在可以假设用户知道他们需要自己管理这些额外的文件,并且 React Router 可以安全地生成更标准的 Vite 构建输出。react-router
- 将参数、加载器数据和操作数据作为路由组件导出的 props (#11961)react-router
- 添加路由模块类型生成 (#12019)react-router
- 移除重复的 RouterProvider
实现 (#11679)react-router
- 稳定 unstable_dataStrategy
(#11969)react-router
- 稳定 unstable_patchRoutesOnNavigation
(#11970)react-router
- 在使用 Remix SSR 时为 Link
/NavLink
添加预取支持 (#11402)react-router
- 增强 ScrollRestoration
,使其能在 SSR 渲染的文档加载时正确恢复滚动位置 (#11401)@react-router/dev
- 在 React Router vite 插件中添加对 prerender
配置的支持,以支持现有的 SSG 用例 (#11539)@react-router/dev
- 移除内部的 entry.server.spa.tsx
实现,该实现与 Single Fetch 异步水合方法不兼容 (#11681)@react-router/serve
:更新 express.static
配置以支持新的 prerender
API (#11547)build/client/assets
文件夹中的资源像以前一样提供,带有 1 年不可变的 Cache-Control
标头.html
和 .data
文件,不带特定的 Cache-Control
标头提供.data
文件以 Content-Type: text/x-turbo
提供express.static
添加此项时,似乎还会为 .data
文件添加一个 Cache-Control: public, max-age=0
substr
替换为 substring
(#12080)react-router
- 修复从 loaders/actions 使用 data()
返回的重定向 (#12021)@react-router/dev
- 为资源路由启用预渲染 (#12200)@react-router/dev
- 相对于扁平输出文件结构解析配置目录 (#12187)react-router
@react-router/architect
@react-router/cloudflare
@react-router/dev
@react-router/express
@react-router/fs-routes
@react-router/node
@react-router/remix-config-routes-adapter
@react-router/serve
完整更新日志: v6.28.0...v7.0.0
日期:2025-05-20
6.29.0
中添加的旨在减少 matchRoutes
调用的优化,因为它引发了其他问题 (#13623)v7_relativeSplatPath
设置为 false
时,停止记录无效警告 (#13502)完整更新日志: v6.30.0...v6.30.1
日期:2025-02-27
fetcherKey
作为参数添加到 patchRoutesOnNavigation
(#13109)6.29.0
中由 #12169 引入的回归问题,该问题导致在使用惰性路由发现(patchRoutesOnNavigation
)的应用中,导航到 splat 路由内的哈希路由时出现问题 (#13108)完整更新日志: v6.29.0...v6.30.0
日期: 2025-01-30
signal
作为参数提供给 patchRoutesOnNavigation
(#12900)data()
结果时正确冒泡标头 (#12845)matchRoutes
调用来优化路由匹配 (#12169)patchRoutesOnNavigation
的 path
参数中剥离搜索参数 (#12899)完整更新日志: v6.28.2...v6.29.0
日期: 2025-01-16
future.v7_fetcherPersist
时手动使用 fetcher key
的问题 (#12674)完整更新日志: v6.28.1...v6.28.2
日期: 2024-12-20
false
来选择不接收 v7 弃用警告 (#12441)完整更新日志: v6.28.0...v6.28.1
日期:2024-11-06
json
/defer
添加弃用警告,推荐返回原始对象完整更新日志: v6.27.0...v6.28.0
日期:2024-10-11
此版本为即将发布的 React Router v7(参见待定,更多信息请见这些文章)稳定了一批“不稳定”的 API
unstable_dataStrategy
→ dataStrategy
(createBrowserRouter
及其相关函数) (文档)unstable_patchRoutesOnNavigation
→ patchRoutesOnNavigation
(createBrowserRouter
及其相关函数) (文档)unstable_flushSync
→ flushSync
(useSubmit
, fetcher.load
, fetcher.submit
) (文档)unstable_viewTransition
→ viewTransition
(<Link>
, <Form>
, useNavigate
, useSubmit
) (文档)unstable_flushSync
选项 (#11989)unstable_viewTransition
选项和相应的 unstable_useViewTransitionState
钩子 (#11989)unstable_dataStrategy
(#11974)unstable_patchRoutesOnNavigation
(#11973)PatchRoutesOnNavigationFunctionArgs
类型 (#11967)?index
参数时,提交到当前上下文路由(带有 index 子路由的父路由)的 bug (#12003)useFormAction
的 bug - 当移除 ?index
参数时,它不会保留其他非 Remix 的 index
参数 (#12003)preventScrollReset
的 bug (#11999)console.error
(#12050)partialHydration
进行水合时出现错误的 bug (#12070)patchRoutesOnNavigation
调用的问题 (#12055)unstable_
API 中的此行为,这可能是一个破坏性变更patchRoutesOnNavigation
调用,以便具有相同起点/终点的多个导航只执行一次函数并使用相同的 promiserequest.signal
中止)时 patch
的短路行为相冲突,因为第一次调用的 patch
会无操作import()
之类的异步路由的重复调用已经自动缓存了 - 如果没有,用户在用户空间实现这个缓存也很容易unstable_patchRoutesOnNavigation
移除内部 discoveredRoutes
FIFO 队列 (#11977)unstable_
API 中的此行为,这可能是一个破坏性变更patchRoutesOnNavigation
内部实现自己的缓存PatchRoutesOnNavigationFunction
的 patch
方法中 RouteObject
的类型,使其不期望传递给 patch
的是与平台无关的路由对象 (#11967)patchRoutesOnNavigation
抛出的错误直接暴露给 useRouteError
,而不是将它们包装在 400 ErrorResponse
实例中 (#12111)完整更新日志: v6.26.2...v6.27.0
日期:2024-09-09
unstable_dataStrategy
API 以允许更高级的实现 (#11943)unstable_dataStrategy
,请仔细审查,因为这包括对此 API 的破坏性变更unstable_HandlerResult
重命名为 unstable_DataStrategyResult
unstable_dataStrategy
的返回签名从一个与 matches
平行的 unstable_DataStrategyResult[]
数组更改为一个键/值对象 routeId => unstable_DataStrategyResult
match.shouldLoad
)handlerOverride
返回/抛出一个结果,而不是返回一个 DataStrategyResult
handlerOverride
的返回值(或抛出的错误)将被包装成一个 DataStrategyResult
并从 match.resolve
返回match.resolve()
的结果聚合到一个最终的结果对象中,您应该不需要考虑 DataStrategyResult
类型handlerOverride
内部手动填充结果对象,那么您需要分配一个 DataStrategyResult
作为值,以便 React Router 知道它是一个成功的执行还是一个错误(详见文档中的示例)unstable_dataStrategy
添加了一个新的 fetcherKey
参数,以允许区分导航调用和 fetcher 调用blocker.proceed
时的阻塞器用法 (#11930)完整更新日志: v6.26.1...v6.26.2
日期:2024-08-15
unstable_patchRoutesOnMiss
重命名为 unstable_patchRoutesOnNavigation
以匹配新行为 (#11888)unstable_patchRoutesOnNavigation
逻辑,以便在匹配到带有动态参数或 splat 段的路由时调用该方法,以防存在我们尚未发现的更高得分的静态路由 (#11883)unstable_patchRoutesOnNavigation
的路径,这样我们就不会在后续导航到相同路径时重复调用完整更新日志: v6.26.0...v6.26.1
日期:2024-08-01
replace(url, init?)
作为 redirect(url, init?)
的替代方案,它在客户端导航重定向上执行 history.replaceState
而不是 history.pushState
(#11811)unstable_data()
API (#11836)createStaticHandler.query()
,允许 loaders/actions 返回任意数据以及自定义的 status
/headers
,而无需强制将数据序列化为 Response
实例unstable_dataStrategy
进行更高级的序列化策略,例如在 Remix Single Fetch 中通过 turbo-stream
进行序列化HandlerResult
中移除 status
字段unstable_dataStrategy
返回特定的 status
,您应该改为通过 unstable_data()
来实现future.v7_partialHydration
和 unstable_patchRoutesOnMiss
时的初始水合行为 (#11838)router.state.matches
现在将包括任何部分匹配,以便我们可以渲染祖先的 HydrateFallback
组件完整更新日志: v6.25.1...v6.26.0
日期:2024-07-17
RouterProvider
内部构件以减少不必要的重新渲染 (#11803)完整更新日志: v6.25.0...v6.25.1
日期:2024-07-16
v7_skipActionErrorRevalidation
此版本将 future.unstable_skipActionErrorRevalidation
标志稳定为 future.v7_skipActionErrorRevalidation
,为即将到来的 React Router v7 版本做准备。
4xx/5xx
Response
的 action 默认将不会触发重新验证shouldRevalidate
的 unstable_actionStatus
参数稳定为 actionStatus
future.unstable_skipActionErrorRevalidation
稳定为 future.v7_skipActionErrorRevalidation
(#11769)useMatch
内的路径,以便匹配/参数反映解码后的参数 (#11789)unstable_patchRoutesOnMiss
抛出的错误的冒泡问题 (#11786)unstable_patchRoutesOnMiss
且在服务器上匹配到 splat 路由的 SSR 应用的水合问题 (#11790)完整更新日志: v6.24.1...v6.25.0
日期:2024-07-03
polyfill.io
的引用,因为该域名已被出售,并被确定为提供恶意软件 (#11741)
NavLinkRenderProps
类型,以便于为自定义 NavLink
回调进行类型定义 (#11553)future.v7_relativeSplatPath
时,正确解析作为无路径路由子路由的 splat 路由中的相对路径 (#11633)router.routes
身份/重排 (#11740)完整更新日志: v6.24.0...v6.24.1
日期:2024-06-24
我们非常激动地在 v6.24.0
中发布我们新的“惰性路由发现”API!有关一些背景信息,请查看原始的 RFC。简而言之,自从我们在 v6.4 中通过 <RouterProvider>
引入了数据 API 以来,我们一直对其中一个权衡感到有些失望,那就是缺乏一个引人注目的代码分割方案,这与我们在 <BrowserRouter>
/<Routes>
应用中所拥有的方案相呼应。我们在 v6.9.0
中通过 route.lazy
向改进这一方案迈出了一小步,但在 v6.24.0
中我们已经走完了剩下的路。
通过“战争迷雾”,您现在可以通过传递给 createBrowserRouter
(及其 memory/hash 对应物)的新的 unstable_patchRoutesOnMiss
选项来惰性加载路由树的部分。这为您提供了一种方法,可以在 React Router 无法匹配给定路径时介入,并在导航(或 fetcher 调用)期间向路由树中修补新的路由。
这是一个非常小的例子,但请参考文档获取更多信息和用例
const router = createBrowserRouter(
[
{
id: "root",
path: "/",
Component: RootComponent,
},
],
{
async unstable_patchRoutesOnMiss({ path, patch }) {
if (path === "/a") {
// Load the `a` route (`{ path: 'a', Component: A }`)
let route = await getARoute();
// Patch the `a` route in as a new child of the `root` route
patch("root", [route]);
}
},
},
);
fetcher.submit
类型 - 移除不正确的 navigate
/fetcherKey
/unstable_viewTransition
选项,因为它们仅与 useSubmit
相关 (#11631)<StaticRouter>
的 location.state
值为假值 (#11495)完整更新日志: v6.23.1...v6.24.0
日期:2024-05-10
undefined
通过 <Await>
解析 (#11513)document.startViewTransition
可用性时添加防御性的 document
检查 (#11544)react-router-dom/server
的导入改回 react-router-dom
而不是 index.ts
(#11514)@remix-run/router
- 在 staticHandler.queryRoute
上支持 unstable_dataStrategy
(#11515)完整更新日志: v6.23.0...v6.23.1
日期:2024-04-23
新的 unstable_dataStrategy
API 是一个低级 API,专为高级用例设计,在这些用例中,您需要控制 loader
/action
函数的数据策略。默认实现是当前的行为,即并行获取所有 loader,但此选项允许用户实现更高级的数据流,包括 Remix 的“单一获取”、用户空间中间件/上下文 API、自动 loader 缓存等。请参阅文档获取更多信息。
注意: 这是一个专为高级用例设计的低级 API。它会覆盖 React Router 内部对 loader
/action
执行的处理,如果操作不当,将会破坏您的应用代码。请谨慎使用并进行适当的测试。
目前,在任何 action
提交后,所有活动的 loader
都会重新验证,无论 action
的结果如何。然而,在大多数情况下,来自 action
的 4xx
/5xx
响应意味着没有数据实际更改,重新验证是不必要的。我们引入了一个新的 future.unstable_skipActionErrorRevalidation
标志来改变这里的行为,并计划在未来版本的 React Router 中将其设为默认行为。
启用此标志后,返回/抛出 4xx
/5xx
响应状态的 action
将不再自动重新验证。如果您需要在启用此标志的情况下在 4xx
/5xx
结果后重新验证,您仍然可以通过从 shouldRevalidate
返回 true
来实现——该函数现在还收到了一个新的 unstable_actionStatus
参数以及 actionResult
,这样您就可以根据 action
响应的状态做出决策,而无需将其编码到 action 数据中。
unstable_dataStrategy
配置选项 (#11098, #11377)@remix-run/router
- 添加一个新的 future.unstable_skipActionRevalidation
未来标志 (#11098)@remix-run/router
- SSR:向 staticHandler.query
方法添加了一个新的 skipLoaderErrorBubbling
选项,以禁用静态处理器对错误的冒泡,用于 Remix 的单一获取实现 (#11098, (#11377))完整更新日志: v6.22.3...v6.23.0
日期:2024-03-07
future.v7_partialHydration
的 bug,该 bug 会在 SSR 加载器错误冒泡到父边界时,在水合时重新运行边界下的加载器 (#11324)future.v7_partialHydration
的 bug,该 bug 会在路由没有加载器时认为路由器未初始化 (#11325)完整更新日志: v6.22.2...v6.22.3
日期:2024-02-28
完整更新日志: v6.22.1...v6.22.2
日期:2024-02-16
完整更新日志: v6.22.0...v6.22.1
日期:2024-02-01
2021年,HTTP Archive 推出了核心 Web 指标技术报告仪表板
通过结合 Chrome 用户体验报告 26 (CrUX) 数据集中的真实用户体验和 HTTP Archive 30 中的 Web 技术检测,我们可以窥见架构决策(如选择 CMS 平台或 JavaScript 框架)如何在网站的 CWV 性能中发挥作用。
他们使用一个名为 wappalyzer
的工具,通过查找特定的脚本、全局 JS 变量或其他识别特征来识别给定网站正在使用的技术。例如,对于 Remix 应用程序,他们查找全局 __remixContext
变量来识别网站正在使用 Remix。
我们注意到,React Router 无法被可靠地识别,因为没有识别性的全局特征。他们目前正在寻找名称中带有 react-router
的外部脚本。这将识别出使用 CDN(如 unpkg
)的 React Router 网站——但这将错过从 npm 注册表安装 React Router 并将其捆绑到 JS 文件中的**绝大多数**网站。这导致了对 React Router 在 Web 上使用情况的严重低估。
从 6.22.0
版本开始,使用 react-router-dom
的网站将开始添加一个 window.__reactRouterVersion
变量,该变量将被设置为 SemVer 主要版本号的字符串值(即 window.__reactRouterVersion = "6";
),以便它们可以被正确识别。
window.__reactRouterVersion
(#11222)createStaticHandler
的 future.v7_throwAbortReason
标志,以便在请求被中止时抛出 request.signal.reason
(默认为 DOMException
),而不是一个 Error
,例如 new Error("query() call aborted: GET /path")
(#11104)DOMException
是在 Node v17 中添加的,因此在 Node 16及以下版本中您不会得到 DOMException
。getStaticContextFormError
的是 ErrorResponse
,则尊重其状态码 (#11213)完整更新日志: v6.21.3...v6.22.0
日期:2024-01-18
basename
时 NavLink
的 isPending
问题 (#11195)Blocker
/BlockerFunction
类型中移除残留的 unstable_
前缀 (#11187)完整更新日志: v6.21.2...v6.21.3
日期:2024-01-11
完整更新日志: v6.21.1...v6.21.2
日期:2023-12-21
v7_partialHydration
时,route.lazy
在初始 SPA 加载时无法正常工作的 bug (#11121)submitting
阶段卸载的持久化 fetchers 无法触发重新验证的 bug (#11102)resolveTo
中去重相对路径逻辑 (#11097)完整更新日志: v6.21.0...v6.21.1
日期:2023-12-13
future.v7_relativeSplatPath
我们在 6.19.0
中修复了一个 splat 路由路径解析的 bug,但后来发现大量应用程序依赖于这个有 bug 的行为,所以我们在 6.20.1
中撤销了修复(参见 #10983, #11052, #11078)。
这个有 bug 的行为是,在解析 splat 路由内的相对路径时,默认行为会*忽略*当前路由路径的任何 splat (*
) 部分。当启用未来标志时,splat 部分会包含在 splat 路由内的相对路径逻辑中。
更多信息,请参阅 useResolvedPath
文档 和/或 详细的更新日志条目。
我们为 @remix-run/router
添加了一个新的 future.v7_partialHydration
未来标志,它在服务器端渲染时启用数据路由器的部分水合。这允许您提供 hydrationData.loaderData
,其中包含*一些*初始匹配的路由加载器的数据,但不是全部。当启用此标志时,路由器将在 router.initialize()
期间为没有水合加载器数据的路由调用 loader
函数,并且在执行未水合的路由时,它将渲染到最深层提供的 HydrateFallback
(直到第一个没有水合数据的路由)。 (#11033)
future.v7_relativeSplatPath
标志,以实现一个对 splat 路由内相对路由的破坏性 bug 修复。 (#11087)future.v7_partialHydration
未来标志,该标志在服务器端渲染时启用数据路由器的部分水合 (#11033)ErrorBoundary
中正确处理假值错误 (#11071)loader
/action
函数中解包响应时抛出的错误 (#11061)Link
/NavLink
时的 relative="path"
问题 (#11062)完整更新日志: v6.20.1...v6.21.0
日期:2023-12-01
useResolvedPath
修复(参见 #11052) (#11078)6.19.0
和 6.20.0
版本中。如果您从 6.18.0
或更早版本升级,您将不会受到此修复的影响。完整更新日志: v6.20.0...v6.20.1
日期:2023-11-22
[!WARNING] 请使用
6.20.1
或更高版本,而不是6.20.0
。我们发现大量应用依赖于此版本中修复的一个有 bug 的行为 (#11045)。我们在6.20.1
中撤销了该修复,并将在后续版本中通过一个未来标志重新引入。详情请见 #11052。
PathParam
类型 (#10719)v7_fetcherPersist
时,不要重新验证未挂载的 fetchers (#11044)resolveTo
路径解析的 bug (#11045)getPathContributingMatches
的其他几个代码路径@remix-run/router
中移除了 UNSAFE_getPathContributingMatches
导出,因为我们不再需要在 react-router
/react-router-dom
层中使用它完整更新日志: v6.19.0...v6.20.0
日期:2023-11-16
[!WARNING] 请使用
6.20.1
或更高版本,而不是6.19.0
。我们发现大量应用依赖于此版本中修复的一个有 bug 的行为 (#10983)。我们在6.20.1
中撤销了该修复,并将在后续版本中通过一个未来标志重新引入。详情请见 #11052。
unstable_flushSync
API此版本为命令式 API(useSubmit
, useNavigate
, fetcher.submit
, fetcher.load
)带来了新的 unstable_flushSync
选项,让用户可以选择为待处理/乐观 UI 启用同步 DOM 更新。
function handleClick() {
submit(data, { flushSync: true });
// Everything is flushed to the DOM so you can focus/scroll to your pending/optimistic UI
setFocusAndOrScrollToNewlyAddedThing();
}
useNavigate
/useSubmit
/fetcher.load
/fetcher.submit
添加 unstable_flushSync
选项,以选择不使用 React.startTransition
,而使用 ReactDOM.flushSync
进行状态更新 (#11005)useBlocker
钩子中移除 unstable_
前缀,因为它已经使用了足够长的时间,我们对该 API 很有信心 (#10991)window.confirm
的方式存在差异,导致 React Router 无法保证一致/正确的行为,我们不打算从 unstable_usePrompt
中移除该前缀修复 useActionData
,使其返回正确的上下文 action 数据,而不是树中的*任何* action 数据 (#11023)
修复 useResolvedPath
中的一个 bug,该 bug 会导致 splat 路由中的 useResolvedPath(".")
丢失 URL 路径的 splat 部分。 (#10983)
"."
路径,该路径错误地丢弃了 URL 的 splat 部分。如果您在应用程序的 splat 路由内通过 "."
进行相对路由,您应该仔细检查您的逻辑是否不依赖于这个有 bug 的行为,并相应地进行更新。修复了一个问题,即在保持挂载的 useFetcher
中更改 fetcher 的 key
没有被检测到 (#11009)
修复 useFormAction
,它错误地从子路由的 action
提交中继承了 ?index
查询参数 (#11025)
修复当 to
位置有尾部斜杠时 NavLink
的 active
逻辑 (#10734)
修复类型,使 unstable_usePrompt
除了 boolean
之外,还可以接受一个 BlockerFunction
(#10991)
修复 relative="path"
的 bug,其中相对路径计算从完整的位置路径名开始,而不是从当前的上下文路由路径名开始。 (#11006)
<Route path="/a">
<Route path="/b" element={<Component />}>
<Route path="/c" />
</Route>
</Route>;
function Component() {
return (
<>
{/* This is now correctly relative to /a/b, not /a/b/c */}
<Link to=".." relative="path" />
<Outlet />
</>
);
}
完整更新日志: 6.18.0...6.19.0
日期:2023-10-31
根据这个 RFC,我们引入了一些新的 API,让您能够更精细地控制您的 fetcher 行为。
useFetcher({ key: string })
指定您自己的 fetcher 标识符,这允许您从应用程序中的不同组件访问相同的 fetcher 实例,而无需通过 props 传递useFetchers
返回的 fetchers 上,以便可以通过 key
查找它们Form
和 useSubmit
现在支持可选的 navigate
/fetcherKey
props/params,以允许在后台启动一个 fetcher 提交,并带有可选的用户指定的 key
<Form method="post" navigate={false} fetcherKey="my-key">
submit(data, { method: "post", navigate: false, fetcherKey: "my-key" })
useFetchers()
或 useFetcher({ key })
在其他地方查找它future.v7_fetcherPersist
)根据与上述相同的 RFC,我们引入了一个新的 future.v7_fetcherPersist
标志,它允许您选择加入新的 fetcher 持久化/清理行为。fetchers 不再在卸载时立即被清理,而是会持续存在直到它们返回到 idle
状态。这使得在原始 fetcher 需要卸载的场景中,实现待定/乐观 UI 变得*非常*容易。
useFetchers()
API 本来应该只反映**进行中**的 fetcher 信息,用于待定/乐观 UI——它不是为了反映 fetcher 数据或在 fetcher 返回到 idle
状态后还保留它们useFetchers()
中 - 它们在那里没有任何作用,因为您可以通过 useFetcher().data
访问数据idle
状态后被清理useFetchers
暴露,因此您在卸载后仍然可以访问待定/乐观数据key
被重新挂载,那么它的结果将被处理,即使原始的 fetcher 已经被卸载key
API 和 navigate=false
选项 (#10960)future.v7_fetcherPersist
标志 (#10962)matchPath
中添加对可选路径段的支持 (#10768)BrowserRouter
、HashRouter
和 MemoryRouter
上的 future
属性,使其接受 Partial<FutureConfig>
,而不是要求包含所有标志 (#10962)router.getFetcher
/router.deleteFetcher
类型定义,它们错误地将 key
指定为可选参数 (#10960)完整更新日志: 6.17.0...6.18.0
日期:2023-10-16
我们很高兴在 React Router 中发布对 视图过渡 API 的实验性支持!您现在可以触发导航性 DOM 更新,将其包装在 document.startViewTransition
中,从而在您的应用程序的 SPA 导航中启用 CSS 动画过渡。
在您的 React Router 应用中启用视图过渡的最简单方法是通过新的 <Link unstable_viewTransition>
属性。这将导致导航 DOM 更新被包装在 document.startViewTransition
中,从而为 DOM 更新启用过渡。没有任何额外的 CSS 样式,您将为您的页面获得一个基本的交叉淡入淡出动画。
如果您需要为您的动画应用更精细的样式,您可以利用 unstable_useViewTransitionState
钩子,它会告诉您过渡何时正在进行中,您可以用它来应用类或样式
function ImageLink(to, src, alt) {
const isTransitioning = unstable_useViewTransitionState(to);
return (
<Link to={to} unstable_viewTransition>
<img
src={src}
alt={alt}
style={{
viewTransitionName: isTransitioning ? "image-expand" : "",
}}
/>
</Link>
);
}
您也可以使用 <NavLink unstable_viewTransition>
简写,它将为您管理钩子的使用,并在过渡期间自动向 <a>
添加一个 transitioning
类
a.transitioning img {
view-transition-name: "image-expand";
}
<NavLink to={to} unstable_viewTransition>
<img src={src} alt={alt} />
</NavLink>
有关视图过渡的用法示例,请查看我们对超棒的 Astro Records 演示的复刻。
有关使用视图过渡 API 的更多信息,请参阅 Google Chrome 团队的使用视图过渡 API 实现平滑简单的过渡指南。
sessionStorage
不可用时,在 ScrollRestoration
中记录警告并优雅地失败 (#10848)RouterProvider
的 future
属性类型为 Partial<FutureConfig>
,这样就不必指定所有标志 (#10900)ErrorResponse
类型以避免泄漏内部字段 (#10876)完整更新日志: 6.16.0...6.17.0
日期:2023-09-13
any
替换为 unknown
。为了在 Remix v2 中做到这一点而不引入 React Router v6 的破坏性变更,我们为一些共享类型添加了泛型。这些在 React Router 中继续默认为 any
,并在 Remix 中被 unknown
覆盖。在 React Router v7 中,我们计划将这些作为破坏性变更移动到 unknown
。 (#10843)Location
现在接受一个泛型用于 location.state
值ActionFunctionArgs
/ActionFunction
/LoaderFunctionArgs
/LoaderFunction
现在接受一个泛型用于 context
参数(仅在通过 createStaticHandler
的 SSR 用法中使用)useMatches
的返回类型(现在导出为 UIMatch
)接受 match.data
和 match.handle
的泛型 - 这两者都已经设置为 unknown
@private
类导出 ErrorResponse
移动到 UNSAFE_ErrorResponseImpl
导出,因为它是一个实现细节,用户不应构造 ErrorResponse
实例。这使我们可以导出一个 type ErrorResponse
,它通过 InstanceType
与类的实例相关联。用户代码只应将 ErrorResponse
用作类型,并应通过 isRouteErrorResponse
进行类型收窄。 (#10811)ShouldRevalidateFunctionArgs
接口 (#10797)_isFetchActionRedirect
, _hasFetcherDoneAnything
) (#10715)query
/queryRoute
调用的错误消息中添加方法/URL (#10793)route.lazy
路由上 loader/action 抛出错误的竞争条件问题 (#10778)shouldRevalidate
的参数对象中 actionResult
的类型 (#10779)完整更新日志: v6.15.0...v6.16.0
日期:2023-08-10
redirectDocument()
函数,它允许用户指定来自 loader
/action
的重定向应该触发文档重新加载(通过 window.location
),而不是尝试通过 React Router 导航到重定向的位置 (#10705)useRevalidator
在重新渲染之间保持引用稳定 (#10707)URLSearchParams
和 useSearchParams
钩子的 Web 扩展的一个边缘情况 (#10620)unstable_usePrompt
中的 effect 顺序,以避免在提示被解除阻塞且同步执行导航时抛出异常 (#10687, #10718)useFormAction()
中包含哈希值,因为它无法在服务器上确定,并会导致水合问题 (#10758)queryRoute
中一个不总能识别抛出的 Response
实例的问题 (#10717)react-router-native
:将 @ungap/url-search-params
依赖从 ^0.1.4
更新到 ^0.2.2
(#10590)完整更新日志:v6.14.2...v6.15.0
日期:2023-07-17
<Form>
添加缺失的 state
属性,以便在提交导航时填充 history.state
(#10630)defer
的 promise 解析/拒绝为 undefined
,则触发错误,以匹配 loader 和 action 必须返回值或 null
的行为 (#10690)<ScrollRestoration>
模拟哈希滚动时,正确解码元素 ID (#10682)Route.lazy
的返回类型,禁止返回空对象 (#10634)Error
的子类(如 ReferenceError
/TypeError
)进行正确的水合 (#10633)完整更新日志:v6.14.1...v6.14.2
日期:2023-06-30
unstable_useBlocker
与不稳定的阻塞函数一起使用时出现的循环问题 (#10652)@remix-run/router@1.7.1
完整更新日志:v6.14.0...v6.14.1
日期:2023-06-23
6.14.0
版本通过 useSubmit
/fetcher.submit
增加了对 JSON 和 Text 提交的支持,因为如果您在客户端 SPA 中工作,不总是方便地需要序列化为 FormData
。要选择使用这些编码,您只需指定正确的 formEncType
选择使用 application/json
编码
function Component() {
let navigation = useNavigation();
let submit = useSubmit();
submit({ key: "value" }, { method: "post", encType: "application/json" });
// navigation.formEncType => "application/json"
// navigation.json => { key: "value" }
}
async function action({ request }) {
// request.headers.get("Content-Type") => "application/json"
// await request.json() => { key: "value" }
}
选择使用 text/plain
编码
function Component() {
let navigation = useNavigation();
let submit = useSubmit();
submit("Text submission", { method: "post", encType: "text/plain" });
// navigation.formEncType => "text/plain"
// navigation.text => "Text submission"
}
async function action({ request }) {
// request.headers.get("Content-Type") => "text/plain"
// await request.text() => "Text submission"
}
⚠️ v7 版本中默认行为将发生变化
请注意,为避免重大变更,默认行为仍会将简单的键/值 JSON 对象编码为 FormData
实例。
function Component() {
let navigation = useNavigation();
let submit = useSubmit();
submit({ key: "value" }, { method: "post" });
// navigation.formEncType => "application/x-www-form-urlencoded"
// navigation.formData => FormData instance
}
async function action({ request }) {
// request.headers.get("Content-Type") => "application/x-www-form-urlencoded"
// await request.formData() => FormData instance
}
此行为很可能会在 v7 中改变,因此最好通过 formEncType: "application/x-www-form-urlencoded"
或 formEncType: "application/json"
明确指定任何 JSON 对象提交,以便您最终顺利迁移到 v7。
useSubmit
/fetcher.submit
添加对 application/json
和 text/plain
编码的支持。为了反映这些额外的类型,useNavigation
/useFetcher
现在也包含 navigation.json
/navigation.text
和 fetcher.json
/fetcher.text
,其中包含适用的 json/text 提交数据。 (#10413)submitter
参数的浏览器)中,当从 submitter
元素提交表单时,优先使用内置的 new FormData(form, submitter)
,而不是之前的手动方法 (#9865)type="image"
按钮添加了基本支持。formdata-submitter-polyfill
。window.history.pushState/replaceState
(而不是之后),以便在同步的 React 17 渲染期间 window.location
与 useLocation
匹配 (#10448)window.location
,而应尽可能引用 useLocation
,因为 window.location
不会 100% 同步(由于 popstate
事件、并发模式等)。shouldRevalidate
(#10623)<ScrollRestoration getKey>
的 location
中剥离 basename
,以匹配 useLocation
的行为 (#10550)unstable_useBlocker
函数的 location 中剥离 basename
,以匹配 useLocation
的行为 (#10573)StrictMode
中 unstable_useBlocker
的 key 问题 (#10573)0
作为参数时 generatePath
的问题 (#10612)tsc --skipLibCheck:false
的问题 (#10622)typescript
升级到 5.1 (#10581)完整更新日志:v6.13.0...v6.14.0
日期:2023-06-14
6.13.0
本质上是一个补丁发布,但由于我们添加了一个新的未来标志,因此它带有一个 SemVer 的次要版本号提升。
future.v7_startTransition
长话短说,6.13.0
与 6.12.0
相同,但我们将 React.startTransition
的使用移到了一个可选的 future.v7_startTransition
未来标志 之后,因为我们发现现有的一些应用程序使用的 Suspense
方式与 React.startTransition
不兼容。
因此,在 6.13.0
中,默认行为将不再利用 React.startTransition
。
<BrowserRouter>
<Routes>{/*...*/}</Routes>
</BrowserRouter>
<RouterProvider router={router} />
如果您希望启用 React.startTransition
,请将未来标志传递给您的路由器组件。
<BrowserRouter future={{ v7_startTransition: true }}>
<Routes>{/*...*/}</Routes>
</BrowserRouter>
<RouterProvider router={router} future={{ v7_startTransition: true }}/>
我们建议大家尽早采用此标志,以便更好地与 React 并发模式兼容,但如果您遇到问题,可以继续不使用 React.startTransition
直到 v7。问题通常归结为在渲染周期中创建全新的 promise,因此如果您在选择使用 React.startTransition
时遇到问题,您应该将 promise 的创建移出渲染周期或将其置于 useMemo
之后。
React.startTransition
的使用移至一个未来标志之后 (#10596)React.startTransition
的压缩错误 (#10588)完整更新日志:v6.12.1...v6.13.0
日期:2023-06-08
[!WARNING] 请使用
6.13.0
或更高版本,而不是6.12.0
/6.12.1
。这些版本存在一些 Webpack 构建/压缩问题,导致构建失败或在您的生产包中出现无效的压缩代码。更多详情请参见 #10569 和 #10579。
React.startTransition
的特性检测,以修复 webpack + react 17 的编译错误 (#10569)完整更新日志:v6.12.0...v6.12.1
日期:2023-06-06
[!WARNING] 请使用
6.13.0
或更高版本,而不是6.12.0
/6.12.1
。这些版本存在一些 Webpack 构建/压缩问题,导致构建失败或在您的生产包中出现无效的压缩代码。更多详情请参见 #10569 和 #10579。
React.startTransition
支持在 6.12.0
版本中,我们通过将内部路由器状态更新包装在 React.startTransition
中,为挂起组件提供了更好的支持。这意味着,例如,如果目标路由中的某个组件挂起,并且您没有提供 Suspense
边界来显示回退 UI,React 将延迟渲染新的 UI,并显示旧的 UI,直到异步操作解决。这对于等待图片或 CSS 文件加载等情况非常有用(技术上讲,您也可以用它来加载数据,但我们仍然推荐使用 loader 😀)。要快速了解此用法,请查看 Ryan 在 Twitter 上的演示。
React.startTransition
包装内部路由器状态更新 (#10438)PUSH
导航时,重新抛出 DOMException
(DataCloneError
)。(#10427)jest
和 jsdom
(#10453)@remix-run/router@1.6.3
(更新日志)完整更新日志:v6.11.2...v6.12.0
日期:2023-05-17
<RouterProvider>
内的后代 <Routes>
中 basename
重复的问题 (#10492)SetURLSearchParams
类型 (#10444)_internalSetRoutes
中正确重构新路由和 manifest
,修复 Remix HMR 驱动的错误边界问题 (#10437)完整更新日志:v6.11.1...v6.11.2
日期:2023-05-03
<Routes>
中使用 Component
API 的问题 (#10434)<RouterProvider>
内的 <Routes>
中调用 useNavigate
的错误 (#10432)<Navigate>
的问题 (#10435)basename
的处理问题 (#10433)/path#hash -> /path#hash
)(#10408)完整更新日志:v6.11.0...v6.11.1
日期:2023-04-28
useFetcher
中启用 basename
支持 (#10336)basename
来解决此问题,那么您需要从您的 fetcher
调用中移除手动前置的 basename
(例如 fetcher.load('/basename/route') -> fetcher.load('/route')
)@remix-run/router@1.6.0
(更新日志)RouterProvider
时,useNavigate
/useSubmit
/fetcher.submit
在 location 变化时现在是稳定的,因为我们可以通过 @remix-run/router
实例处理相对路由,从而摆脱了对 useLocation()
的依赖 (#10336)BrowserRouter
时,这些钩子在 location 变化时仍然不稳定,因为它们仍然依赖 useLocation()
action
提交或 router.revalidate
调用时重新验证 (#10344)Component
而非 element
时导致的意外重新渲染 (#10287)<Link to="//">
和其他无效 URL 值进行优雅地失败处理 (#10367)<RouterProvider>
中,将内部 @remix-run/router
路由器状态同步从 useSyncExternalStore
切换到 useState
。我们发现了一些 细微的 bug,其中路由器状态更新的传播*先于*其他正常的 useState
更新,这可能在 useEffect
调用中导致陷阱。(#10377, #10409)RouterProvider
存在错误时,阻止后代 <Routes>
渲染的 bug (#10374)activeRef
来修复渲染周期中 useNavigate
的检测,允许将 navigate
函数传递给子组件并在其 useEffect
中调用 (#10394)useRevalidator()
解决 loader 驱动的错误边界场景 (#10369)LoaderFunction
/ActionFunction
的返回类型,以防止 undefined
成为有效的返回值 (#10267)loader
的路由进行 fetcher.load
调用时,返回正确的 404 错误 (#10345)AbortController
使用,这样重新验证的 fetcher 的卸载/删除不会影响正在进行的触发导航/重新验证 (#10271)完整更新日志:v6.10.0...v6.11.0
日期:2023-03-29
我们最近在 Remix 博客上发表了一篇题为“为您的 Remix 应用提供未来保障”的文章,其中介绍了我们确保您的 Remix 和 React Router 应用未来能平滑升级的策略。React Router 6.10.0
为数据路由器添加了对这些标志的支持,您可以在创建路由器时指定它们。
const router = createBrowserRouter(routes, {
future: {
// specify future flags here
},
});
future.v7_normalizeFormMethod
引入的第一个未来标志是 future.v7_normalizeFormMethod
,它将把暴露的 useNavigation()/useFetcher()
formMethod
字段规范化为大写的 HTTP 方法,以与 fetch()
(以及一些 Remix)的行为保持一致。(#10207)
future.v7_normalizeFormMethod
未指定或设置为 false
(v6 默认行为)时,useNavigation().formMethod
是小写useFetcher().formMethod
是小写future.v7_normalizeFormMethod === true
时useNavigation().formMethod
是大写useFetcher().formMethod
是大写createStaticHandler
,使其除了检查 errorElement
外,还检查路由上的 ErrorBoundary
(#10190)createRoutesFromElements
中使用 Fragments 时的路由 ID 生成问题 (#10193)shouldRevalidate
提供 fetcher 提交信息 (#10208)lazy()
错误 (#10201)DeferredData
的 instanceof
检查,以适应 SSR 打包场景中的 ESM/CJS 边界问题 (#10247)@remix-run/web-fetch@4.3.3
(#10216)完整更新日志:v6.9.0...v6.10.0
日期:2023-03-10
Component
/ErrorBoundary
路由属性React Router 现在支持一种替代方式来定义路由的 element
和 errorElement
字段,即使用 React 组件而非 React 元素。您可以选择将 React 组件传递给新的 Component
和 ErrorBoundary
字段。两者在功能上没有区别,所以请使用您喜欢的方式 😀。您不应该同时定义两者,但如果这样做了,Component
/ErrorBoundary
将会“获胜”。
JSON 语法示例
// Both of these work the same:
const elementRoutes = [{
path: '/',
element: <Home />,
errorElement: <HomeError />,
}]
const componentRoutes = [{
path: '/',
Component: Home,
ErrorBoundary: HomeError,
}]
function Home() { ... }
function HomeError() { ... }
JSX 语法示例
// Both of these work the same:
const elementRoutes = createRoutesFromElements(
<Route path='/' element={<Home />} errorElement={<HomeError /> } />
);
const componentRoutes = createRoutesFromElements(
<Route path='/' Component={Home} ErrorBoundary={HomeError} />
);
function Home() { ... }
function HomeError() { ... }
为了保持您的应用程序包足够小并支持路由的代码分割,我们引入了一个新的 lazy()
路由属性。这是一个异步函数,它会解析您路由定义中非路由匹配的部分(loader
, action
, element
/Component
, errorElement
/ErrorBoundary
, shouldRevalidate
, handle
)。
懒加载路由在初始加载时以及导航或 fetcher 调用的 loading
或 submitting
阶段被解析。您不能懒加载定义路由匹配的属性(path
, index
, children
),因为我们只在匹配到已知路由后才执行您的懒加载路由函数。
您的 lazy
函数通常会返回动态导入的结果。
// In this example, we assume most folks land on the homepage so we include that
// in our critical-path bundle, but then we lazily load modules for /a and /b so
// they don't load until the user navigates to those routes
let routes = createRoutesFromElements(
<Route path="/" element={<Layout />}>
<Route index element={<Home />} />
<Route path="a" lazy={() => import("./a")} />
<Route path="b" lazy={() => import("./b")} />
</Route>,
);
然后在您的懒加载路由模块中,导出您想为该路由定义的属性。
export async function loader({ request }) {
let data = await fetchData(request);
return json(data);
}
// Export a `Component` directly instead of needing to create a React Element from it
export function Component() {
let data = useLoaderData();
return (
<>
<h1>You made it!</h1>
<p>{data}</p>
</>
);
}
// Export an `ErrorBoundary` directly instead of needing to create a React Element from it
export function ErrorBoundary() {
let error = useRouteError();
return isRouteErrorResponse(error) ? (
<h1>
{error.status} {error.statusText}
</h1>
) : (
<h1>{error.message || error}</h1>
);
}
此功能的一个实际例子可以在仓库的 examples/lazy-loading-router-provider
目录中找到。更多信息,请查看 lazy
文档。
🙌 非常感谢 @rossipedia 的初步提议和POC 实现。
generatePath
在某些情况下错误应用参数的问题 (#10078)[react-router-dom-v5-compat]
添加遗漏的数据路由器 API 重导出 (#10171)完整更新日志:v6.8.2...v6.9.0
日期:2023-02-27
<Link to>
中的同源绝对 URL 超出了路由器 basename
的范围,则将其视为外部链接 (#10135)basename
范围的同源绝对 URL,正确执行硬重定向 (#10076)<Link to>
URL 的 SSR 问题 (#10112)StaticRouterProvider
的序列化水合数据中正确转义 HTML 字符 (#10068)useBlocker
在 SSR 期间返回 IDLE_BLOCKER
的问题 (#10046)createStaticHandler
的 query()
方法中,确保为 defer
loader 响应保留状态码和头信息 (#10077)invariant
更改为 UNSAFE_invariant
导出,因为它仅供内部使用 (#10066)完整更新日志:v6.8.1...v6.8.2
日期:2023-02-06
Link
组件中的绝对 URL 检测(现在也支持 mailto:
URL)(#9994)完整更新日志:v6.8.0...v6.8.1
日期:2023-01-26
支持在 <Link to>
中使用绝对 URL。如果 URL 是当前源,它仍会进行客户端导航。如果 URL 是不同的源,它将为新源发起一个新的文档请求。(#9900)
<Link to="https://neworigin.com/some/path"> {/* Document request */}
<Link to="//neworigin.com/some/path"> {/* Document request */}
<Link to="https://www.currentorigin.com/path"> {/* Client-side navigation */}
shouldRevalidate
调用的 2 个独立问题 (#9948)shouldRevalidate
函数仅在*显式*重新验证场景中被调用(例如在 mutation 之后、手动调用 useRevalidator
或在 Remix 中用于设置 cookie 的 X-Remix-Revalidate
头)。它没有在也适用于导航 loader
重新验证的*隐式*重新验证场景中被正确调用,例如搜索参数的更改或点击当前页面的链接。现在它在这些额外场景中也能被正确调用。current*
/next*
参数反映了静态的 fetcher.load
URL(因此是相同的)。相反,它们应该反映触发重新验证的导航(就像 form*
参数那样)。这些参数现在能正确反映触发的导航。useSearchParams
移除搜索参数的 bug (#9969)<fetcher.Form>
上遵守 preventScrollReset
(#9963)<ScrollRestoration>
使用 pagehide
而不是 beforeunload
。这有更好的跨浏览器支持,特别是在移动版 Safari 上。(#9945)isRouteErrorResponse
中移除 instanceof
检查,以避免服务器上的打包问题 (#9930)defer
调用仅包含关键数据时,移除 AbortController
(#9965)File
类型的 FormData
条目进行 URL 编码时,将名称作为值发送 (#9867)react-router-dom-v5-compat
- 修复使用 CompatRouter
时 SSR useLayoutEffect
的 console.error
(#9820)完整更新日志:v6.7.0...v6.8.0
日期:2023-01-18
unstable_useBlocker
/unstable_usePrompt
钩子,用于在应用程序的 location 源内阻塞导航 (#9709, #9932)<Form>
添加 preventScrollReset
属性 (#9886)useBeforeUnload
添加了透传的事件监听器选项参数 (#9709)generatePath
的问题 (#9764)<Await>
以接受 ReactNode
作为子函数返回结果 (#9896)jsdom
错误绕过方案 (#9824)完整更新日志:v6.6.2...v6.7.0
日期:2023-01-09
useId
的一致性 (#9805)完整更新日志:v6.6.1...v6.6.2
日期:2022-12-23
完整更新日志:v6.6.0...v6.6.1
日期:2022-12-21
这个次要版本主要是为了稳定我们的数据路由器 SSR API,因为我们已经将新的 RouterProvider
连接到了 Remix 中,这是 React Router-ing Remix 工作的一部分。
createStaticHandler
/createStaticRouter
/StaticRouterProvider
的 unstable_
前缀 (#9738)useBeforeUnload()
钩子 (#9664)<Form method>
和 useSubmit
的 method 值 (#9664)<button formmethod>
表单提交的覆盖问题 (#9664)replace
和提交到新路径时的 PUSH
(#9734)errorElement
中使用 useLoaderData
(#9735)StaticRouterProvider
中正确水合 Error
对象 (#9664)hydrationData
的 SSR 应用,跳过初始滚动恢复 (#9664)完整更新日志:v6.5.0...v6.6.0
日期:2022-12-16
此版本引入了对可选路由段的支持。现在,在任何路径段的末尾添加一个 ?
将使整个段变为可选的。这对静态段和动态参数都有效。
可选参数示例
<Route path=":lang?/about">
将匹配/:lang/about
/about
<Route path="/multistep/:widget1?/widget2?/widget3?">
将匹配/multistep
/multistep/:widget1
/multistep/:widget1/:widget2
/multistep/:widget1/:widget2/:widget3
可选静态段示例
<Route path="/home?">
将匹配/
/home
<Route path="/fr?/about">
将匹配/about
/fr/about
<Route path="prefix-:param">
,以与 splat 参数的工作方式保持一致。如果您之前依赖此行为,建议在 useParams
调用处提取路径的静态部分:(#9506)// Old behavior at URL /prefix-123
<Route path="prefix-:id" element={<Comp /> }>
function Comp() {
let params = useParams(); // { id: '123' }
let id = params.id; // "123"
...
}
// New behavior at URL /prefix-123
<Route path=":id" element={<Comp /> }>
function Comp() {
let params = useParams(); // { id: 'prefix-123' }
let id = params.id.replace(/^prefix-/, ''); // "123"
...
}
action
请求后,在 loader
request
上保留 headers
(#9721)完整更新日志:v6.4.5...v6.5.0
日期:2022-12-07
GET
请求 (#9680)isResponse
替代 instanceof Response
检查 (#9690)URL
的问题 (#9682, #9689)query
/queryRoute
添加 requestContext
支持 (#9696)queryRoute(path, routeId)
已更改为 queryRoute(path, { routeId, requestContext })
完整更新日志:v6.4.4...v6.4.5
日期:2022-11-30
action
/loader
函数返回 undefined
,则抛出错误,因为重新验证需要知道 loader 是否已执行过。undefined
在 SSR 序列化以进行水合时也会导致问题。您应始终确保您的 loader
/action
返回一个值,如果您不想返回任何内容,可以返回 null
。(#9511)basename
(#9591)ErrorResponse
的主体,以在内部 403/404/405 场景中包含更具描述性的文本NavLink
和后代 <Routes>
中编码字符的问题 (#9589, #9647)ErrorResponse
实例 (#9593)basename
(#9591)@remix-run/router@1.0.4
react-router@6.4.4
完整更新日志:v6.4.3...v6.4.4
日期:2022-11-01
createHashRouter
时生成正确的 <a href>
值 (#9409)index
路由同时也有一个 path
时,生成正确的 formAction
路径名 (#9486)NavLink
上的 relative=path
属性 (#9453)NavLink
对根 URL 的行为 (#9497)locationArg
时,useRoutes
应该能够返回 null
(#9485)createMemoryRouter
中的 initialEntries
类型 (#9498)loader
/action
重定向中支持 basename
和相对路由 (#9447)action
函数时,忽略无路径的布局路由 (#9455)@remix-run/router
添加 UMD 构建 (#9446)createURL
(#9464)完整更新日志:v6.4.2...v6.4.3
日期:2022-10-06
useFormAction
中尊重 basename
(#9352)IndexRouteObject
和 NonIndexRouteObject
类型,使 hasErrorElement
成为可选 (#9394)RouteObject
/RouteProps
类型,以便在 TypeScript 中显示此错误。(#9366)完整更新日志:v6.4.1...v6.4.2
日期:2022-09-22
完整更新日志:v6.4.0...v6.4.1
日期:2022-09-13
哇,这是一个大更新!6.4.0
将 Remix 的所有数据加载和变更 API 都引入了进来。这里有一个简单的高层概述,但建议您去查看文档,特别是功能概述和教程。
新的 react-router
API
createMemoryRouter
创建您的路由器<RouterProvider>
渲染您的路由器loader
加载数据,使用路由 action
进行变更errorElement
处理错误defer
和 Await
延迟非关键数据的加载新的 react-router-dom
API
createBrowserRouter
/createHashRouter
创建您的路由器<Form>
组件提交数据useFetcher()
执行页面内数据加载和变更defer
和 Await
延迟非关键数据的加载<ScrollRestoration>
管理滚动位置<Link relative="path">
执行路径相对导航 (#9160)<Routes location>
组件内,useLocation
返回作用域内的 location (#9094)<Link replace>
属性,则尊重它 (#8779)完整更新日志:v6.3.0...v6.4.0
日期:2022-03-31
完整更新日志:v6.2.2...v6.3.0
日期:2022-02-28
完整更新日志:v6.2.1...v6.2.2
日期:2021-12-17
history
依赖更新到 5.2.0
。完整更新日志:v6.2.0...v6.2.1
日期:2021-12-17
完整更新日志:v6.1.1...v6.2.0
日期:2021-12-11
HistoryRouter
标记为 unstable_HistoryRouter
,因为这个 API 在新的主版本发布前可能需要更改。完整更新日志:v6.1.0...v6.1.1
日期:2021-12-10
<Outlet>
现在可以接收一个 context
属性。这个值会传递给子路由,并可以通过新的 useOutletContext
钩子访问。详情请见API 文档。(#8461)<NavLink>
现在可以接收一个子函数来访问其 props。(#8164)useMatch
和 matchPath
的 TypeScript 签名。例如,当你调用 useMatch("foo/:bar/:baz")
时,路径会被解析,返回类型将是 PathMatch<"bar" | "baz">
。(#8030)完整更新日志:v6.0.2...v6.1.0
日期:2021-11-09
<Link>
添加了 reloadDocument
属性。这允许 <Link>
像普通锚标签一样,在导航后重新加载文档,同时保持相对的 to
解析 (#8283)完整更新日志:v6.0.1...v6.0.2
日期:2021-11-05
完整更新日志:v6.0.0...v6.0.1
日期:2021-11-03
React Router v6 来啦!
请阅读我们的博文,了解更多关于 v6 中所有精彩内容的信息,包括关于如何从 React Router v5 和 Reach Router 升级的说明。