react-router
)@react-router/*
)此页面列出了 React Router 自 v6.0.0
以来的所有发布版本/发布说明。对于 v6 之前的版本,请参阅 Github 发布页面。
我们在此文件中管理发布说明,而不是在分页的 Github 发布页面上,原因有二:
日期:2025-08-07
loaderData
值你是否曾注意到框架提供给你的加载器数据值存在差异?比如,在你的组件 props 中我们称之为 loaderData
,但在你的匹配项中却是 match.data
?是的,我们也注意到了——还有一些眼尖的 React Router 用户在一份提案中提出了这个问题。我们已经在一些遗留的地方,在现有的 data
字段旁边添加了新的 loaderData
字段,以便与新的 Route.*
API 中使用的 loaderData
命名保持一致。
7.8.0
中最大的一组变更是针对 unstable_middleware
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
重新验证期间导航时出现“未找到相应的 fetcher 结果”的控制台错误 (#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
抛出错误触发了边界
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
- 即使没有加载器存在,也在客户端导航上运行客户端中间件 (#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)
next
调用包装在 try
/catch
中,您应该可以移除它们。react-router
- 将 next
之前的客户端中间件错误冒泡到适当的祖先错误边界 (#14138)
react-router
- 当启用中间件时,使 context
参数为只读 (Readonly<unstable_RouterContextProvider>
),这样 TypeScript 将不允许您在加载器、操作或中间件中向其写入任意字段。(#14097)
react-router
- 在 staticHandler.query
/staticHandler.queryRoute
中重命名并更改 unstable_respond
API 的签名/功能 (#14103)
这仅影响在非框架模式 SSR 期间使用 createStaticHandler()
进行手动数据加载的用户
为了更清晰,该 API 已重命名为 unstable_generateMiddlewareResponse
主要的功能变化是,我们现在不再在调用 unstable_respond
之前运行加载器/操作并交给你结果,而是将一个 query
/queryRoute
函数作为参数传递,你可以在你的回调中执行加载器/操作,从而获得对预处理和错误处理的完全控制。
query
版本的 API 现在签名为 (query: (r: Request) => Promise<StaticHandlerContext | Response>) => Promise<Response>
queryRoute
版本的 API 现在签名为 (queryRoute: (r: Request) => Promise<Response>) => Promise<Response>
这允许更高级的用法,例如在调用 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
文档。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
- 更新到 Prettier v3,用于在运行 react-router reveal --no-typescript
时进行格式化 (#14049)⚠️ 不稳定功能不建议在生产环境中使用
react-router
- RSC 数据模式:修复当 shouldRevalidate
返回 false
时,带有错误的路由不会被强制重新验证的错误 (#14026)react-router
- RSC 数据模式:修复在渲染错误边界时出现的“在位置 "/..." 匹配的叶子路由没有 element 或 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)
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 向后兼容。
这使得 `createRoutesStub` 在您选择使用中间件和更新的 `context` 类型时可以工作,但对于尚未选择使用中间件的用户则会破坏 `createRoutesStub`。
我们已经撤销了此更改,并以一种方式重新实现它,使两组用户都可以利用它。
⚠️ 如果您已采用不稳定的中间件功能并使用更新后的 API 的 createRoutesStub
,这可能是一个破坏性的错误。
// 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
- 改进在构建期间找不到 chunk 时的 chunk 错误日志记录 (#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
chunk (#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
此外,您可以从组件 props 传递 `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` 替换 splat `*` (#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;
以前,类型生成会任意选择其中一个路径作为“胜出者”,并基于该路径为路由模块生成类型。
现在,typegen 会在必要时为同一路由文件的备用路径创建联合类型
@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
的 params 类型将是 { 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
版本中增加了一个新的配置选项,让你能更好地控制路由懒发现(Lazy Route Discovery)功能。如果你在同一台服务器上运行多个 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
中增加了对路由组件 props 的支持 (#13528)
这允许你使用 props 而不是 hooks 来对路由组件进行单元测试
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
时出现的 hydration 错误 (#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
- 修复了在 .data
请求中,当路由的最后一个 URL 段是动态参数时,handleError
的 params
值不正确的问题 (#13481)react-router
- 在路由懒发现中检测到清单版本不匹配时,不要在重新加载前触发 ErrorBoundary
UI (#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
,则支持没有 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
- 修复了中间件用例中错误冒泡的几个 bug (#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
- 修复了一个 bug,即冒泡的 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
请求的重定向,就像在实现单次获取之前的 Remix v2 中一样.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 时,如果在 hydration 后懒加载路由,HydrateFallback
和 hydrateFallbackElement
属性现在会被跳过 (#13376)
如果你将这些属性的代码移动到单独的文件中,由于 hydrate 属性已经未使用(如果路由在 hydration 期间不存在),你就可以完全避免下载它们。例如
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
- 修复了单次获取的 bug,即向上导航到重用的父路由时不会发出重新验证请求 (#13253)
react-router
- 在使用 ssr:false
+ prerender
配置时,当参数值改变时,正确地重新验证预渲染的路径 (#13380)
react-router
- 修复了当 loader 返回重定向时预渲染的问题 (#13365)
react-router
- 如果路由没有 loader,不要自动将 null
添加到 staticHandler.query()
的 context.loaderData
中 (#13223)
undefined
,我们之前对 loaderData[routeId] !== undefined
的检查已不再足够,并已更改为 routeId in loaderData
检查——这些 null
值可能会对这个新检查造成问题createStaticHandler()
/<StaticRouterProvider>
进行手动 SSR,并使用 context.loaderData
来控制客户端的 <RouterProvider>
hydration 行为,这可能是一个“破坏性 bug 修复”⚠️ 不稳定功能不建议在生产环境中使用
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
peer 依赖范围以支持 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
路由导出拆分到单独的 chunk 中 (#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"
时,允许根路由导出可拆分和不可拆分的模块,因为它总是在一个 chunk 中 (#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
的品牌属性被标记为必需(required)而非可选(optional)。
对于使用 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
文件将只渲染到根路由,并能够为任何有效的应用程序路径进行水合(hydrate)。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
应用禁用惰性路由发现(Lazy Route Discovery),而不仅仅是“SPA 模式”,因为没有运行时服务器来处理由搜索参数配置的 __manifest
请求 (#12894)。ssr:false
应用。prerender
场景中,我们会预渲染 /__manifest
文件,但这会对静态文件服务器的行为做出一些不必要的假设。react-router
- 在 SPA 模式下不应用单一获取(Single Fetch)重新验证的去优化,因为没有服务器 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
配置时,只对根 HydrateFallback
进行 SSR(SPA 模式)。prerender
配置但当前路径未被预渲染时,只对根 HydrateFallback
进行 SSR(SPA 后备)。.data
请求返回 404。react-router
- 提高框架模式下 CSS 副作用的预取性能 (#12889)。react-router
- 在惰性路由发现中正确处理被中断的 manifest 请求 (#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
- 修复重复参数的类型生成(typegen)(#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 调用,而默认的组件导出则要大得多。这对性能来说是个问题,因为它意味着如果我们要客户端导航到此路由,必须在客户端 loader 开始运行之前下载整个路由模块。
将其可视化为时间线
Get Route Module: |--=======|
Run clientLoader: |-----|
Render: |-|
相反,我们希望将其优化为以下形式
Get clientLoader: |--|
Get Component: |=======|
Run clientLoader: |-----|
Render: |-|
为实现此优化,React Router 将在生产构建过程中将路由模块拆分为多个较小的模块。在这种情况下,我们将得到两个独立的虚拟模块 — 一个用于客户端 loader,另一个用于组件及其依赖项。
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
在库模式下实现它,并将您的路由编写在多个文件中,如我们关于惰性加载路由模块的博客文章中所述。
现在这些作为单独的模块可用,客户端 loader 和组件可以并行下载。这意味着客户端 loader 可以在准备好后立即执行,而不必等待组件。
当使用更多路由模块 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} />;
}
此路由仍然可以工作,但由于客户端 loader 和组件都依赖于在同一文件中定义的 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
future 标志设置为 "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
future 标志时,正确解析 Windows 文件路径以扫描 Vite 的依赖项优化 (#12637)。@react-router/dev
- 修复使用自定义服务器时的预渲染问题 - 之前我们尝试导入用户的自定义服务器,而实际上我们想导入的是虚拟服务器构建模块 (#12759)。react-router
- 在单一获取响应中正确处理不能有主体的状态码(204 等)(#12760)。react-router
- 当抛出 data()
结果时,正确地将头信息作为 errorHeaders
冒泡 (#12846)。Set-Cookie
头信息重复,如果它们也从 headers
中返回的话。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.
symbol
不再用于 redirect
响应类型,这些错误应该不会再出现了。@react-router/dev
- 修复 Vite v6 中的默认外部条件 (#12644)。@react-router/dev
- 修复当路径缺少前导斜杠时预渲染 html/data 文件不匹配的问题 (#12684)。@react-router/dev
- 在运行时启用时使用 module-sync
服务器条件。这修复了在使用对 React Router 有 peer 依赖的库时,Node 22.10.0+ 开发过程中的 React 上下文不匹配问题(例如 useHref() may be used only in the context of a <Router> component.
)(#12729)。@react-router/dev
- 修复 react-refresh
的 source maps (#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
- 暂时只在导出映射中使用一个构建,以便包可以对 react router 有 peer 依赖 (#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 开发服务器重启时清理类型生成(typegen)文件监视器 (#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
在 v7 中仍作为 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 开发策略,利用“Future Flags”来避免在主要版本中引入大量重大变更。相反,重大变更在次要版本中通过标志引入,允许用户在方便时选择加入。在下一个主要版本中,所有 future flag 行为都成为默认行为。
以下先前通过标志启用的行为现在是 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
对于迁移到 React Router 的 Remix 用户,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
选项
对于迁移到 React Router 的 Remix 用户,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 的 promises。
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/<不带扩展名的路由文件名>
导入来访问这些类型。
更多详情,请参阅 操作指南 > 路由模块类型安全 和 原理解析 > 类型安全。
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
实现,转而通过单次 fetch 和 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
future 标志(#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
future 标志(#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)<RouterProvider>
中盲目地 import ReactDOM from "react-dom"
来访问 ReactDOM.flushSync()
,因为这会破坏在非 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
函数已更新,以使用 Node 的 require('node:crypto').webcrypto
实现来定义 globalThis.crypto
。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
- 对于从 Remix 迁移到 React Router 的用户,现在 @react-router/cloudflare
包为 React Router 用户提供了所有来自 @remix-run/cloudflare-pages
的导出。不再有单独的 Cloudflare Pages 包。(#11801)@react-router/cloudflare
- @remix-run/cloudflare-workers
包已被弃用。从 Remix 迁移到 React Router 的用户应直接使用 @react-router/cloudflare
包。有关如何在 Cloudflare Workers 上下文中使用 @react-router/cloudflare
的指导,请参阅 Cloudflare Workers 模板。(#11801)@react-router/dev
- 对于从 Remix 迁移到 React Router 的用户,vitePlugin
和 cloudflareDevProxyVitePlugin
导出项已被重命名和移动。(#11904)@react-router/dev
- 对于使用 Vite 插件 buildEnd
钩子的、从 Remix 迁移到 React Router 的用户,解析后的 reactRouterConfig
对象不再包含 publicPath
属性,因为这属于 Vite,而不是 React Router(#11575)。@react-router/dev
- 对于从 Remix 迁移到 React Router 的用户,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
升级到 isbot@5
。package.json
中有 isbot@3
,并且您的仓库中没有自己的 entry.server.tsx
文件:package.json
中将 isbot
升级到 isbot@5
。@react-router/dev
- 对于从 Remix 迁移到 React Router 的用户,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
- 将 params、loader 数据和 action 数据作为路由组件导出的 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
文件夹中的资源像以前一样提供,并带有一年不可变的 Cache-Control
响应头.html
和 .data
文件,不带特定的 Cache-Control
响应头.data
文件以 Content-Type: text/x-turbo
提供express.static
添加此配置时,似乎也会为 .data
文件添加一个 Cache-Control: public, max-age=0
响应头substring
替换 substr
(#12080)react-router
- 修复使用 data()
从 loader/action 返回的重定向 (#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
此版本稳定了一系列“不稳定”的 API,为即将到来的 React Router v7 版本做准备(更多信息请参阅这些文章)
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
无法在重定向后持久化的问题 (#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
被快速/同步调用时,blocker 的使用问题(#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
时的初始 hydration(注水)行为(#11838)router.state.matches
现在将包含任何部分匹配项,以便我们可以渲染祖先的 HydrateFallback
组件完整更新日志: v6.25.1...v6.26.0
日期: 2024-07-17
RouterProvider
内部实现进行 Memoize 优化,以减少不必要的重新渲染(#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 应用的 hydration 问题(#11790)完整更新日志: v6.24.1...v6.25.0
日期: 2024-07-03
polyfill.io
的引用,因为该域名已被出售,并被发现会提供恶意软件(#11741)
NavLinkRenderProps
类型,以便于为自定义 NavLink
回调函数添加类型(#11553)future.v7_relativeSplatPath
时,正确解析 splat 路由中的相对路径,这些 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
(及其内存/哈希路由对应版本) 的新选项 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)location.state
传递给 <StaticRouter>
(#11495)完整更新日志: v6.23.1...v6.24.0
日期: 2024-05-10
<Await>
解析 undefined
(#11513)document.startViewTransition
可用性时,增加防御性的 document
检查(#11544)react-router-dom/server
的导入从 index.ts
改回 react-router-dom
(#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
执行的处理,如果使用不当,将会破坏你的应用代码。请谨慎使用并进行适当的测试。
目前,所有活跃的 loader
在任何 action
提交后都会重新验证,无论 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 loader 错误冒泡到父级边界时,在 hydration 期间重新运行边界以下的 loader(#11324)future.v7_partialHydration
的 bug,该 bug 会在路由没有 loader 时将路由器视为未初始化(#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 用户体验报告(CrUX)数据集中的真实用户体验和 HTTP Archive 中的 Web 技术检测,我们可以了解到架构决策(如选择 CMS 平台或 JavaScript 框架)如何影响网站的 CWV 性能。
他们使用一个名为 wappalyzer
的工具,通过查找特定的脚本、全局 JS 变量或其他识别特征来识别给定网站正在使用的技术。例如,对于 Remix 应用程序,他们查找全局变量 __remixContext
来识别网站正在使用 Remix。
有人向我们指出,React Router 无法被可靠地识别,因为没有可识别的全局特征。他们目前正在寻找名称中包含 react-router
的外部脚本。这可以识别从 CDN(如 unpkg
)使用 React Router 的网站,但会漏掉绝大多数从 npm 注册表安装 React Router 并将其捆绑到其 JS 文件中的网站。这导致严重低估了 React Router 在网络上的使用情况。
从 6.22.0
版本开始,使用 react-router-dom
的网站将开始添加一个 window.__reactRouterVersion
变量,该变量将被设置为 SemVer 主版本号的字符串值(即 window.__reactRouterVersion = "6";
),以便它们可以被正确识别。
window.__reactRouterVersion
(#11222)createStaticHandler
的 future.v7_throwAbortReason
标志,当请求被中止时,抛出 request.signal.reason
(默认为 DOMException
),而不是像 new Error("query() call aborted: GET /path")
这样的 Error
(#11104)DOMException
是在 Node v17 中添加的,因此在 Node 16及以下版本中你不会得到 DOMException
。ErrorResponse
状态码传递给 getStaticContextFormError
,则尊重该状态码(#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
阶段卸载的持久化 fetcher 无法进行重新验证的 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
未来标志,用于在服务器端渲染时启用数据路由器的部分 hydration。这允许你提供的 hydrationData.loaderData
只包含部分初始匹配路由 loader 的值,而不是全部。当启用此标志时,路由器将在 router.initialize()
期间为没有 hydration loader 数据的路由调用 loader
函数,并且在执行未 hydration 的路由时,它将渲染到最深层提供的 HydrateFallback
(直到第一个没有 hydration 数据的路由)。(#11033)
future.v7_relativeSplatPath
标志,以实现一个对 splat 路由内相对路由的破坏性 bug 修复。(#11087)future.v7_partialHydration
未来标志,用于在服务器端渲染时启用数据路由器的部分 hydration(#11033)ErrorBoundary
中正确处理假值(falsy)的错误值(#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
时,不重新验证已卸载的 fetcher (#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
location 带有尾部斜杠时 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 实例,而无需进行属性传递(prop-drilling)。useFetchers
返回的 fetcher 上,以便可以通过 key
来查找它们。Form
和 useSubmit
现在支持可选的 navigate
/fetcherKey
属性/参数,以允许在底层以用户可选指定的 key
启动一个 fetcher 提交。<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 持久化/清理行为。Fetcher 将不再在卸载时立即被清理,而是会持续存在直到它们返回到 idle
(空闲)状态。这使得在原始 fetcher 需要卸载的情况下,处理 pending/optimistic UI(待定/乐观 UI)变得*非常*容易。
useFetchers()
API 本应只反映进行中的 fetcher 信息,用于 pending/optimistic UI —— 它并不打算反映 fetcher 数据或在 fetcher 返回到 idle
状态后还保留它们。useFetchers()
中 - 它们在那里没有任何作用,因为您可以通过 useFetcher().data
访问数据。idle
状态后被清理。useFetchers
暴露,因此您在卸载后仍然可以访问 pending/optimistic 数据。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
hook,它会告诉您过渡是否正在进行中,您可以用它来应用类或样式。
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>
简写方式,它将为您管理 hook 的使用,并在过渡期间自动为 <a>
标签添加一个 transitioning
类。
a.transitioning img {
view-transition-name: "image-expand";
}
<NavLink to={to} unstable_viewTransition>
<img src={src} alt={alt} />
</NavLink>
关于视图过渡的示例用法,请查看我们的 fork 版本,它基于出色的 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
调用错误信息中添加 method/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
hook 的 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 resolve/reject 结果为 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
在与不稳定的 blocker 函数一起使用时出现的循环问题 (#10652)@remix-run/router@1.7.1
完整变更日志: v6.14.0...v6.14.1
日期: 2023-06-23
6.14.0
版本通过 useSubmit
/fetcher.submit
增加了对 JSON 和文本提交的支持,因为在客户端单页应用(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)unstable_useBlocker
在 StrictMode
中的 key 问题 (#10573)generatePath
在传递数值 0
作为参数时的问题 (#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
的压缩 bug (#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
中,为挂起(suspending)组件提供了更好的支持。这意味着,例如,如果目标路由中的某个组件挂起,而您没有提供 Suspense
边界来显示回退界面,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
的 bug (#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 元素。如果你愿意,可以向新的 Component
和 ErrorBoundary
字段传递一个 React 组件。两者在功能上没有区别,所以请选择你喜欢的方式 😀。你不应该同时定义两者,但如果这样做了,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
序列化的 hydration 数据中正确转义 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
调用的独立问题(#9948)shouldRevalidate
函数之前只在*显式*重新验证场景下被调用(在 mutation 后、手动调用 useRevalidator
或在 Remix 中用于设置 cookie 的 X-Remix-Revalidate
标头)。它没有在同样适用于导航 loader
重新验证的*隐式*重新验证场景下被正确调用,例如搜索参数更改或点击已在当前页面的链接。现在,它在这些额外场景中也能被正确调用。current*
/next*
参数反映的是静态的 fetcher.load
URL(因此是相同的)。相反,它们应该反映触发重新验证的导航(就像 form*
参数那样)。这些参数现在能正确反映触发的导航。useSearchParams
移除搜索参数时的错误(#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
hooks,用于在应用 location 源内阻止导航(#9709, #9932)<Form>
添加 preventScrollReset
属性(#9886)useBeforeUnload
添加了透传的事件监听器选项参数(#9709)generatePath
的问题(#9764)<Await>
,使其子函数返回结果接受 ReactNode
(#9896)jsdom
bug 的变通方案(#9824)完整更新日志: v6.6.2...v6.7.0
日期: 2023-01-09
useId
的一致性(#9805)完整更新日志: v6.6.1...v6.6.2
日期: 2022-12-23
shouldRevalidate
中包含提交信息(#9777, #9782)actionData
(#9772)完整更新日志: v6.6.0...v6.6.1
日期: 2022-12-21
这次次要版本发布主要是为了稳定我们的数据路由器 SSR API,因为我们已经作为 React Router-ing Remix 工作的一部分,在 Remix 中集成了新的 RouterProvider
。
createStaticHandler
/createStaticRouter
/StaticRouterProvider
的 unstable_
前缀(#9738)useBeforeUnload()
hook(#9664)<Form method>
和 useSubmit
方法值(#9664)<button formmethod>
表单提交的覆盖问题(#9664)replace
和提交到新路径时的 PUSH
问题(#9734)errorElement
中使用 useLoaderData
(#9735)StaticRouterProvider
正确地进行 Error
对象的 hydration(#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)instanceof Response
检查,改为使用 isResponse
(#9690)URL
的问题(#9682, #9689)query
/queryRoute
添加 requestContext
支持(#9696)queryRoute(path, routeId)
的不稳定 API 已更改为 queryRoute(path, { routeId, requestContext })
完整更新日志: v6.4.4...v6.4.5
日期: 2022-11-30
action
/loader
函数返回 undefined
,则抛出错误,因为重新验证需要知道 loader 是否已执行过。undefined
在 SSR 字符串化以进行 hydration 时也会导致问题。您应始终确保您的 loader
/action
返回一个值,如果您不希望返回任何内容,可以返回 null
。(#9511)basename
(#9591)ErrorResponse
的 body 以包含更具描述性的文本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
行为(#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)useLocation
在 <Routes location>
组件内返回作用域内的 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
hook 访问。详情请见 API 文档。(#8461)<NavLink>
现在可以接收一个子函数以访问其属性。(#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 升级的说明。