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
- 修复了单次 fetch 的 bug,该 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>
同构行为的用户来说,可能是一个“破坏性的 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 dependency 范围以支持 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 操纵和潜在的缓存污染。
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.unstable_middleware
属性在 route.lazy
的返回值中不再受支持route.unstable_lazyMiddleware
@react-router/dev
- 当 future.unstable_middleware
和 future.unstable_splitRouteModules
都启用时,在可能的情况下,将 unstable_clientMiddleware
路由导出拆分成单独的块 (#13210)@react-router/dev
- 通过确保只有在定义了 unstable_clientMiddleware
时,路由模块才会在中间件阶段阻塞,从而提升 future.unstable_middleware
的性能 (#13210)完整变更日志: v7.4.0...v7.4.1
日期: 2025-03-19
@react-router/dev
- 为 virtual:react-router/server-build
模块生成类型 (#13152)react-router
- 修复 SPA 模式下首次加载重定向时的根 loader 数据 (#13222)react-router
- 在延迟路由发现中加载祖先无路径/index 路由,用于向上非急切发现的路由 (#13203)react-router
- 在 ssr:true
应用中,修复仅使用 clientLoader
的路由的 shouldRevalidate
行为 (#13221)@react-router/dev
- 修复与使用了 configureServer
和/或 configurePreviewServer
钩子的其他 Vite 插件的冲突 (#13184)⚠️ 不稳定特性不推荐用于生产环境
react-router
- 如果中间件抛出错误,确保我们只通过 next()
传递错误本身,并且不再暴露 MiddlewareError
的实现细节 (#13180)next()
函数抛出的错误,这可能是一个破坏性变更react-router
- 修复当中间件启用时 RequestHandler
的 loadContext
参数类型 (#13204)react-router
- 更新 Route.unstable_MiddlewareFunction
,使其返回值类型为 Response | undefined
而非 Response | void
(#13199)@react-router/dev
- 当 future.unstable_splitRouteModules
设置为 "enforce"
时,允许根路由导出可拆分和不可拆分的部分,因为它总是位于一个单独的块中 (#13238)@react-router/dev
- 当 future.unstable_viteEnvironmentApi
启用时,允许覆盖默认 SSR 环境的插件 (例如 @cloudflare/vite-plugin
) 放置在 React Router 插件之前或之后 (#13183)create-react-router
react-router
@react-router/architect
@react-router/cloudflare
@react-router/dev
@react-router/express
@react-router/fs-routes
@react-router/node
@react-router/remix-config-routes-adapter
@react-router/serve
完整变更日志: v7.3.0...v7.4.0
日期: 2025-03-06
fetcherKey
作为 patchRoutesOnNavigation
的参数 (#13061)react-router
- 检测并处理在活动会话期间进行新部署时的 manifest 偏移问题 (#13061)fetcher
调用未发现的路由时,这种不匹配将触发当前路径的文档重新加载react-router
- 在 SPA 模式下,跳过开发服务器中的资源路由流程 (#13113)react-router
- 修复使用 basename
时单次 fetch
_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
选项的用户,服务器 bundle ID 中不再支持连字符,因为它们也需要是有效的 Vite 环境名称。@react-router/dev
- 修复使用 HTTPS 时开发服务器的问题,通过从开发服务器请求中剥离 HTTP/2 伪头部 (#12830)@react-router/dev
- 当使用 cloudflareDevProxy
Vite 插件时,在首次开发服务器请求时延迟加载 Cloudflare 平台代理,以避免创建不必要的 workerd
进程 (#13016)@react-router/dev
- 修复布局路由及其对应的 index 路由在类型生成中重复的条目 (#13140)@react-router/express
- 更新 express
peerDependency
以包含 v5 (https://github.com/remix-run/react-router/pull/13064) (#12961)⚠️ 不稳定特性不推荐用于生产环境
react-router
- 为客户端数据路由器添加 context
支持 (待稳定) (#12941)react-router
- 支持路由上的 middleware (待稳定) (#12941)@react-router/dev
- 修复在使用 future.unstable_viteEnvironmentApi
时,如果 ssr
环境被其他插件配置为自定义的 Vite.DevEnvironment
而非默认的 Vite.RunnableDevEnvironment
时出现的错误 (#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
。如果您有希望在每次请求的 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;
}
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)注册自定义序列化类型的方式。它通过一个品牌类型实现,该品牌类型的属性被设为可选,以便于任意值的类型转换。
// without the brand being marked as optional
let x1 = 42 as unknown as unstable_SerializesTo<number>;
// ^^^^^^^^^^
// with the brand being marked as optional
let x2 = 42 as unstable_SerializesTo<number>;
然而,这破坏了 loaderData
和 actionData
对任何 Record
类型的类型推断,因为它们现在会(错误地)匹配 unstable_SerializesTo
。这影响了所有用户,而不仅仅是依赖 unstable_SerializesTo
的用户。为了修复此问题,unstable_SerializesTo
的品牌属性被标记为必需而非可选。
对于使用 unstable_SerializesTo
的库和框架作者,在转换为 unstable_SerializesTo
之前,您可能需要添加 as unknown
类型转换。
create-react-router
react-router
@react-router/architect
@react-router/cloudflare
@react-router/dev
@react-router/express
@react-router/fs-routes
@react-router/node
@react-router/remix-config-routes-adapter
@react-router/serve
完整变更日志: v7.2.0...v7.3.0
日期: 2025-02-18
href
工具函数在框架模式下,我们现在为您提供一个完全类型安全的 href
工具函数,让您在应用中获得路径自动补全和参数验证的所有便利和保障。
import { href } from "react-router";
export default function Component() {
const link = href("/blog/:slug", { slug: "my-first-post" });
// ^ type-safe! ^ Also type-safe!
return (
<main>
<Link to={href("/products/:id", { id: "asdf" })} />
<NavLink to={href("/:lang?/about", { lang: "en" })} />
</main>
);
}
现在,如果您传递了错误的路径值或错误的参数值,将会收到类型错误提示。
const badPath = href("/not/a/valid/path");
// ^ Error!
const badParam = href("/blog/:slug", { oops: "bad param" });
// ^ Error!
此版本增强了在使用 ssr:false
进行预渲染时,结合使用预渲染路径和其他以“SPA 模式”运行的路径的能力。
prerender
配置的情况下指定 ssr:false
,这将被视为“SPA 模式”,生成的 index.html
文件将只渲染到根路由,并且能够为任何有效的应用路径进行 hydrate。prerender
配置中指定了 ssr:false
但 不 包含 /
路径 (例如,prerender: ['/blog/post']
),那么我们仍然会生成一个 "SPA 模式" 的 index.html
文件,该文件可以在应用程序中的任何路径进行水合。ssr:false
并在 prerender
配置中包含了 /
路径,那么生成的 index.html
文件将仅用于根索引路由,因此我们现在还会生成一个单独的 "SPA 模式" 文件,位于 __spa-fallback.html
中,你可以将其用于服务/水合未预渲染的路径。更多信息,请参阅预渲染 文档。
loader
SPA 模式过去禁止在所有路由中使用 loader,以便我们可以在应用程序中的任何路径进行水合。然而,由于根路由总是在构建时渲染,我们可以解除对根路由的此限制。
为了在预渲染期间使用你的构建时 loader 数据,我们现在也将 loaderData
作为路由上 HydrateFallback
组件的可选属性暴露出来。
HydrateFallback
正在渲染(因为 子 路由正在加载),此属性将是已定义的。HydrateFallback
正在渲染(因为路由本身有自己的水合 clientLoader
),这将是 undefined
。react-router
- 新的类型安全的 href
工具函数,确保链接指向应用程序中的实际路径 (#13012)@react-router/dev
- 当使用 ssr:false
预渲染 /
路由时,生成一个 "SPA 回退" HTML 文件 (#12948)@react-router/dev
- 在 SPA 模式下允许根路由使用 loader
,因为它可以在构建时被调用/服务端渲染 (#12948)Route.HydrateFallbackProps
现在也接收 loaderData
react-router
- 禁用所有 ssr:false
应用程序(而不仅仅是 "SPA 模式")的懒加载路由发现,因为没有运行时服务器来处理使用搜索参数配置的 __manifest
请求 (#12894)ssr:false
应用程序。prerender
场景中,我们会预渲染 /__manifest
文件,但这会产生一些关于静态文件服务器行为的不必要的假设。react-router
- 在 SPA 模式下不应用单次抓取重新验证去优化,因为没有服务器 HTTP 请求 (#12948)react-router
- 正确处理跨越预渲染/SPA 边界的重新验证 (#13021).data
请求,因为请求会返回 404。ssr:false
模式下,所有 loader
数据都是静态的,因为它是在构建时生成的。clientLoader
来执行任何动态操作。loader
而没有 clientLoader
,我们默认禁用重新验证,因为没有新的数据可供检索。dataStrategy
中没有带有 shouldLoad=true
的服务端 loader,我们就会短路并跳过单次抓取 .data
请求逻辑。.data
请求。react-router
- 当设置 ssr:false
时,使开发服务器行为与静态文件服务器行为对齐 (#12948)prerender
配置时,仅将 SSR 执行到根 HydrateFallback
(SPA 模式)。prerender
配置但当前路径未被预渲染时,仅将 SSR 执行到根 HydrateFallback
(SPA 回退)。.data
请求,返回 404。react-router
- 改进框架模式下 CSS 副作用的预取性能 (#12889)react-router
- 正确处理懒加载路由发现中中断的 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
- 修复重复参数的类型生成问题 (#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 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} />;
}
在此示例中,我们有一个执行基本 fetch 调用的最小化 clientLoader
导出,而默认组件导出则大得多。这对性能来说是一个问题,因为这意味着如果我们想在客户端导航到此路由,则必须在客户端加载器开始运行之前下载整个路由模块。
为了将此可视化为时间线
Get Route Module: |--=======|
Run clientLoader: |-----|
Render: |-|
相反,我们希望将其优化为以下形式
Get clientLoader: |--|
Get Component: |=======|
Run clientLoader: |-----|
Render: |-|
为了实现此优化,React Router 会在生产构建过程中将路由模块拆分成多个更小的模块。在这种情况下,我们将最终得到两个独立的虚拟模块——一个用于客户端加载器,另一个用于组件及其依赖项。
export async function clientLoader() {
return await fetch("https://example.com/api").then((response) =>
response.json()
);
}
import { MassiveComponent } from "~/components";
export default function Component({ loaderData }) {
return <MassiveComponent data={loaderData} />;
}
💡 此优化在框架模式下自动应用,但您也可以在库模式下通过
route.lazy
并在多个文件中编写路由来手动实现,正如我们在关于路由模块懒加载的博客文章中介绍的那样。
既然这些可以作为独立的模块使用,客户端加载器和组件就可以并行下载。这意味着客户端加载器一旦准备好就可以执行,而无需等待组件。
当使用更多路由模块 API 时,此优化效果更加显著。例如,在使用 clientLoader
、clientAction
和 HydrateFallback
时,单个路由模块在客户端导航期间的时间线可能如下所示:
Get Route Module: |--~~++++=======|
Run clientLoader: |-----|
Render: |-|
这将被优化为以下形式
Get clientLoader: |--|
Get clientAction: |~~|
Get HydrateFallback: SKIPPED
Get Component: |=======|
Run clientLoader: |-----|
Render: |-|
请注意,此优化仅在被拆分的路由模块 API 不在同一个文件中共享代码时才有效。例如,以下路由模块无法拆分:
import { MassiveComponent } from "~/components";
const shared = () => console.log("hello");
export async function clientLoader() {
shared();
return await fetch("https://example.com/api").then((response) =>
response.json()
);
}
export default function Component({ loaderData }) {
shared();
return <MassiveComponent data={loaderData} />;
}
此路由仍然可以工作,但由于客户端加载器和组件都依赖于在同一个文件中定义的 shared
函数,它将被反优化为单个路由模块。
为了避免这种情况,您可以将导出之间共享的任何代码提取到一个单独的文件中。例如:
export const shared = () => console.log("hello");
然后您可以在您的路由模块中导入此共享代码,而不会触发反优化。
import { MassiveComponent } from "~/components";
import { shared } from "./shared";
export async function clientLoader() {
shared();
return await fetch("https://example.com/api").then((response) =>
response.json()
);
}
export default function Component({ loaderData }) {
shared();
return <MassiveComponent data={loaderData} />;
}
由于共享代码在自己的模块中,React Router 现在能够将此路由模块拆分成两个独立的虚拟模块。
import { shared } from "./shared";
export async function clientLoader() {
shared();
return await fetch("https://example.com/api").then((response) =>
response.json()
);
}
import { MassiveComponent } from "~/components";
import { shared } from "./shared";
export default function Component({ loaderData }) {
shared();
return <MassiveComponent data={loaderData} />;
}
如果您的项目对性能特别敏感,您可以将 unstable_splitRouteModules
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
- 修复了通过 #12800 在 7.1.4
中引入的回归问题,该问题导致使用懒加载路由发现 (patchRoutesOnNavigation
) 的应用在 splat 路由内部导航到 hash 路由时出错 (#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
- 正确处理单次 fetch 响应中不能包含响应体的状态码(如 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/数据文件中的不匹配问题 (#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
源码映射 (#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>
警告,该警告在懒加载路由发现场景下存在误报问题 (#12485)create-react-router
- 修复缺失的 fs-extra
依赖 (#12556)@react-router/dev
/@react-router/serve
- 为了与 React 19 兼容,在 NODE_ENV
尚未设置时正确初始化它 (#12578)@react-router/dev
- 移除 ServerRouter
中剩余/未使用的 abortDelay
prop,并更新默认的 entry.server.tsx
文件以使用用于 Single Fetch 的新 streamTimeout
值 (#12478)abortDelay
功能在 v7 中被移除,因为它与 Remix v2 的 defer
实现耦合,但漏掉了这个 prop 的移除entry.server
文件中使用此 prop,很可能你的应用程序没有按预期中止流,并且你需要采用随 Single Fetch 引入的新的 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 有对等依赖 (#12437)@react-router/dev
- 支持 moduleResolution
的 Node16
和 NodeNext
模式 (#12440)@react-router/dev
- 为子路由生成更宽泛的 matches
和 params
类型 (#12397)matches
包含子路由的 matches,params
包含子路由的路径参数matches
和 params
的类型完整更新日志: v7.0.1...v7.0.2
日期: 2024-11-22
@react-router/dev
- 确保当 Vite 开发服务器重启时,类型生成文件监听器被清理 (#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
包从 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
方法来polyfill fetch
APIreact@18
, react-dom@18
Remix 和 React Router 遵循一个API 开发策略,利用“未来标志”(Future Flags)来避免在主版本中引入大量破坏性更改。相反,破坏性更改会在次要版本中通过一个标志引入,允许用户根据需要选择启用。在下一个主版本中,所有未来标志行为都将成为默认行为。
以下先前标记的行为现在在 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
hook 所取代,因为它会传递 buildManifest
参数。如果需要,您仍然可以将构建清单(build manifest)写入磁盘,但您很可能会发现,将依赖构建清单的任何逻辑直接写入 buildEnd
hook 中更方便。(#11573)
如果您之前使用了 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;
暴露的 Router Promise
因为 React 19 将在一流支持渲染阶段处理 promise(通过 React.use
和 useAction
),我们现在可以放心地暴露那些以前返回 undefined
的 API 的 promise。
useNavigate()
useSubmit()
useFetcher().load
useFetcher().submit
routes.ts
// 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 Vite 插件时,路由在 app/routes.ts
中定义。路由配置通过 routes
导出进行导出,符合 RouteConfig
类型。提供了路由辅助函数 route
, index
, 和 layout
,以便更容易进行声明式类型安全的路由定义。
// app/routes.ts
import { type RouteConfig } from "@react-router/dev/routes";
import { flatRoutes } from "@react-router/fs-routes";
export const routes: RouteConfig = flatRoutes();
对于迁移到 React Router 的 Remix 用户,您仍然可以使用 @react-router/fs-routes
包在 routes.ts
中配置文件系统路由。一个重现默认 Remix 设置的最小路由配置如下所示:
// 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",
})),
];
如果您想从文件系统路由迁移到基于配置的路由,您可以通过将异步 flatRoutes
函数的结果展开到基于配置的路由数组中来混合使用这两种方法。
如果您之前使用了 Remix 的 routes
选项来使用替代的文件系统路由约定,您可以使用 @react-router/remix-config-routes-adapter
将这些适配到新的 RouteConfig
格式。
// 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 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 { 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"),
+ ]),
+];
routes
选项来定义基于配置的路由,您也可以使用 @react-router/remix-config-routes-adapter
并进行少量代码更改,将其适配到新的 RouteConfig
格式。虽然这提供了一条快速的迁移路径,但我们建议将 Remix 中任何基于配置的路由迁移到新的 RouteConfig
格式,因为这是一项相当直接的迁移。类型安全改进
React Router 现在会为您的每个路由模块生成类型,并将类型化 props 传递给路由模块的组件导出(#11961, #12019)。您可以通过从 ./+types/<route filename without extension>
导入来访问这些类型。
预渲染
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"];
}
prerender
配置,用于支持 SSG(静态站点生成)用例。这会在构建时预渲染您的 .html
和 .data
文件,因此您可以在运行时从运行的服务器或 CDN 静态地提供它们(#11539)react-router
)defer
实现,转而通过 single fetch 和 turbo-stream
使用原始的 promise(#11744)defer
这将从 React Router 中移除以下导出:
AbortedDeferredError
type TypedDeferredData
UNSAFE_DeferredData
将包合并到 react-router
中(#11505)
@remix-run/router
react-router-dom
@remix-run/server-runtime
react-router-dom
包得以保留是为了方便迁移,但它只是简单地重新导出了 react-router
中的所有 API。future.v7_normalizeFormMethod
未来标志(#11697)@remix-run/router
公共 API 的导出:AgnosticDataIndexRouteObject
AgnosticDataNonIndexRouteObject
AgnosticDataRouteMatch
AgnosticDataRouteObject
AgnosticIndexRouteObject
AgnosticNonIndexRouteObject
AgnosticRouteMatch
AgnosticRouteObject
TrackedPromise
unstable_AgnosticPatchRoutesOnMissFunction
Action
-> 作为 NavigationType
通过 react-router
导出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
导出的以下内容Hash
Pathname
Search
@remix-run/router
包中移除 future.v7_prependBasename
(#11726)@remix-run/router
包中移除 future.v7_throwAbortReason
(#11728)exports
字段 (#11675)RemixContext
重命名为 FrameworkContext
(#11705)PrefetchPageDescriptor
被 PageLinkDescriptor
取代 (#11960)future.v7_partialHydration
标志 (#11725)<RouterProvider fallbackElement>
属性fallbackElement
移动到您的根路由上的 hydrateFallbackElement
/HydrateFallback
future.v7_partialHydration
时(使用 fallbackElement
时),state.navigation
在初始加载期间会被填充future.v7_partialHydration
时,state.navigation
在初始加载期间保持 "idle"
状态future.v7_relativeSplatPath
未来标志 (#11695)v7_skipActionErrorRevalidation
v3_fetcherPersist
, v3_relativeSplatPath
, v3_throwAbortReason
createRemixStub
重命名为 createRoutesStub
(#11692)@remix-run/router
已弃用的 detectErrorBoundary
选项,推荐使用 mapRouteProperties
(#11751)react-router/dom
子路径导出,以正确地将 react-dom
作为可选的对等依赖 (peerDependency
) 启用 (#11851)<RouterProvider>
中盲目地 import ReactDOM from "react-dom"
来访问 ReactDOM.flushSync()
,因为这会破坏 createMemoryRouter
在非 DOM 环境中的用例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
- 对于迁移到 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
- 对于迁移到 React Router 并使用 Vite 插件的 buildEnd
钩子的 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
升级到 v5,这独立于 React Router v7 升级package.json
中有 isbot@3
并且你的仓库中没有自己的 entry.server.tsx
文件package.json
中的 isbot
升级到 v5@react-router/dev
- 对于迁移到 React Router 的 Remix 用户,Vite 清单文件(即 .vite/manifest.json
)现在被写入每个构建子目录中,例如 build/client/.vite/manifest.json
和 build/server/.vite/manifest.json
,而不是 build/.vite/client-manifest.json
和 build/.vite/server-manifest.json
。这意味着构建输出现在更接近于一个典型的 Vite 项目的预期。(#11573)build/.vite
目录中,以避免在生产环境中意外地提供这些文件,特别是来自客户端构建的文件。后来,通过附加逻辑进行了改进,除非在应用的 Vite 配置中启用了 Vite 的 build.manifest
选项,否则会在构建过程结束时删除这些 Vite 清单文件。这大大降低了在生产环境中意外提供 Vite 清单文件的风险,因为它们只有在明确请求时才会存在。因此,我们现在可以假设用户知道他们需要自己管理这些附加文件,并且 React Router 可以安全地生成更标准的 Vite 构建输出。react-router
- 将 Params、loader 数据和 action 数据作为 route 组件导出的 props 提供。(#11961)react-router
- 添加 route 模块类型生成。(#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
文件夹中的资源文件像以前一样提供服务,并带有 1 年不可变的 Cache-Control
头部.html
和 .data
文件,不会带有一个特定的 Cache-Control
头部提供服务.data
文件会带 Content-Type: text/x-turbo
头部提供服务express.static
添加此设置时,它似乎还会为 .data
文件添加 Cache-Control: public, max-age=0
头部substr
替换为 substring
。(#12080)react-router
- 修复使用 data()
从 loaders/actions 返回的重定向。(#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
fetcherKey
添加为 patchRoutesOnNavigation
的参数。(#13109)6.29.0
中引入的回归问题,该问题导致在使用 Lazy Route Discovery (patchRoutesOnNavigation
) 的应用程序中,导航到 splat routes 内部的 hash routes 时出现问题 (#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
key
时,若未选择 future.v7_fetcherPersist
会出现的用法问题 (#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
hook (#11989)unstable_dataStrategy
(#11974)unstable_patchRoutesOnNavigation
(#11973)PatchRoutesOnNavigationFunctionArgs
类型以提高便利性 (#11967)?index
参数而导致的错误 (#12003)useFormAction
错误 - 当移除 ?index
参数时,它不会保留其他非 Remix 的 index
参数 (#12003)preventScrollReset
的错误 (#11999)console.error
(#12050)partialHydration
的错误 (#12070)patchRoutesOnNavigation
调用引起的问题 (#12055)unstable_
API 中的此行为,这可能是个破坏性变更patchRoutesOnNavigation
调用,这样具有相同起点/终点的多次导航只会执行一次函数并使用同一个 promisepatch
在导航被中断(并且 request.signal
被中止)时短路的行为相悖,因为第一次调用的 patch 会成为空操作(no-op)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 的重大变更(breaking changes)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
逻辑,以便在匹配具有动态参数(dynamic param)或 splat 段(splat segments)的路由时调用该方法,以防存在我们尚未发现的更高优先级的静态路由 (#11883)unstable_patchRoutesOnNavigation
的先前路径,这样在后续导航到相同路径时就不会重复调用完整更新日志: v6.26.0...v6.26.1
日期: 2024-08-01
replace(url, init?)
API 作为 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
现在将包含所有 partial matches,以便我们可以渲染祖先 HydrateFallback
组件完整更新日志: v6.25.1...v6.26.0
日期: 2024-07-17
RouterProvider
的部分内部实现进行 memoization,以减少不必要的重新渲染 (#11803)完整更新日志: v6.25.0...v6.25.1
日期: 2024-07-16
v7_skipActionErrorRevalidation
稳定化此版本将 future.unstable_skipActionErrorRevalidation
标志稳定化为 future.v7_skipActionErrorRevalidation
,为即将到来的 React Router v7 版本做准备。
4xx/5xx
`Response` 时,默认情况下不会触发重新验证(revalidation)shouldRevalidate
的 unstable_actionStatus
参数稳定化为 actionStatus
future.unstable_skipActionErrorRevalidation
稳定化为 future.v7_skipActionErrorRevalidation
(#11769)useMatch
中的路径,以便 matches/params 反映解码后的参数 (#11789)unstable_patchRoutesOnMiss
抛出的错误的冒泡问题 (#11786)unstable_patchRoutesOnMiss
且在服务器端匹配到 splat 路由时的 hydration 问题 (#11790)完整更新日志: v6.24.1...v6.25.0
日期: 2024-07-03
polyfill.io
的引用,因为该域名已被出售,并且后来被确定服务于恶意软件 (#11741)
NavLinkRenderProps
类型,以便更轻松地为自定义 NavLink
回调函数进行类型标注 (#11553)future.v7_relativeSplatPath
时,正确解析无路径路由的子级 splat 路由中的相对路径 (#11633)router.routes
身份/重排 (#11740)完整更新日志: v6.24.0...v6.24.1
日期: 2024-06-24
我们非常高兴在 v6.24.0
中发布全新的“懒加载路由发现”API!有关背景信息,请查看原始的 RFC。**简而言之**:自从我们在 v6.4 中通过 <RouterProvider>
引入 Data APIs 以来,我们一直有些沮丧,因为一个权衡点是缺乏一个引人注目的代码分割方案,无法像在 <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
的导入改回 react-router-dom
,而不是 index.ts
(#11514)@remix-run/router
- 支持在 staticHandler.queryRoute
上使用 unstable_dataStrategy
(#11515)完整更新日志: v6.23.0...v6.23.1
日期: 2024-04-23
新的 unstable_dataStrategy
API 是一个低级 API,专为需要控制 loader
/action
函数数据策略的高级用例而设计。默认实现是当前的行为,即并行获取所有 loader,但此选项允许用户实现更高级的数据流,包括 Remix 的“Single Fetch”、用户空间(user-land)的中间件/上下文 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
来实现 - shouldRevalidate
现在也接收一个新的参数 unstable_actionStatus
(与 actionResult
一起),以便您可以根据 action
响应的状态做出决定,而无需将其编码到 action 数据中。
unstable_dataStrategy
(#11098, #11377)@remix-run/router
- 添加一个新的未来标志 future.unstable_skipActionRevalidation
(#11098)@remix-run/router
- SSR:在 staticHandler.query
方法中添加了一个新的选项 skipLoaderErrorBubbling
,用于禁用静态处理器的错误冒泡,以便在 Remix 的 Single Fetch 实现中使用 (#11098, (#11377))完整更新日志: v6.22.3...v6.23.0
日期: 2024-03-07
future.v7_partialHydration
的一个 bug,该 bug 会在水合(hydration)时,如果 SSR loader 错误冒泡到父级边界,则重新运行边界下方的 loader (#11324)future.v7_partialHydration
的一个 bug,如果路由没有 loader,该 bug 会将路由器视为未初始化 (#11325)完整更新日志: v6.22.2...v6.22.3
日期: 2024-02-28
完整更新日志: v6.22.1...v6.22.2
日期: 2024-02-16
完整更新日志: v6.22.0...v6.22.1
日期: 2024-02-01
2021 年,HTTP Archive 推出了 核心 Web 指标技术报告仪表板
通过结合 Chrome UX Report 26 (CrUX) 数据集中的真实用户体验数据与 HTTP Archive 30 中的网络技术检测数据,我们可以大致了解像 CMS 平台或 JavaScript 框架选择等架构决策如何影响网站的 CWV 性能。
他们使用一个名为 wappalyzer
的工具,通过查找特定的脚本、全局 JS 变量或其他识别特征来识别给定网站正在使用的技术。例如,对于 Remix 应用,他们会 查找全局的 __remixContext
变量来识别网站是否正在使用 Remix。
我们注意到 React Router 无法可靠地识别,因为它没有可识别的全局特性。他们目前正在 寻找名称中包含 react-router
的外部脚本。这将识别使用 CDN(例如 unpkg
)的 React Router 网站,但会遗漏绝大多数从 npm registry 安装 React Router 并将其打包到 JS 文件中的网站。这导致在 web 上 大幅度低估 了 React Router 的使用量。
从 6.22.0
版本开始,使用 react-router-dom
的网站将开始添加一个 window.__reactRouterVersion
变量,其值将被设置为 SemVer 主版本号的字符串值(即 window.__reactRouterVersion = "6";
),以便它们能够被正确识别。
window.__reactRouterVersion
以供 CWV 报告检测 (#11222)createStaticHandler
的 future.v7_throwAbortReason
future 标志,以便在请求中止时抛出 request.signal.reason
(默认为 DOMException
),而不是诸如 new Error("query() call aborted: GET /path")
这样的 Error
(#11104)DOMException
是在 Node v17 中添加的,因此在 Node 16 及以下版本中您将无法获得 DOMException
。getStaticContextFormError
,则遵循 ErrorResponse
的状态码 (#11213)完整更新日志: v6.21.3...v6.22.0
日期: 2024-01-18
basename
时 NavLink
的 isPending
问题 (#11195)Blocker
/BlockerFunction
类型中遗留的 unstable_
前缀 (#11187)完整更新日志: v6.21.2...v6.21.3
日期: 2024-01-11
完整更新日志: v6.21.1...v6.21.2
日期: 2023-12-21
v7_partialHydration
时,在初始 SPA 加载期间 route.lazy
无法正常工作的 bug (#11121)resolveTo
中对相对路径逻辑进行去重 (#11097)完整更新日志: v6.21.0...v6.21.1
日期: 2023-12-13
future.v7_relativeSplatPath
我们在 6.19.0 中修复了一个动态路径路由的路径解析 bug,但后来发现大量应用依赖于该错误行为,因此我们在 6.20.1 中恢复了原状(参见 #10983, #11052, #11078)。
该错误行为是指在动态路径路由中解析相对路径时,默认行为会忽略当前路由路径中的任何动态部分(*
)。当 future 标志启用时,动态部分会包含在动态路径路由内的相对路径逻辑中。
更多信息请参阅 useResolvedPath
文档和/或 详细的更新日志条目。
我们为 @remix-run/router
添加了一个新的 future.v7_partialHydration
future 标志,它在服务器端渲染 (SSR) 时启用数据路由器的部分注水。这允许您提供包含部分初始匹配的路由加载器值(但不是全部)的 hydrationData.loaderData
。当启用此标志时,路由器将在 router.initialize()
期间调用没有注水加载器数据的路由的 loader
函数,并且在执行未注水路由时,它将渲染到提供的最深的 HydrateFallback
(直到第一个没有注水数据的路由)。(#11033)
future.v7_relativeSplatPath
标志,用于实现对 splat 路由内相对路由的破坏性错误修复。(#11087)future.v7_partialHydration
未来标志,用于在服务器端渲染时启用数据路由器的部分注水 (#11033)ErrorBoundary
中的假值错误值 (#11071)loader
/action
函数响应时抛出的错误 (#11061)Link
/NavLink
时 relative="path"
的问题 (#11062)完整更新日志:v6.20.1...v6.21.0
日期:2023-12-01
useResolvedPath
修复,因为大量应用依赖于该错误行为(见 #11052)(#11078)6.19.0
和 6.20.0
中。如果您从 6.18.0
或更早版本升级,则不会受到此修复的影响。完整更新日志:v6.20.0...v6.20.1
日期:2023-11-22
[!警告] 请使用版本
6.20.1
或更高版本,而不是6.20.0
。我们发现大量应用依赖于在此版本中修复的错误行为(#11045)。我们在6.20.1
中回滚了该修复,并将在后续版本中将其作为未来标志重新引入。详见 #11052。
PathParam
类型 (#10719)v7_fetcherPersist
启用时,不要重新验证已卸载的 fetcher (#11044)resolveTo
路径解析的错误 (#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
中回滚了该修复,并将在后续版本中将其作为未来标志重新引入。详见 #11052。
unstable_flushSync
API此版本为命令式 API(useSubmit
、useNavigate
、fetcher.submit
、fetcher.load
)带来了新的 unstable_flushSync
选项,允许用户选择同步 DOM 更新以实现挂起/乐观 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
hook 的 unstable_
前缀,因为它已经使用了足够长的时间,我们对 API 充满信心 (#10991)unstable_usePrompt
中移除前缀,因为浏览器处理 window.confirm
的方式存在差异,这使得 React Router 无法保证一致/正确的行为修复 useActionData
,使其返回正确的上下文 action 数据,而不是树中的任何 action 数据 (#11023)
修复 useResolvedPath
中的错误,该错误会导致 splat 路由中 useResolvedPath(".")
丢失 URL 路径的 splat 部分。(#10983)
"."
路径,它错误地丢弃了 URL 的 splat 部分。如果您在应用中通过 splat 路由内的 "."
进行相对路由,您应该仔细检查您的逻辑是否依赖于此错误行为,并进行相应更新。修复已挂载的 useFetcher
中,fetcher 的 key
更改后未被拾取的问题 (#11009)
修复 useFormAction
错误地继承了子路由 action
提交的 ?index
查询参数的问题 (#11025)
修复当 to
位置带有尾随斜杠时 NavLink
的 active
逻辑问题 (#10734)
修复类型,使 unstable_usePrompt
除了接受 boolean
外,还可以接受 BlockerFunction
(#10991)
修复 relative="path"
错误,该错误导致相对路径计算从完整的 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 上暴露了 fetcher keys,这样就可以通过 key
进行查找Form
和 useSubmit
现在支持可选的 navigate
/fetcherKey
props/params,以便在底层使用可选的用户指定 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 更加容易。
useFetchers()
API 始终只应该反映处于 in-flight 状态的 fetcher 信息,用于 pending/optimistic UI——它并不打算反映 fetcher 数据或在 fetcher 返回到 idle
状态后仍然保留它们useFetchers()
中 - 因为您可以通过 useFetcher().data
访问数据,它们在那里没有其他作用idle
状态后才被清理useFetchers
暴露,这样您在卸载后仍然可以访问 pending/optimistic 数据key
重新挂载了 fetcher,那么它的结果将会被处理,即使原始的 fetcher 已经卸载key
APIs 和 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 应用中启用视图过渡的最简单方法是通过新的 <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>
有关视图过渡的示例用法,请查看我们基于出色的 Astro Records 演示的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
实例。这使我们能够导出一个通过 InstanceType
关联到该类实例的 type ErrorResponse
。用户代码应该只将 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 的 Firefox Web 扩展的边界情况 (#10620)unstable_usePrompt
中的 effect,以避免在提示被解除阻塞并同步执行导航时抛出异常 (#10687, #10718)useFormAction()
中不包含 hash,因为它无法在服务器上确定并导致水合问题 (#10758)queryRoute
中的一个问题,该问题是并非总能识别抛出的 Response
实例 (#10717)react-router-native
: 更新 @ungap/url-search-params
依赖从 ^0.1.4
到 ^0.2.2
(#10590)完整更新日志: v6.14.2...v6.15.0
日期: 2023-07-17
<Form state>
属性,用于在提交导航时填充 history.state
(#10630)defer
promise 以 undefined
resolve/reject,则触发一个错误,以便与 loader 和 action 的行为保持一致(它们必须返回一个值或 null
) (#10690)<ScrollRestoration>
模拟 hash 滚动时,正确解码元素 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/text 提交内容。(#10413)submitter
元素提交表单时,在现代浏览器(支持新的 submitter
参数的浏览器)中优先使用内置的 new FormData(form, submitter)
,而不是之前的手动方法。(#9865)type="image"
按钮添加了基本支持。formdata-submitter-polyfill
。window.history.pushState/replaceState
(而不是之后),以便在同步的 React 17 渲染期间 window.location
与 useLocation
匹配。(#10448)window.location
,应尽可能始终引用 useLocation
,因为 window.location
不会始终 100% 同步(由于 popstate
事件、并发模式等)。shouldRevalidate
。(#10623)<ScrollRestoration getKey>
的 location
中剥离 basename
,以匹配 useLocation
的行为。(#10550)unstable_useBlocker
函数的 location 中剥离 basename
,以匹配 useLocation
的行为。(#10573)StrictMode
中 unstable_useBlocker
的 key 问题。(#10573)0
参数时 generatePath
的问题。(#10612)tsc --skipLibCheck:false
的问题。(#10622)typescript
升级到 5.1。(#10581)完整更新日志:v6.13.0...v6.14.0
日期:2023-06-14
从本质上讲,6.13.0
是一个补丁版本,但由于我们添加了一个新的 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
中,为 Suspense 组件添加了更好的支持。这意味着,例如,如果目标路由中的某个组件发生 Suspense,并且您没有提供 Suspense
边界来显示回退内容,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
<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
)不再重新运行 loader,以匹配浏览器行为 (#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
在位置变化时现在是稳定的,因为我们可以通过 @remix-run/router
实例处理相对路由,并摆脱对 useLocation()
的依赖 (#10336)BrowserRouter
时,这些 hook 在位置变化时仍然不稳定,因为它们仍然依赖于 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
以检查路由上的 ErrorBoundary
以及 errorElement
(#10190)createRoutesFromElements
中使用 Fragments 时路由 ID 的生成 (#10193)shouldRevalidate
提供 fetcher 提交数据 (#10208)lazy()
错误 (#10201)DeferredData
的 instanceof
检查,以在 SSR 打包场景中应对 ESM/CJS 边界 (#10247)@remix-run/web-fetch@4.3.3
(#10216)完整更新日志: v6.9.0...v6.10.0
日期: 2023-03-10
Component
/ErrorBoundary
路由属性React Router 现在支持一种替代方式,将路由的 element
和 errorElement
字段定义为 React 组件,而不是 React 元素。你可以选择将 React 组件传递给新的 Component
和 ErrorBoundary
字段。两者之间没有功能上的区别,所以你可以选择你喜欢的方法 😀。你不应该同时定义两者,但如果你这样做了,则 Component
/ErrorBoundary
会“胜出”
JSON 语法示例
// Both of these work the same:
const elementRoutes = [{
path: '/',
element: <Home />,
errorElement: <HomeError />,
}]
const componentRoutes = [{
path: '/',
Component: Home,
ErrorBoundary: HomeError,
}]
function Home() { ... }
function HomeError() { ... }
JSX 语法示例
// Both of these work the same:
const elementRoutes = createRoutesFromElements(
<Route path='/' element={<Home />} errorElement={<HomeError /> } />
);
const componentRoutes = createRoutesFromElements(
<Route path='/' Component={Home} ErrorBoundary={HomeError} />
);
function Home() { ... }
function HomeError() { ... }
为了保持应用打包体积小并支持路由的代码分割,我们引入了一个新的 lazy()
路由属性。这是一个异步函数,用于解析路由定义中非路由匹配的部分(loader
, action
, element
/Component
, errorElement
/ErrorBoundary
, shouldRevalidate
, handle
)。
惰性路由在初始加载以及导航或 fetcher 调用的 loading
或 submitting
阶段被解析。你不能惰性定义路由匹配属性(path
, index
, children
),因为我们只在匹配已知路由后执行你的惰性路由函数。
你的 lazy
函数通常会返回动态导入的结果。
// In this example, we assume most folks land on the homepage so we include that
// in our critical-path bundle, but then we lazily load modules for /a and /b so
// they don't load until the user navigates to those routes
let routes = createRoutesFromElements(
<Route path="/" element={<Layout />}>
<Route index element={<Home />} />
<Route path="a" lazy={() => import("./a")} />
<Route path="b" lazy={() => import("./b")} />
</Route>
);
然后在你的惰性路由模块中,导出你想为该路由定义的属性
export async function loader({ request }) {
let data = await fetchData(request);
return json(data);
}
// Export a `Component` directly instead of needing to create a React Element from it
export function Component() {
let data = useLoaderData();
return (
<>
<h1>You made it!</h1>
<p>{data}</p>
</>
);
}
// Export an `ErrorBoundary` directly instead of needing to create a React Element from it
export function ErrorBoundary() {
let error = useRouteError();
return isRouteErrorResponse(error) ? (
<h1>
{error.status} {error.statusText}
</h1>
) : (
<h1>{error.message || error}</h1>
);
}
可以在仓库的 examples/lazy-loading-router-provider
目录中找到一个实际示例。更多信息,请查看 lazy
文档。
🙌 感谢 @rossipedia 在 初步提案 和 POC 实现 方面做出的巨大贡献。
generatePath
错误地应用参数的问题 (#10078)[react-router-dom-v5-compat]
添加遗漏的 data router 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 中使用 X-Remix-Revalidate
头设置 cookie)下被调用。在适用于导航 loader
重新验证的隐式重新验证场景(如搜索参数更改或点击当前页面链接)下未正确调用。现在已在这些额外场景下正确调用。current*
/next*
参数反映的是静态的 fetcher.load
URL(因此它们是相同的)。相反,它们应该反映触发重新验证的导航(就像 form*
参数那样)。现在这些参数已正确反映触发的导航。useSearchParams
移除搜索参数的 bug (#9969)<fetcher.Form>
上遵守 preventScrollReset
属性 (#9963)<ScrollRestoration>
使用 pagehide
事件代替 beforeunload
事件。这有更好的跨浏览器支持,特别是在移动端 Safari 上。 (#9945)isRouteErrorResponse
中的 instanceof
检查,以避免服务端的打包问题 (#9930)defer
调用何时只包含关键数据并移除 AbortController
(#9965)File
类型的 FormData
条目进行 URL 编码时,将名称作为值发送 (#9867)react-router-dom-v5-compat
- 修复使用 CompatRouter
时 SSR useLayoutEffect
导致 console.error
的问题 (#9820)完整更新日志: v6.7.0...v6.8.0
日期: 2023-01-18
unstable_useBlocker
/unstable_usePrompt
钩子,用于阻止应用 location origin 内部的导航 (#9709, #9932)<Form>
添加 preventScrollReset
属性 (#9886)useBeforeUnload
添加透传事件监听器选项参数 (#9709)generatePath
在存在可选参数时的问题 (#9764)<Await>
以接受 ReactNode
作为 children 函数的返回结果 (#9896)jsdom
bug 的变通方法 (#9824)完整更新日志: v6.6.2...v6.7.0
日期: 2023-01-09
useId
的一致性 (#9805)完整更新日志: v6.6.1...v6.6.2
日期: 2022-12-23
完整更新日志: v6.6.0...v6.6.1
日期: 2022-12-21
此次次要版本发布主要是为了稳定数据路由的 SSR
API,因为作为 React Router-ing Remix 工作的一部分,我们已在 Remix 中连接了新的 RouterProvider
。
createStaticHandler
/createStaticRouter
/StaticRouterProvider
的 unstable_
前缀 (#9738)useBeforeUnload()
钩子 (#9664)<Form method>
和 useSubmit
method 值 (#9664)<button formmethod>
表单提交覆盖问题 (#9664)replace
操作以及提交到新路径时的 PUSH
操作问题 (#9734)errorElement
中使用 useLoaderData
(#9735)StaticRouterProvider
正确水合 Error
对象 (#9664)hydrationData
的 SSR 应用,跳过初始滚动恢复 (#9664)完整更新日志: v6.5.0...v6.6.0
日期: 2022-12-16
此版本引入了对 可选路由段 的支持。现在,在任何路径段的末尾添加 ?
将使整个段成为可选的。这适用于静态段和动态参数。
可选参数示例
<Route path=":lang?/about>
将匹配/:lang/about
/about
<Route path="/multistep/:widget1?/widget2?/widget3?">
将匹配/multistep
/multistep/:widget1
/multistep/:widget1/:widget2
/multistep/:widget1/:widget2/:widget3
可选静态片段示例
<Route path="/home?">
将匹配/
/home
<Route path="/fr?/about">
将匹配/about
/fr/about
<Route path="prefix-:param">
,以与 splat 参数的工作方式对齐。如果你之前依赖了此行为,则建议在使用 useParams
调用时提取路径的静态部分:(#9506)// Old behavior at URL /prefix-123
<Route path="prefix-:id" element={<Comp /> }>
function Comp() {
let params = useParams(); // { id: '123' }
let id = params.id; // "123"
...
}
// New behavior at URL /prefix-123
<Route path=":id" element={<Comp /> }>
function Comp() {
let params = useParams(); // { id: 'prefix-123' }
let id = params.id.replace(/^prefix-/, ''); // "123"
...
}
完整更新日志: v6.4.5...v6.5.0
Date: 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
Date: 2022-11-30
action
/loader
函数返回 undefined
,则抛出错误,因为 revalidation 需要知道 loader 是否之前已执行过。undefined
还会导致 SSR stringification 过程中出现 hydration 问题。你应始终确保你的 loader
/action
返回一个值,如果你不想返回任何内容,可以返回 null
。(#9511)basename
(#9591)ErrorResponse
的 body,以便在内部 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
Date: 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
Date: 2022-10-06
useFormAction
中的 basename
(#9352)IndexRouteObject
和 NonIndexRouteObject
类型,使 hasErrorElement
成为可选 (#9394)RouteObject
/RouteProps
类型,以便在 TypeScript 中提示此错误。(#9366)完整更新日志: v6.4.1...v6.4.2
Date: 2022-09-22
完整更新日志: v6.4.0...v6.4.1
Date: 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>
组件内部返回作用域内的位置 (#9094)<Link replace>
prop,则遵守该设置 (#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
prop。这个值会传递给子路由,并且可以通过新的 useOutletContext
hook 访问。详情请参阅 API 文档。 (#8461)<NavLink>
现在可以接收一个子函数来访问其 props。 (#8164)useMatch
和 matchPath
的 TypeScript 签名。例如,当你调用 useMatch("foo/:bar/:baz")
时,路径会被解析,返回类型将是 PathMatch<"bar" | "baz">
。 (#8030)完整变更日志: v6.0.2...v6.1.0
日期: 2021-11-09
<Link>
添加了 reloadDocument
prop。这使得 <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 升级的说明。