react-router
)@react-router/*
)本页面列出了 React Router 自 v6.0.0
以来的所有版本发布/版本说明。对于 v6 之前的版本,请参阅 Github 发布页面。
我们在此文件中管理版本说明,而不是使用 Github 分页发布页面,原因有二:
日期: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
- 修复单次请求 bug,即当向上导航到被重用的父路由时,不会发起重新验证请求 (#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>
的水合行为,这可能是一个“破坏性 bug 修复”⚠️ 不稳定功能不建议用于生产环境
react-router
- 当 getLoadContext
未更新为返回 Map 时,添加更好的错误消息 (#13242)react-router
- 启用中间件时,更新 LoaderFunctionArgs
/ActionFunctionArgs
的 context 类型 (#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.lazy.unstable_middleware
取代的 route.unstable_lazyMiddleware
API,这是一个破坏性更改。有关更多信息,请参阅下面的“不稳定更改”部分。
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
flag,启用为浏览器将加载的脚本生成带有 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 操作和潜在的缓存污染,原因是端口净化不足。
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
路由导出分割到单独的 chunks 中 (#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
- 如果一个 middleware 抛出错误,确保我们只通过 next()
向上冒泡错误本身,并且不再暴露 MiddlewareError
的实现细节 (#13180)catch
由 next()
函数抛出的错误,这可能是一个破坏性变更。react-router
- 修复启用 middleware 时 RequestHandler
的 loadContext
参数类型 (#13204)react-router
- 更新 Route.unstable_MiddlewareFunction
的返回值类型为 Response | undefined
,而非 Response | void
(#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
patchRoutesOnNavigation
添加 fetcherKey
参数 (#13061)react-router
- 检测和处理新部署在活跃会话期间出现的 manifest 倾斜问题 (#13061)fetcher
调用时,这种不匹配将触发当前路径的文档重载react-router
- 在 SPA 模式下,跳过开发服务器中的资源路由流程 (#13113)react-router
- 修复使用 basename
时单次 fetch
_root.data
请求的问题 (#12898)react-router
- 修复包含 Record
s 的 loaderData
和 actionData
的类型定义问题 (#13139)unstable_SerializesTo
的用户来说,这是一个破坏性变更 - 更多信息请参见下方“不稳定变化”部分中的说明。@react-router/dev
- 修复对自定义客户端 build.rollupOptions.output.entryFileNames
的支持 (#13098)@react-router/dev
- 修复当 serverBundles
选项已被配置或由 preset(例如 @vercel/react-router
的 vercelPreset
)提供时,prerender
选项的使用问题 (#13082)@react-router/dev
- 修复对自定义 build.assetsDir
的支持 (#13077)@react-router/dev
- 移除未使用的依赖项 (#13134)@react-router/dev
- 在“SPA 模式”服务器构建中,除了根路由外,对所有路由进行 stub,以避免当路由模块或其依赖项导入非 SSR 友好模块时出现问题 (#13023)@react-router/dev
- 移除未使用的 Vite 文件系统 watcher (#13133)@react-router/dev
- 修复当 serverBundles
选项已配置时,对自定义 SSR 构建输入的支 (#13107)future.unstable_viteEnvironmentApi
和 serverBundles
选项的用户,服务器 bundle ID 中不再支持连字符,因为它们还需要是有效的 Vite 环境名称。@react-router/dev
- 通过从开发服务器请求中剥离 HTTP/2 伪头部来修复使用 HTTPS 时的开发服务器问题 (#12830)@react-router/dev
- 当使用 cloudflareDevProxy
Vite 插件时,在第一次开发服务器请求时延迟加载 Cloudflare 平台代理,以避免创建不必要的 workerd
进程 (#13016)@react-router/dev
- 修复 layout 路由及其对应 index 路由在 typegen 中出现重复条目的问题 (#13140)@react-router/express
- 更新 express
的 peerDependency
以包含 v5 (https://github.com/remix-run/react-router/pull/13064) (#12961)⚠️ 不稳定功能不建议用于生产环境
react-router
- 为客户端数据路由器添加 context
支持 (unstable) (#12941)react-router
- 支持在路由上使用 middleware (unstable) (#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
(unstable)您的应用程序的 clientLoader
/clientAction
函数(或 library 模式下的 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
。如果您有希望在每次请求的 context 中填充的初始数据,可以在应用程序的根部提供一个 unstable_getContext
函数。
createBrowserRouter(routes, { unstable_getContext })
<HydratedRouter unstable_getContext>
此函数应返回一个类型为 unstable_InitialContext
的值,它是一个 Map<unstable_RouterContext, unknown>
,包含 context 及其初始值。
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;
}
Middleware 是通过 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;
⚠️ Middleware 是不稳定的,不应在生产环境中使用。对于 clientMiddleware
的路由模块加载,至少存在一个已知的性能退化问题,我们将在稳定版本发布前解决。
⚠️ 启用 middleware 会对传递给您的 loader
/action
函数的 context
参数造成破坏性变更 - 更多信息请参见下方。
启用后,路由可以定义一个 middleware 函数数组,这些函数将在路由处理程序运行之前按顺序执行。这些函数接受与 loader
/action
相同的参数,外加一个额外的 next
参数来运行剩余的数据管道。这允许 middlewares 在处理程序执行之前和之后执行逻辑。
// 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,
},
];
这是一个简单的客户端日志记录 middleware 示例,可以放在根路由上。
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
函数没有返回任何内容。这是设计使然,因为在客户端没有像服务器上运行的 middlewares 那样需要通过网络发送的“响应”。所有数据都由有状态的 router
在幕后处理。
对于服务器端 middleware,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;
};
您可以从 middleware 中抛出一个 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
。
这是另一个使用服务器 middleware 检测 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
参数启用 middleware 后,您的应用程序将在 loaders 和 actions 中使用不同类型的 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
函数的自定义服务器,从服务器适配器层传递的初始 context 值的返回值不再是对象,现在应该返回一个 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
为 Single Fetch 添加了一种注册自定义序列化类型的方式,供 Apollo 等其他库和框架作者使用。它使用了一种标记类型(branded type)实现,该类型的标记属性(branded property)被设为可选,以便轻松地转换(casting)任意值。
// 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 数据,我们现在还为路由上的 HydrateFallback
组件公开了 loaderData
作为可选 prop。
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 模式下不应用 Single Fetch 重新验证去优化(revalidation de-optimization),因为没有服务器 HTTP 请求 (#12948)react-router
- 正确处理跨越预渲染/SPA 边界的重新验证 (#13021).data
请求,因为该请求会返回 404。ssr:false
模式下,所有的 loader
数据都是静态的,因为它是在构建时生成的。clientLoader
来实现任何动态功能。loader
而没有 clientLoader
,我们默认禁用重新验证,因为没有新的数据可以检索。dataStrategy
中没有带有 shouldLoad=true
的服务器端 loader,我们会短路并跳过 Single Fetch 的 .data
请求逻辑。.data
请求返回 404。react-router
- 在设置 ssr:false
时,使开发服务器行为与静态文件服务器行为保持一致 (#12948)prerender
配置时,仅 SSR 到根 HydrateFallback
(SPA 模式)。prerender
配置但当前路径未被预渲染时,仅 SSR 到根 HydrateFallback
(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
prop 中 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
- 修复 react-router --version
命令加载 package.json
的路径问题 (#13012)⚠️ 不稳定功能不建议用于生产环境
react-router
- 为库作者添加 unstable_SerializesTo
标记类型,用于注册可通过 React Router 流式格式 (turbo-stream
) 序列化的类型 (#12264)@react-router/dev
- 通过 future.unstable_splitRouteModules
添加在框架模式下分割路由模块的不稳定支持 (#11871)@react-router/dev
- 添加 future.unstable_viteEnvironmentApi
标志以启用实验性的 Vite Environment 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 调用,而默认组件导出则大得多。这对性能是一个问题,因为它意味着如果我们在客户端导航到这个路由,必须在 client loader
开始运行之前下载整个路由模块。
以时间线形式可视化:
Get Route Module: |--=======|
Run clientLoader: |-----|
Render: |-|
相反,我们希望将其优化为如下所示:
Get clientLoader: |--|
Get Component: |=======|
Run clientLoader: |-----|
Render: |-|
为了实现此优化,React Router 会在生产构建过程中将路由模块分割成多个更小的模块。在这种情况下,我们最终会得到两个独立的虚拟模块——一个用于 client 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
并将路由分成多个文件来实现它,正如我们关于 路由模块懒加载 的博客文章中所述。
现在这些可以作为单独的模块使用,client loader
和组件可以并行下载。这意味着 client 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} />;
}
此路由仍然有效,但由于 client 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 引入的回归问题,该问题导致使用 Lazy Route Discovery (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
- 在 Single Fetch 响应中正确处理无法包含主体的状态码 (204 等) (#12760)react-router
- 抛出 data()
结果时,将 header 正确地作为 errorHeaders
冒泡 (#12846)headers
也返回了 Set-Cookie
headers,则避免重复react-router
- 停止对返回原始字符串/对象的资源路由报错,转而将其序列化为 text/plain
或 application/json
响应 (#12848).data
扩展名的情况下作为资源路由访问时.data
请求访问时,它们仍将通过 turbo-stream
进行编码react-router
- 优化 Lazy Route Discovery 路径发现,优先使用 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
- 在从 loader 数据中过滤掉 redirect
响应时,不依赖于 symbol
(#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
服务器条件。这修复了在 Node 22.10.0+ 上开发期间,当使用依赖于 React Router 的库时出现的 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
- 抛出未包裹的 Single Fetch redirect
以与 Single Fetch 之前的行为对齐 (#12506)react-router
- 推断 loader 数据类型时忽略重定向 (#12527)react-router
- 移除 <Link prefetch>
警告,该警告在 Lazy Route Discovery 环境中容易产生误报 (#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
以使用 Single Fetch 的新 streamTimeout
值 (#12478)abortDelay
功能已在 v7 中移除,因为它与 Remix v2 的 defer
实现耦合,但此属性的移除被遗漏了entry.server
文件中仍在使用此属性,你的应用程序可能没有像你预期那样中断流,你需要采用 Single Fetch 引入的新 streamTimeout
值@react-router/fs-routes
- 如果 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 dependency (#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 dev server 重启时清理 typegen 文件监听器 (#12331)@react-router/dev
- 将路由 error
作为 prop 传递给 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
包已被移除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
方法来polyfill fetch
APIreact@18
, react-dom@18
Remix 和 React Router 遵循一个API 开发策略,利用“Future Flags”来避免在主要版本中引入大量重大变更。相反,重大变更会在次要版本中通过 flag 引入,允许用户方便地选择启用。在下一个主要版本中,所有 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
使用 React Router v7 构建全栈 SSR 应用的正确方法是使用Remix Vite 插件。以前基于 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
hook 取代,因为它会传递 buildManifest
参数。如果需要,您仍然可以将构建 manifest 写入磁盘,但您很可能会发现将依赖于构建 manifest 的任何逻辑写入 buildEnd
hook 本身更方便。(#11573)
如果您之前使用了 manifest
选项,可以用一个将 manifest 写入磁盘的 buildEnd
hook 来替代它,示例如下:
// 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"),
]),
];
对于迁移到 React Router 的 Remix 用户,您仍然可以使用 @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
实现,转而使用通过 single 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
flag(#11696)future.v7_normalizeFormMethod
future flag(#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
flag(#11725)<RouterProvider fallbackElement>
prop。fallbackElement
移到您的根路由上的 hydrateFallbackElement
/HydrateFallback
。future.v7_partialHydration
(使用 fallbackElement
时)的情况下,state.navigation
在初始加载期间会被填充。future.v7_partialHydration
后,state.navigation
在初始加载期间保持 "idle"
状态。future.v7_relativeSplatPath
future flag(#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
flag(#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
- 对于迁移到 React Router 的 Remix 用户,@remix-run/cloudflare-pages
中的所有导出现在都在 @react-router/cloudflare
包中提供给 React Router 用户。不再有单独的 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
升级到 isbot@5
@react-router/dev
- 对于迁移到 React Router 的 Remix 用户,Vite manifests(即 .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
下,以避免在生产环境中意外提供它们,特别是客户端构建的 manifests。后来通过额外的逻辑进行了改进,即在构建过程结束时删除这些 Vite manifest 文件,除非应用的 Vite 配置中启用了 Vite 的 build.manifest
。这大大降低了在生产环境中意外提供 Vite manifests 的风险,因为只有明确请求时它们才会存在。因此,我们现在可以假定用户知道需要自行管理这些额外文件,React Router 可以安全地生成更标准的 Vite 构建输出。react-router
- 将 Params、loader data 和 action data 作为路由组件导出的 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 异步 hydration 方法不兼容 (#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
头部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-02-27
patchRoutesOnNavigation
添加 fetcherKey
参数 (#13109)6.29.0
中引入的回归问题,该问题通过 #12169 引入,导致在使用惰性路由发现(patchRoutesOnNavigation
)的应用中,导航到通配符路由内的哈希路由时出现问题 (#13108)完整更新日志: v6.29.0...v6.30.0
日期:2025-01-30
signal
作为参数提供给 patchRoutesOnNavigation
(#12900)data()
结果时正确传递 headers (#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
Date: 2024-11-06
json
/defer
添加弃用警告,推荐返回原始对象完整更新日志: v6.27.0...v6.28.0
Date: 2024-10-11
本次发布稳定化了一系列“不稳定”的 API,为即将到来的 pending React Router v7 发布做准备(更多信息请参阅 these posts)
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
参数,会导致 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
Date: 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
Date: 2024-08-15
unstable_patchRoutesOnMiss
重命名为 unstable_patchRoutesOnNavigation
以匹配新行为 (#11888)unstable_patchRoutesOnNavigation
逻辑,以便在匹配具有动态参数或通配符片段的路由时调用该方法,以防存在尚未发现的得分更高的静态路由 (#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,用于 Remix Single Fetch (#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
内部实现进行记忆化处理,以减少不必要的重新渲染 (#11803)完整更新日志:v6.25.0...v6.25.1
日期:2024-07-16
v7_skipActionErrorRevalidation
已稳定本次发布将 `future.unstable_skipActionErrorRevalidation` 标记稳定为 future.v7_skipActionErrorRevalidation
,为即将发布的 React Router v7 版本做准备。
完整更新日志:v6.24.1...v6.25.0
日期:2024-07-03
完整更新日志:v6.24.0...v6.24.1
日期:2024-06-24
我们非常高兴在 `v6.24.0` 中发布新的“懒加载路由发现”API!有关背景信息,请参阅原始 RFC。要点是: 自从我们在 v6.4 中通过 <RouterProvider>
引入 Data APIs 以来,我们一直有点遗憾,其中一个权衡是缺乏一个引人注目的代码分割方案,能够媲美我们在 <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]);
}
},
}
);
完整更新日志:v6.23.1...v6.24.0
日期:2024-05-10
@remix-run/router
- 在 `staticHandler.queryRoute` 上支持 `unstable_dataStrategy` (#11515)完整更新日志:v6.23.0...v6.23.1
日期:2024-04-23
新的 `unstable_dataStrategy` API 是一个低级别 API,设计用于高级用例,你需要在其中控制 `loader`/`action` 函数的数据策略。默认实现是当前行为,即并行抓取所有 loaders,但此选项允许用户实现更高级的数据流,包括 Remix "Single Fetch"、用户自定义的 middleware/context APIs、自动 loader 缓存等。更多信息请参阅文档。
注意:这是一个低级别 API,旨在用于高级用例。这会覆盖 React Router 内部处理 `loader`/`action` 执行的方式,如果处理不当,可能会导致你的应用代码出错。请谨慎使用并进行充分测试。
目前,无论 action
结果如何,所有活跃的 loader
都会在任何 action
提交后重新验证。然而,在大多数情况下,action
返回 `4xx` 或 `5xx` 响应意味着数据并未实际改变,此时重新验证是不必要的。我们引入了一个新的 future.unstable_skipActionErrorRevalidation
标记来改变这一行为,并计划在未来的 React Router 版本中将其设为默认行为。
启用此标记后,返回/抛出 `4xx`/`5xx` 响应状态的 action
将不再自动重新验证。如果在此标记启用时,你需要在一个 `4xx`/`5xx` 结果后进行重新验证,你仍然可以通过让 `shouldRevalidate` 返回 true
来实现——现在 `shouldRevalidate` 也接收一个新的参数 `unstable_actionStatus`,与 `actionResult` 一起,以便你可以根据 action
响应的状态做出决定,而无需将其编码到 action 数据中。
@remix-run/router
- 添加一个新的 `future.unstable_skipActionRevalidation` 未来标记 (#11098)@remix-run/router
- SSR(服务器端渲染):向 `staticHandler.query` 方法中添加了一个新的 `skipLoaderErrorBubbling` 选项,用于禁用静态处理程序 (static handler) 的错误冒泡,以便在 Remix 的 Single Fetch 实现中使用 (#11098, (#11377))完整更新日志:v6.22.3...v6.23.0
日期:2024-03-07
完整更新日志: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 技术检测,我们可以了解内容管理系统平台或 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
用于 CWV 报告检测 (#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 加载时不正确工作的错误 (#11121)submitting
阶段卸载的持久化 fetcher 重新验证的错误 (#11102)resolveTo
中相对路径逻辑的重复 (#11097)完整更新日志: v6.21.0...v6.21.1
日期: 2023-12-13
future.v7_relativeSplatPath
我们在 6.19.0
中修复了一个 splat 路由路径解析错误,但后来发现大量应用程序依赖于该错误行为,因此我们在 6.20.1
中回滚了该修复(参见 #10983、#11052、#11078)。
错误行为是,在 splat 路由内部解析相对路径时的默认行为会忽略当前路由路径中的任何 splat (`*`) 部分。启用该 future 标记后,splat 部分将包含在 splat 路由内的相对路径逻辑中。
更多信息请参考 useResolvedPath
文档和/或详细的更新日志条目。
我们为 `@remix-run/router` 添加了一个新的 future.v7_partialHydration
future 标记,它允许在服务器端渲染时对数据路由器进行部分水合。这允许您提供 hydrationData.loaderData
,其中包含部分初始匹配路由加载器的数据,但不包含全部。启用此标记后,路由器将在 router.initialize()
期间调用没有水合加载器数据的路由的 loader
函数,并在执行未水合的路由时,渲染到最深的提供的 HydrateFallback
(直到第一个没有水合数据的路由)。(#11033)
future.v7_relativeSplatPath
标记,以实现在 splat 路由内修复相对路由的重大错误。(#11087)future.v7_partialHydration
future 标记,在服务器端渲染时启用数据路由器的部分水合 (#11033)ErrorBoundary
中正确处理 falsy 错误值 (#11071)loader
/action
函数的响应时抛出的错误 (#11061)Link
/NavLink
时 relative="path"
的问题 (#11062)完整更新日志: v6.20.1...v6.21.0
日期: 2023-12-01
useResolvedPath
针对 splat 路由的修复,原因是大量应用程序依赖于该错误行为(参见 #11052)(#11078)6.19.0
和 6.20.0
版本中。如果您从 6.18.0
或更早版本升级,则不会受到此修复的影响。完整更新日志: v6.20.0...v6.20.1
日期: 2023-11-22
[!警告] 请使用
6.20.1
或更高版本代替6.20.0
。我们发现大量应用依赖于此版本中修复的错误行为 (#11045)。我们在6.20.1
中回滚了该修复,并将在后续版本中通过 future 标记重新引入。更多详情请参阅 #11052。
PathParam
类型 (#10719)v7_fetcherPersist
时,不重新验证已卸载的 fetcher (#11044)resolveTo
在 splat 路由中的路径解析错误 (#11045)getPathContributingMatches
的其他一些代码路径UNSAFE_getPathContributingMatches
导出,因为我们在 react-router
/react-router-dom
层中不再需要它完整更新日志: v6.19.0...v6.20.0
日期: 2023-11-16
[!警告] 请使用
6.20.1
或更高版本代替6.19.0
。我们发现大量应用依赖于此版本中修复的错误行为 (#10983)。我们在6.20.1
中回滚了该修复,并将在后续版本中通过 future 标记重新引入。更多详情请参阅 #11052。
unstable_flushSync
API此版本为命令式 API(useSubmit
、useNavigate
、fetcher.submit
、fetcher.load
)带来了一个新的 unstable_flushSync
选项,允许用户选择同步 DOM 更新以实现 pending/optimistic UI。
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 会导致在通配符路由中使用 useResolvedPath(".")
时丢失 URL 路径的通配符部分。 (#10983)
"."
路径。如果您在应用程序的通配符路由内部通过 "."
进行相对路由,您应该仔细检查您的逻辑是否依赖于这种有 bug 的行为,并进行相应的更新。修复了在使用保持挂载状态的 useFetcher
中,更改 key
没有被拾取的问题 (#11009)
修复 useFormAction
错误地继承了子路由 action
提交中的 ?index
查询参数的问题 (#11025)
修复 NavLink
在 to
location 带有末尾斜杠时的 active
逻辑 (#10734)
修复类型定义,使 unstable_usePrompt
除了接受 boolean
外,还可以接受 BlockerFunction
(#10991)
修复 relative="path"
bug,该 bug 导致相对路径计算从完整的 location pathname 开始,而不是从当前的上下文路由 pathname 开始。 (#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-drillinguseFetchers
返回的 fetcher 上暴露出来,以便可以通过 key
进行查找Form
和 useSubmit
现在支持可选的 navigate
/fetcherKey
prop/参数,以便在底层启动一个 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 始终应该只反映待处理/乐观 UI 的进行中的 fetcher 信息——它不打算反映 fetcher 数据或在 fetchers 返回 idle
状态后继续持有它们useFetchers()
中 - 它们在那里没有作用,因为您可以通过 useFetcher().data
访问数据idle
状态后进行清理useFetchers
暴露,因此您可以在卸载后仍然访问待处理/乐观数据key
重新挂载,那么即使原始的 fetcher 已被卸载,其结果也会被处理key
API 和 navigate=false
选项 (#10960)future.v7_fetcherPersist
标志 (#10962)matchPath
中添加对可选路径段的支持 (#10768)BrowserRouter
、HashRouter
和 MemoryRouter
上的 future
prop,使其接受 Partial<FutureConfig>
而不是要求包含所有标志 (#10962)router.getFetcher
/router.deleteFetcher
类型定义错误地将 key
指定为可选参数的问题 (#10960)完整更新日志: 6.17.0...6.18.0
日期: 2023-10-16
我们很高兴发布 React Router 对 View Transitions API 的实验性支持!您现在可以触发导航 DOM 更新,使其被包裹在 document.startViewTransition
中,以在应用程序的 SPA 导航上启用 CSS 动画过渡。
在您的 React Router 应用中启用 View Transition 的最简单方法是通过新的 <Link unstable_viewTransition>
prop。这将导致导航 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>
有关 View Transitions 的示例用法,请查看我们对出色的 Astro Records demo 的 fork。
有关使用 View Transitions API 的更多信息,请参阅 Google Chrome 团队的 使用 View Transitions API 实现平滑简单的过渡 指南。
sessionStorage
不可用时,在 ScrollRestoration
中记录警告并优雅地处理失败 (#10848)RouterProvider
future
prop 类型,使其成为 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
中的副作用,以避免在提示被解除阻塞且同步执行导航时抛出异常 (#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>
prop,以便在提交导航时填充 history.state
(#10630)defer
promise 以 undefined
resolve/reject,则触发错误,以匹配 loaders 和 actions 的行为(它们必须返回一个值或 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 和文本提交的支持,因为在客户端 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/文本提交内容。 (#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
函数的位置中移除 basename
,以匹配 useLocation
的行为 (#10573)StrictMode
中 unstable_useBlocker
的关键问题 (#10573)generatePath
传入数值 0
参数时的问题 (#10612)tsc --skipLibCheck:false
的问题 (#10622)typescript
到 5.1 (#10581)完整更新日志: v6.13.0...v6.14.0
日期: 2023-06-14
6.13.0
实际上是一个补丁版本,但由于我们添加了一个新的 future flag,它带来了 SemVer 次要版本升级。
future.v7_startTransition
**简而言之**,6.13.0
与 6.12.0
相同,但我们将 React.startTransition
的使用移至了可选启用的 future.v7_startTransition
future flag 后面,因为我们发现有些实际应用当前使用 Suspense
的方式与 React.startTransition
不兼容。
因此,在 6.13.0
中,默认行为将不再利用 React.startTransition
<BrowserRouter>
<Routes>{/*...*/}</Routes>
</BrowserRouter>
<RouterProvider router={router} />
如果您希望启用 React.startTransition
,请将此 future flag 传递给您的路由组件
<BrowserRouter future={{ v7_startTransition: true }}>
<Routes>{/*...*/}</Routes>
</BrowserRouter>
<RouterProvider router={router} future={{ v7_startTransition: true }}/>
我们建议大家尽早采用此 flag,以便更好地兼容 React 并发模式,但如果您遇到问题,可以在 v7 之前继续不使用 React.startTransition
。问题通常归结于在渲染周期中创建全新的 Promise,因此如果您在选择启用 React.startTransition
时遇到问题,应将 Promise 创建移出渲染周期或放在 useMemo
后面。
React.startTransition
的用法移至 future flag 后面 (#10596)React.startTransition
的压缩错误 (#10588)完整更新日志: v6.12.1...v6.13.0
日期: 2023-06-08
[!警告] 请使用版本
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
[!警告] 请使用版本
6.13.0
或更高版本,而不是6.12.0
/6.12.1
。这些版本存在一些 Webpack 构建/压缩问题,导致生产打包文件构建失败或包含无效的压缩代码。详情请参见 #10569 和 #10579。
React.startTransition
支持在 6.12.0
中,我们通过将内部路由状态更新包裹在 React.startTransition
中,为 suspending 组件提供了更好的支持。这意味着,例如,如果您在目标路由中的一个组件 suspending,并且您没有提供 Suspense
边界来显示 fallback,React 将延迟新 UI 的渲染并显示旧 UI,直到该异步操作完成。这对于等待图片或 CSS 文件加载等场景很有用(从技术上讲,您也可以用它来加载数据,但我们仍然建议使用 loaders 来实现 😀)。要快速了解此用法,请查看 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
basename
在 <RouterProvider>
内部的后代 <Routes>
中的重复问题 (#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)fetcher
调用中前面加上 basename
来变通解决此问题,则需要移除手动添加的 basename
(fetcher.load('/basename/route') -> fetcher.load('/route')
)@remix-run/router@1.6.0
(更新日志)RouterProvider
时,useNavigate
/useSubmit
/fetcher.submit
现在在位置变化时保持稳定,因为我们可以通过 @remix-run/router
实例处理相对路由并摆脱对 useLocation()
的依赖 (#10336)BrowserRouter
时,这些 hook 在位置变化时仍然不稳定,因为它们仍然依赖于 useLocation()
action
提交或 router.revalidate
调用时重新验证 (#10344)Component
而不是 element
时出现的意外重新渲染问题 (#10287)<Link to="//">
和其他无效 URL 值 (#10367)<RouterProvider>
中,用于同步内部 @remix-run/router
路由状态的 hook 从 useSyncExternalStore
切换到了 useState
。我们发现了一些 微妙的 bug,其中路由状态更新在其他正常 useState
更新之前传播,这可能导致在 useEffect
调用中出现问题。 (#10377, #10409)RouterProvider
存在错误时阻止渲染后代 <Routes>
的 bug (#10374)activeRef
,修复了在渲染周期中对 useNavigate
的检测,使得 navigate
函数可以传递给子组件并在那里的 useEffect
中调用 (#10394)useRevalidator()
解决 loader 驱动的错误边界场景 (#10369)LoaderFunction
/ActionFunction
的返回类型,以防止 undefined
成为有效的返回值 (#10267)fetcher.load
指向没有 loader
的路由时返回正确的 404 错误 (#10345)AbortController
在正在重新验证的 fetchers 和触发它们的导航/重新验证之间的使用,以便正在重新验证的 fetcher 的卸载/删除不会影响正在进行的触发导航/重新验证 (#10271)完整更新日志: v6.10.0...v6.11.0
日期: 2023-03-29
我们最近在 Remix 博客上发布了一篇题为 "让您的 Remix 应用面向未来" 的文章,介绍了我们如何确保您的 Remix 和 React Router 应用未来平滑升级的策略。React Router 6.10.0
为这些 flag(针对数据路由)添加了支持,您可以在创建路由器时指定这些 flag
const router = createBrowserRouter(routes, {
future: {
// specify future flags here
},
});
future.v7_normalizeFormMethod
引入的第一个未来 flag 是 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
(#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 提供了初步提案和概念验证实现。
generatePath
错误地应用参数的问题 (#10078)[react-router-dom-v5-compat]
添加了遗漏的数据路由 API 重新导出 (#10171)完整更新日志: v6.8.2...v6.9.0
日期: 2023-02-27
basename
外部,则将 <Link to>
中的同源绝对 URL 视为外部链接 (#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 与当前源 (origin) 相同,仍会执行客户端导航。如果 URL 是指向不同源 (origin),则会为新源发起全新的文档请求。 (#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 中使用 X-Remix-Revalidate
头部信息设置 cookie)中被调用。但在也适用于导航 loader 重新验证的隐式重新验证场景(如搜索参数变化,或点击当前页面的链接)中,它没有被正确调用。现在已在这些附加场景中正确调用该函数。current*
/next*
参数反映的是静态的 fetcher.load
URL(因此它们是相同的)。实际上,它们应该反映触发重新验证的导航(就像 form*
参数那样)。现在这些参数已正确反映触发导航。useSearchParams
移除搜索参数时出现的错误 (#9969)<fetcher.Form>
上遵守 preventScrollReset
属性 (#9963)<ScrollRestoration>
使用 pagehide
代替 beforeunload
事件。这提供了更好的跨浏览器支持,尤其是在 Mobile 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
Hook,用于阻止在应用当前源 (location origin) 内的导航 (#9709, #9932)<Form>
添加 preventScrollReset
属性 (#9886)useBeforeUnload
添加了传递事件监听器选项的参数 (#9709)generatePath
在存在可选参数时的问题 (#9764)<Await>
组件,使其 children 函数的返回值可以接受 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
此 minor 版本主要旨在稳定 Data Routers 的 SSR API,现在我们已将新的 RouterProvider
集成到 Remix 中,作为 React Router-ing Remix 工作的一部分。
createStaticHandler
/createStaticRouter
/StaticRouterProvider
的 unstable_
前缀 (#9738)useBeforeUnload()
Hook (#9664)<Form method>
和 useSubmit
方法值 (#9664)<button formmethod>
覆盖表单提交方法的问题 (#9664)replace
以及提交到新路径时使用 PUSH
的问题 (#9734)errorElement
中使用 useLoaderData
(#9735)StaticRouterProvider
正确 hydrate Error
对象 (#9664)hydrationData
的 SSR 应用,跳过初始滚动恢复 (#9664)完整变更日志: v6.5.0...v6.6.0
日期:2022-12-16
此版本引入了对可选路由段 (Optional Route Segments) 的支持。现在,在任意路径段的末尾添加 ?
会使整个路径段变为可选。这适用于静态段和动态参数。
可选参数示例
<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"
...
}
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)
已更改为 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
主体,使其在内部 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
加载数据,使用 Route 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>
现在可以接收一个子函数来访问其属性。 (#8164)useMatch
和 matchPath
的 TypeScript 签名。例如,当你调用 useMatch("foo/:bar/:baz")
时,路径会被解析,返回类型将是 PathMatch<"bar" | "baz">
。 (#8030)完整更新日志: v6.0.2...v6.1.0
日期: 2021-11-09
<Link>
添加了 reloadDocument
属性。这使得 <Link>
可以像普通的 anchor 标签一样工作,在导航后重新加载文档,同时保留相对的 to
解析。(#8283)完整更新日志: v6.0.1...v6.0.2
日期: 2021-11-05
<StaticRouter location>
值 (#8243)<Routes>
内部使用 <Route>
时的断言(invariant),以帮助人们进行更改 (#8238)完整更新日志: v6.0.0...v6.0.1
日期: 2021-11-03
React Router v6 来了!
请阅读我们的博客文章,了解 v6 版本中的所有精彩内容,包括如何从 React Router v5 和 Reach Router 升级的说明。