更新日志
本页内容

React Router 发布版本

此页面列出了 React Router 自 v6.0.0 以来的所有版本/发布说明。对于 v6 之前的版本,请参阅 Github 发布页面

我们在此文件而不是 GitHub 的分页发布页面中管理发布说明,原因有两点:

  • GitHub UI 中的分页意味着您无法轻松一次性搜索大范围版本段的发布说明
  • GitHub 分页界面在列表视图中还会截断较长的发布说明,且没有提示,您需要点击进入详细视图才能看到完整的发布说明
目录

v7.5.1

日期:2025-04-17

补丁更新

  • react-router - 使用基于对象的 route.lazy API 时,在同构后懒加载路由时,将跳过 HydrateFallbackhydrateFallbackElement 属性 (#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)

    • 这是 Remix v2 的一个实现细节,意外地保留在了 React Router v7 中
    • 现在我们允许 loader 返回 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

v7.5.0

日期: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_middlewareroute.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 标志,启用生成带有 integrityimportmap,用于浏览器将加载的脚本 (#13163)
  • react-router - 移除对 route.unstable_lazyMiddleware 属性的支持 (#13294)
    • 为了懒加载中间件,您可以使用新的基于对象的 route.lazy.unstable_middleware API
  • @react-router/dev - 启用 future.unstable_viteEnvironmentApi 时,确保在配置了自定义 Vite base 的情况下,开发环境中的关键 CSS 生效 (#13305)

按包划分的更新

完整更新日志: v7.4.1...v7.5.0

v7.4.1

日期:2025-03-28

安全公告

修复了一个安全漏洞,该漏洞由于端口净化不足,允许通过 HostX-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)
    • ⚠️ 我们目前不建议采用此 API,因为我们很可能在中间件稳定版本发布前对其进行更改
    • ⚠️ 如果你的应用目前从 route.lazy 返回了 unstable_middleware,这可能是一个破坏性变更
    • route.unstable_middleware 属性在 route.lazy 的返回值中不再受支持
    • 如果你想延迟加载中间件,你必须使用 route.unstable_lazyMiddleware
  • @react-router/dev - 当 future.unstable_middlewarefuture.unstable_splitRouteModules 都启用时,在可能的情况下,将 unstable_clientMiddleware 路由导出拆分成单独的块 (#13210)
  • @react-router/dev - 通过确保只有在定义了 unstable_clientMiddleware 时,路由模块才会在中间件阶段阻塞,从而提升 future.unstable_middleware 的性能 (#13210)

完整变更日志: v7.4.0...v7.4.1

v7.4.0

日期: 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 - 修复当中间件启用时 RequestHandlerloadContext 参数类型 (#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)

按包分类的变更

完整变更日志: v7.3.0...v7.4.0

v7.3.0

日期: 2025-03-06

小幅变更

  • 添加 fetcherKey 作为 patchRoutesOnNavigation 的参数 (#13061)

补丁变更

  • react-router - 检测并处理在活动会话期间进行新部署时的 manifest 偏移问题 (#13061)
    • 在框架模式下,新部署后的活动会话中,延迟路由发现现在将检测到 manifest 版本不匹配
    • 导航到未发现的路由时,这种不匹配将触发目标路径的文档重新加载
    • fetcher 调用未发现的路由时,这种不匹配将触发当前路径的文档重新加载
  • react-router - 在 SPA 模式下,跳过开发服务器中的资源路由流程 (#13113)
  • react-router - 修复使用 basename 时单次 fetch _root.data 请求的问题 (#12898)
  • react-router - 修复包含 Record 类型的 loaderDataactionData 的类型 (#13139)
    • ⚠️ 对于已经采用了 unstable_SerializesTo 的用户,这是一个破坏性变更 - 更多信息请参阅下方“不稳定特性变更”部分中的说明
  • @react-router/dev - 修复对自定义客户端 build.rollupOptions.output.entryFileNames 的支持 (#13098)
  • @react-router/dev - 修复当 serverBundles 选项已配置或由预设提供时 (例如来自 @vercel/react-routervercelPreset) 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_viteEnvironmentApiserverBundles 选项的用户,服务器 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.entriesoptimizeDeps.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 (待稳定)

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/设计 的更多信息,请参阅 决策文档

Middleware 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>;

然而,这破坏了 loaderDataactionData 对任何 Record 类型的类型推断,因为它们现在会(错误地)匹配 unstable_SerializesTo。这影响了所有用户,而不仅仅是依赖 unstable_SerializesTo 的用户。为了修复此问题,unstable_SerializesTo 的品牌属性被标记为必需而非可选。

对于使用 unstable_SerializesTo 的库和框架作者,在转换为 unstable_SerializesTo 之前,您可能需要添加 as unknown 类型转换。

按包分类的变更

完整变更日志: v7.2.0...v7.3.0

v7.2.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!

带 SPA 回退的预渲染

此版本增强了在使用 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 中,你可以将其用于服务/水合未预渲染的路径。

更多信息,请参阅预渲染 文档。

在 SPA 模式下允许根 loader

SPA 模式过去禁止在所有路由中使用 loader,以便我们可以在应用程序中的任何路径进行水合。然而,由于根路由总是在构建时渲染,我们可以解除对根路由的此限制。

为了在预渲染期间使用你的构建时 loader 数据,我们现在也将 loaderData 作为路由上 HydrateFallback 组件的可选属性暴露出来。

  • 只要 HydrateFallback 正在渲染(因为 路由正在加载),此属性将是已定义的。
  • 如果 HydrateFallback 正在渲染(因为路由本身有自己的水合 clientLoader),这将是 undefined
    • 在 SPA 模式下,这将允许你将 loader 根数据渲染到 SPA 模式的 HTML 文件中。

次要变更

  • 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)
    • 我们之前仅在 "SPA 模式" 下禁用此功能,但我们意识到它应该应用于所有 ssr:false 应用程序。
    • 在那些 prerender 场景中,我们会预渲染 /__manifest 文件,但这会产生一些关于静态文件服务器行为的不必要的假设。
  • react-router - 在 SPA 模式下不应用单次抓取重新验证去优化,因为没有服务器 HTTP 请求 (#12948)
  • react-router - 正确处理跨越预渲染/SPA 边界的重新验证 (#13021)
    • 在 "混合式" 应用程序中(其中一些路由是预渲染的,另一些则由 SPA 回退提供服务),如果路径未被预渲染,我们需要避免发出 .data 请求,因为请求会返回 404。
    • 然而,我们在客户端不知道所有预渲染的路径。
      • ssr:false 模式下,所有 loader 数据都是静态的,因为它是在构建时生成的。
      • 路由必须使用 clientLoader 来执行任何动态操作。
      • 因此,如果一个路由只有 loader 而没有 clientLoader,我们默认禁用重新验证,因为没有新的数据可供检索。
      • 如果在我们的单次抓取 dataStrategy 中没有带有 shouldLoad=true 的服务端 loader,我们就会短路并跳过单次抓取 .data 请求逻辑。
      • 这确保了该路由不会在提交后导致会返回 404 的 .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:falseprerender 配置时,只有与 prerender 路径匹配的路由可以有 loader
  • @react-router/dev - 在 ssr:false + prerender 应用程序中,针对以下边缘情况在构建时报错: (#13021)
    • 父路由只有 loader (没有 clientLoader)
    • 该父路由已预渲染
    • 该父路由有子路由,这些子路由未被预渲染
    • 这意味着当通过 SPA 回退加载子路径时,父路由不会有任何 loaderData,因为没有服务器来运行 loader
    • 这可以通过添加父路由的 clientLoader 或预渲染子路径来解决。
    • 如果你添加了 clientLoader,在未预渲染的路径上调用 serverLoader() 将抛出 404 错误。
  • @react-router/dev - 限制预渲染的资源路由 .data 文件仅包含目标路由 (#13004)
  • @react-router/dev - 修复二进制文件的预渲染问题 (#13039)
  • @react-router/dev - 修复重复参数的类型生成问题 (#13012)
    • 在 React Router 中,路径参数通过其名称作为键,因此对于像 /a/:id/b/:id?/c/:id 这样的路径模式,最后一个 :id 将设置 useParamsparams 属性中 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 的便利之处之一在于,路由所需的一切都在单个文件中。不幸的是,在使用 clientLoaderclientActionHydrateFallback 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 导出,而默认组件导出则大得多。这对性能来说是一个问题,因为这意味着如果我们想在客户端导航到此路由,则必须在客户端加载器开始运行之前下载整个路由模块。

为了将此可视化为时间线

在以下时间线图表中,路由模块条内部使用了不同的字符来表示导出的不同路由模块 API。

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 时,此优化效果更加显著。例如,在使用 clientLoaderclientActionHydrateFallback 时,单个路由模块在客户端导航期间的时间线可能如下所示:

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.

按包划分的变更

完整更新日志: v7.1.5...v7.2.0

v7.1.5

日期:2025-01-31

补丁变更

  • react-router - 修复了通过 #128007.1.4 中引入的回归问题,该问题导致使用懒加载路由发现 (patchRoutesOnNavigation) 的应用在 splat 路由内部导航到 hash 路由时出错 (#12927)。

完整更新日志: v7.1.4...v7.1.5

v7.1.4

日期: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/plainapplication/json 响应 (#12848)。
    • 这仅在作为资源路由访问且没有 .data 扩展名时适用。
    • 通过单次 Fetch 的 .data 请求访问时,它们仍将通过 turbo-stream 编码。
  • react-router - 优化懒加载路由发现的路径发现,倾向于在 body 级别进行单次 querySelectorAll 调用,而非在子树级别进行多次调用 (#12731)。
  • react-router - 通过在可能的情况下跳过冗余的 matchRoutes 调用来优化路由匹配 (#12800, #12882)。
  • react-router - 内部重组以清理一些重复的路由模块类型 (#12799)。

完整更新日志: v7.1.3...v7.1.4

v7.1.3

日期:2025-01-17

补丁变更

  • @react-router/dev - 修复了 revealroutes CLI 命令 (#12745)。

完整更新日志: v7.1.2...v7.1.3

v7.1.2

日期: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)
    • 这修复了使用某些 npm 包时的解析问题
  • @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

v7.1.1

日期: 2024-12-23

补丁更新

  • @react-router/dev - 修复当可选参数被传递给 CLI 时导致的崩溃问题 (#12609)

完整更新日志: v7.1.0...v7.1.1

v7.1.0

日期: 2024-12-20

次要更新

  • 添加对 Vite v6 的支持 (#12469)

补丁更新

  • 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)

按包分类的更新

完整更新日志: v7.0.2...v7.1.0

v7.0.2

日期: 2024-12-02

补丁更新

  • react-router - 暂时只在导出映射中使用一个构建,以便包可以对 react router 有对等依赖 (#12437)
  • @react-router/dev - 支持 moduleResolutionNode16NodeNext 模式 (#12440)
  • @react-router/dev - 为子路由生成更宽泛的 matchesparams 类型 (#12397)
    • 运行时,matches 包含子路由的 matches,params 包含子路由的路径参数
    • 但之前,我们只为父路由和当前路由生成 matchesparams 的类型
    • 为了使我们生成的类型更接近运行时行为,现在我们在访问子路由信息时生成更宽松、更宽泛的类型

完整更新日志: v7.0.1...v7.0.2

v7.0.1

日期: 2024-11-22

补丁更新

  • @react-router/dev - 确保当 Vite 开发服务器重启时,类型生成文件监听器被清理 (#12331)
  • @react-router/dev - 将路由 error 作为 prop 传递给 ErrorBoundary (#12338)

完整更新日志: v7.0.0...v7.0.1

v7.0.0

日期: 2024-11-21

重大变更

包结构重组

  • react-router-dom@remix-run/react@remix-run/server-runtime@remix-run/router 已合并到 react-router 包中
    • 为了简化迁移,在 v7 中 react-router-dom 仍然发布,作为 react-router 中所有内容的重新导出
  • @remix-run/cloudflare-pages@remix-run/cloudflare-workers 已合并到 @react-router/cloudflare 包中
  • react-router-dom-v5-compatreact-router-native 包从 v7 版本开始移除

移除的适配器重新导出

Remix v2 过去会通过各种运行时包(如 nodecloudflaredeno)重新导出所有通用的 @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

以下 API 已在 React Router v7 中移除:

  • json
  • defer
  • unstable_composeUploadHandlers
  • unstable_createMemoryUploadHandler
  • unstable_parseMultipartFormData

最低版本

React Router v7 需要以下最低版本

  • node@20
    • React Router 不再提供一个 installGlobals 方法来polyfill fetch API
  • react@18, react-dom@18

已采用的未来标志行为

Remix 和 React Router 遵循一个API 开发策略,利用“未来标志”(Future Flags)来避免在主版本中引入大量破坏性更改。相反,破坏性更改会在次要版本中通过一个标志引入,允许用户根据需要选择启用。在下一个主版本中,所有未来标志行为都将成为默认行为。

以下先前标记的行为现在在 React Router v7 中成为默认行为

  • React Router v6 标志
    • future.v7_relativeSplatPath
    • future.v7_startTransition
    • future.v7_fetcherPersist
    • future.v7_normalizeFormMethod
    • future.v7_partialHydration
    • future.v7_skipActionStatusRevalidation
  • Remix v2 标志
    • future.v3_fetcherPersist
    • future.v3_relativeSplatPath
    • future.v3_throwAbortReason
    • future.v3_singleFetch
    • future.v3_lazyRouteDiscovery
    • future.v3_optimizeDeps

Vite 编译器

Remix Vite 插件是使用 React Router v7 构建全栈 SSR 应用的正确方式。以前基于 esbuild 的编译器不再可用。

重命名了 vitePlugincloudflareDevProxyVitePlugin

对于迁移到 React Router 的 Remix 用户,vitePlugincloudflareDevProxyVitePlugin 导出已被重命名和移动(#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.useuseAction),我们现在可以放心地暴露那些以前返回 undefined 的 API 的 promise。
  • useNavigate()
  • useSubmit()
  • useFetcher().load
  • useFetcher().submit

useRevalidator().revalidate()

其他值得注意的更改

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"),
+  ]),
+];

另请注意,如果您之前使用了 Remix 的 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"];
}

React Router v7 在 Vite 插件中包含了一个新的 prerender 配置,用于支持 SSG(静态站点生成)用例。这会在构建时预渲染您的 .html.data 文件,因此您可以在运行时从运行的服务器或 CDN 静态地提供它们(#11539

  • 主要更改 (react-router)
    • 移除了原始的 defer 实现,转而通过 single fetch 和 turbo-stream 使用原始的 promise(#11744
      • defer
      • 这将从 React Router 中移除以下导出:
      • 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。
  • 放弃支持 Node 16,React Router SSR 现在需要 Node 18 或更高版本(#11391, #11690
  • 从以下 API 中暴露底层的 router promises,以便在 React 19 API 中进行组合:(#11521
  • 移除了 future.v7_normalizeFormMethod 未来标志(#11697
  • 导入/导出清理(#11840
    • 移除了以下之前是 @remix-run/router 公共 API 的导出:
      • types
        • AgnosticDataIndexRouteObject
        • AgnosticDataNonIndexRouteObject
        • AgnosticDataRouteMatch
        • AgnosticDataRouteObject
        • AgnosticIndexRouteObject
        • AgnosticNonIndexRouteObject
        • AgnosticRouteMatch
        • AgnosticRouteObject
        • TrackedPromise
        • unstable_AgnosticPatchRoutesOnMissFunction
        • Action -> 作为 NavigationType 通过 react-router 导出
        • Router 被导出为 RemixRouter,以区别于 RR 的 <Router>
      • API
        • getToPathname (@private,私有)
        • joinPaths (@private,私有)
        • normalizePathname (@private,私有)
        • resolveTo (@private,私有)
        • stripBasename (@private,私有)
        • createBrowserHistory -> 推荐使用 createBrowserRouter
        • createHashHistory -> 推荐使用 createHashRouter
        • createMemoryHistory -> 推荐使用 createMemoryRouter
        • createRouter
        • createStaticHandler -> 推荐使用 RR Dom 中的包装器 createStaticHandler
        • getStaticContextFromError
    • 移除了之前作为公共 API 从 react-router 导出的以下内容
      • Hash
      • Pathname
      • Search
  • 从内部化的 @remix-run/router 包中移除 future.v7_prependBasename (#11726)
  • 从内部化的 @remix-run/router 包中移除 future.v7_throwAbortReason (#11728)
  • 向所有包添加 exports 字段 (#11675)
  • RemixContext 重命名为 FrameworkContext (#11705)
  • 将最低 React 版本更新到 18 (#11689)
  • PrefetchPageDescriptorPageLinkDescriptor 取代 (#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)
  • 移除剩余的未来标志 (#11820)
    • React Router v7_skipActionErrorRevalidation
    • Remix 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 环境中的用例
    • DOM 环境应从 react-router/dom 导入,以获取使 ReactDOM.flushSync() 可用的正确组件
      • 如果你正在使用 Vite 插件,请在 entry.client.tsx 中使用这个
        • import { HydratedRouter } from 'react-router/dom'
      • 如果你没有使用 Vite 插件并且手动调用 createBrowserRouter/createHashRouter
        • import { RouterProvider } from "react-router/dom"
  • 移除 future.v7_fetcherPersist 标志 (#11731)
  • 允许从 loader 和 action 返回 undefined (#11680, #12057)
  • entry.client 中使用 createRemixRouter/RouterProvider 代替 RemixBrowser (#11469)
  • 移除已弃用的 json 工具函数 (#12146)
    • 如果你仍然需要在你的应用中构建 JSON 响应,你可以使用 Response.json

重大变更 (@react-router/*)

  • 移除 future.v3_singleFetch 标志 (#11522)
  • 放弃对 Node 16 和 18 的支持,将最低 Node 版本更新到 20 (#11690, #12171)
    • 移除 installGlobals(),因为这不再是必需的
  • 向所有包添加 exports 字段 (#11675)
  • 不再通过不同的运行时/适配器包重新导出 react-router 的 API (#11702)
  • 对于迁移到 React Router 的 Remix 用户,现在使用 cookie 和 session API 时,需要来自 Web Crypto APIcrypto 全局对象
    • 这意味着以下 API 现在由 react-router 提供,而不是由平台特定的包提供:(#11837)
      • createCookie
      • createCookieSessionStorage
      • createMemorySessionStorage
      • createSessionStorage
    • 对于运行旧版本 Node 的用户,@remix-run/node 中的 installGlobals 函数已更新,以使用 Node 的 require('node:crypto').webcrypto 实现来定义 globalThis.crypto
    • 由于平台特定的包不再需要实现此 API,以下低级 API 已被移除
      • 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
    • Remix 代码使用的 RouteManifest 类型现在稍微更严格了,因为它使用了之前的 @remix-run/router RouteManifest
      • Record<string, Route> 变为 Record<string, Route | undefined>
    • 移除 AppData 类型,取而代之的是在少数使用它的地方内联 unknown
    • 移除 ServerRuntimeMeta* 类型,取而代之的是它们复制来源的 Meta* 类型
  • 将 Remix v2 类型泛型迁移到 React Router (#12180)
    • 提供这些泛型是为了 Remix v2 迁移目的
    • 这些泛型及其所在的 API 应被视为非正式弃用,推荐使用新的 Route.* 类型
    • 从 React Router v6 迁移的任何人可能不应该利用这些新的泛型,而应该直接迁移到 Route.* 类型
    • 对于 React Router v6 用户来说,这些泛型是新的,并且不应影响你的应用,只有一个例外
      • useFetcher 之前有一个可选的泛型(主要由 Remix v2 使用),它期望的是数据类型
      • 这在 v7 中已更新,以期望生成数据函数的类型(即,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 用户,vitePlugincloudflareDevProxyVitePlugin 导出已被重命名并移动。(#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@4isbot@5
      • 你无需进行任何更改
    • 如果你的 package.json 中有 isbot@3 并且你的仓库中有自己的 entry.server.tsx 文件
      • 你无需进行任何更改
      • 你可以将 isbot 升级到 v5,这独立于 React Router v7 升级
    • 如果你的 package.json 中有 isbot@3 并且你的仓库中没有自己的 entry.server.tsx 文件
      • 你正在使用 React Router v7 提供的内部默认入口,并且你需要将 package.json 中的 isbot 升级到 v5
  • @react-router/dev - 对于迁移到 React Router 的 Remix 用户,Vite 清单文件(即 .vite/manifest.json)现在被写入每个构建子目录中,例如 build/client/.vite/manifest.jsonbuild/server/.vite/manifest.json,而不是 build/.vite/client-manifest.jsonbuild/.vite/server-manifest.json。这意味着构建输出现在更接近于一个典型的 Vite 项目的预期。(#11573)
    • 最初,Remix Vite 插件将所有 Vite 清单文件移动到一个根级别的 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)

按包划分的变更

完整更新日志: v6.28.0...v7.0.0

React Router v6 发行版

v6.30.0

日期: 2025-02-27

次要变更

  • fetcherKey 添加为 patchRoutesOnNavigation 的参数。(#13109)

补丁更新

  • 修复通过 #121696.29.0 中引入的回归问题,该问题导致在使用 Lazy Route Discovery (patchRoutesOnNavigation) 的应用程序中,导航到 splat routes 内部的 hash routes 时出现问题 (#13108)

完整更新日志: v6.29.0...v6.30.0

v6.29.0

日期:2025-01-30

次要更新

  • 将请求的 signal 作为参数提供给 patchRoutesOnNavigation (#12900)
    • 这可用于在进行中的导航/fetcher 被中止时,中止任何 manifest fetches

补丁更新

  • 在生产构建中不记录 v7 废弃警告 (#12794)
  • 抛出 data() 结果时正确地冒泡 headers (#12845)
  • 通过在可能时跳过冗余的 matchRoutes 调用来优化路由匹配 (#12169)
  • 为 fetcher 调用剥离 patchRoutesOnNavigationpath 参数中的搜索参数 (#12899)

完整更新日志: v6.28.2...v6.29.0

v6.28.2

日期:2025-01-16

补丁更新

  • 修复在使用手动 fetcher key 时,若未选择 future.v7_fetcherPersist 会出现的用法问题 (#12674)
  • 修复在 fetcher 卸载时,数据层中 fetcher 数据清理的问题 (#12674)

完整更新日志: v6.28.1...v6.28.2

v6.28.1

日期: 2024-12-20

补丁更新

  • 允许用户通过将标志设置为 false 来选择退出 v7 废弃警告 (#12441)

完整更新日志: v6.28.0...v6.28.1

v6.28.0

日期: 2024-11-06

更新内容

  • 为 v7 做准备,我们为所有尚未选择启用的未来标志添加了废弃警告。请使用这些标志,以便更好地为最终升级到 v7 做准备。

次要更新

  • 记录 v7 标志的废弃警告 (#11750)
    • 添加 json/defer 的废弃警告,推荐直接返回原始对象
      • 这些方法将在 React Router v7 中移除

补丁更新

  • 更新 JSDoc URL 以适应新的网站结构(添加 /v6/ 段) (#12141)

完整更新日志: v6.27.0...v6.28.0

v6.27.0

日期: 2024-10-11

更新内容

稳定化的 API

本次发布稳定化了一系列“不稳定”的 API,为即将发布的 React Router v7 做准备(详见这些博文

  • unstable_dataStrategydataStrategy (createBrowserRouter 及相关函数) (文档)
  • unstable_patchRoutesOnNavigationpatchRoutesOnNavigation (createBrowserRouter 及相关函数) (文档)
  • unstable_flushSyncflushSync (useSubmitfetcher.loadfetcher.submit) (文档)
  • unstable_viewTransitionviewTransition (<Link><Form>useNavigateuseSubmit) (文档)

次要更新

  • 稳定化导航和 fetcher 的 unstable_flushSync 选项 (#11989)
  • 稳定化导航的 unstable_viewTransition 选项以及相应的 unstable_useViewTransitionState hook (#11989)
  • 稳定化 unstable_dataStrategy (#11974)
  • 稳定化 unstable_patchRoutesOnNavigation (#11973)
    • 添加新的 PatchRoutesOnNavigationFunctionArgs 类型以提高便利性 (#11967)

补丁更新

  • 修复在提交到当前上下文路由(带有 index 子路由的父路由)时,如果先前的提交已存在 ?index 参数而导致的错误 (#12003)
  • 修复 useFormAction 错误 - 当移除 ?index 参数时,它不会保留其他非 Remix 的 index 参数 (#12003)
  • 修复 fetchers 在并发获取期间通过重定向无法持久化 preventScrollReset 的错误 (#11999)
  • 避免由于连续的重新验证调用导致 fetcher 中止时出现不必要的 console.error (#12050)
  • 修复使用错误进行水合时 partialHydration 的错误 (#12070)
  • 移除内部缓存,以解决中断的 patchRoutesOnNavigation 调用引起的问题 (#12055)
    • ⚠️ 如果您依赖 unstable_ API 中的此行为,这可能是个破坏性变更
    • 我们过去会在内部缓存进行中的 patchRoutesOnNavigation 调用,这样具有相同起点/终点的多次导航只会执行一次函数并使用同一个 promise
    • 然而,这种方法与 patch 在导航被中断(并且 request.signal 被中止)时短路的行为相悖,因为第一次调用的 patch 会成为空操作(no-op)
    • 这个缓存也对什么是有效的缓存键做出了一些假设,并且忽略了可能发生的任何其他应用程序状态变化
    • 因此,缓存已被移除,因为在大多数情况下,对异步路由进行 import() 等重复调用会自动被缓存;即使没有,用户也很容易在用户空间中实现此缓存
  • 移除 unstable_patchRoutesOnNavigation 中的内部 discoveredRoutes FIFO 队列 (#11977)
    • ⚠️ 如果您依赖 unstable_ API 中的此行为,这可能是个破坏性变更
    • 这最初是作为一个优化实现的,但事实证明它有点过于限制
    • 如果您需要此优化,您可以在 patchRoutesOnNavigation 内部实现自己的缓存
  • 修复 PatchRoutesOnNavigationFunctionpatch 方法中 RouteObject 的类型,使其不期望将与具体环境无关的路由对象传递给 patch (#11967)
  • 将从 patchRoutesOnNavigation 抛出的错误直接暴露给 useRouteError,而不是将它们包装在 400 ErrorResponse 实例中 (#12111)

完整更新日志: v6.26.2...v6.27.0

v6.26.2

日期: 2024-09-09

补丁更新

  • 更新 unstable_dataStrategy API,以支持更高级的实现 (#11943)
    • ⚠️ 如果您已经使用了 unstable_dataStrategy,请仔细查看,因为此更新包含对该 API 的重大变更(breaking changes)
    • unstable_HandlerResult 重命名为 unstable_DataStrategyResult
    • unstable_dataStrategy 的返回签名从一个与 matches 并行的 unstable_DataStrategyResult[] 数组改为一个键值对对象,结构为 routeId => unstable_DataStrategyResult
      • 这允许对重新验证(revalidation)行为进行更高级的控制,因为您可以选择是否重新验证默认情况下可能不会重新验证的数据(通过 match.shouldLoad
    • 您现在应该从 handlerOverride 返回/抛出结果,而不是返回一个 DataStrategyResult
      • 您的 handlerOverride 的返回值(或抛出的错误)将被包装成一个 DataStrategyResult,并从 match.resolve 返回
      • 因此,如果您将 match.resolve() 的结果聚合成一个最终结果对象,您就不需要考虑 DataStrategyResult 类型了
      • 如果您在 handlerOverride 中手动填充结果对象,则需要将 DataStrategyResult 作为值进行分配,以便 React Router 知道它是成功执行还是错误(详情请参阅文档中的示例)
    • unstable_dataStrategy 中新增了一个 fetcherKey 参数,用于区分导航调用和 fetcher 调用
  • 在重定向(redirects)时保留选择启用的视图过渡(view transitions)(#11925)
  • 在路由器的重新验证(revalidation)调用期间保留待处理的视图过渡(view transitions)(#11917)
  • 修复 blocker.proceed 被快速/同步调用时的 blocker 使用问题 (#11930)

完整更新日志: v6.26.1...v6.26.2

v6.26.1

日期: 2024-08-15

补丁更新

  • unstable_patchRoutesOnMiss 重命名为 unstable_patchRoutesOnNavigation 以匹配新行为 (#11888)
  • 更新 unstable_patchRoutesOnNavigation 逻辑,以便在匹配具有动态参数(dynamic param)或 splat 段(splat segments)的路由时调用该方法,以防存在我们尚未发现的更高优先级的静态路由 (#11883)
    • 我们现在还利用了一个内部 FIFO 队列来记录我们已针对其调用过 unstable_patchRoutesOnNavigation 的先前路径,这样在后续导航到相同路径时就不会重复调用

完整更新日志: v6.26.0...v6.26.1

v6.26.0

日期: 2024-08-01

次要更新

  • 新增一个 replace(url, init?) API 作为 redirect(url, init?) 的替代方案,用于在客户端导航重定向时执行 history.replaceState 而不是 history.pushState (#11811)
  • 新增一个 unstable_data() API,用于与 Remix Single Fetch 结合使用 (#11836)
    • 此 API 不适用于在 React Router SPA 应用中直接使用
    • 它主要用于与 createStaticHandler.query() 结合使用,允许 loaders/actions 返回任意数据以及自定义 status/headers,而无需强制将数据序列化为 Response 实例
    • 这允许通过 unstable_dataStrategy 实现更高级的序列化策略,例如在 Remix Single Fetch 中通过 turbo-stream 进行序列化
    • ⚠️ 此更新移除了 HandlerResult 中的 status 字段
      • 如果您需要从 unstable_dataStrategy 返回特定的 status,应改为通过 unstable_data() 进行

补丁更新

  • 修复中断的 fetchers 的内部清理问题,以避免在导航时出现无效的重新验证(revalidations)(#11839)
  • 修复在 SSR 应用中结合使用 future.v7_partialHydrationunstable_patchRoutesOnMiss 时的初始 hydration 行为 (#11838)
    • 在初始 hydration 期间,router.state.matches 现在将包含所有 partial matches,以便我们可以渲染祖先 HydrateFallback 组件

完整更新日志: v6.25.1...v6.26.0

v6.25.1

日期: 2024-07-17

补丁更新

  • RouterProvider 的部分内部实现进行 memoization,以减少不必要的重新渲染 (#11803)

完整更新日志: v6.25.0...v6.25.1

v6.25.0

日期: 2024-07-16

变更内容

v7_skipActionErrorRevalidation 稳定化

此版本将 future.unstable_skipActionErrorRevalidation 标志稳定化为 future.v7_skipActionErrorRevalidation,为即将到来的 React Router v7 版本做准备。

  • 启用此标志后,actions 返回/抛出 4xx/5xx `Response` 时,默认情况下不会触发重新验证(revalidation)
  • 这也将 shouldRevalidateunstable_actionStatus 参数稳定化为 actionStatus

次要更新

  • future.unstable_skipActionErrorRevalidation 稳定化为 future.v7_skipActionErrorRevalidation (#11769)

补丁更新

  • 修复回归问题,正确解码 useMatch 中的路径,以便 matches/params 反映解码后的参数 (#11789)
  • 修复从 unstable_patchRoutesOnMiss 抛出的错误的冒泡问题 (#11786)
  • 修复在 SSR 应用中使用 unstable_patchRoutesOnMiss 且在服务器端匹配到 splat 路由时的 hydration 问题 (#11790)

完整更新日志: v6.24.1...v6.25.0

v6.24.1

日期: 2024-07-03

补丁更新

  • 从警告消息中移除 polyfill.io 的引用,因为该域名已被出售,并且后来被确定服务于恶意软件 (#11741)
  • 导出 NavLinkRenderProps 类型,以便更轻松地为自定义 NavLink 回调函数进行类型标注 (#11553)
  • 使用 future.v7_relativeSplatPath 时,正确解析无路径路由的子级 splat 路由中的相对路径 (#11633)
  • 战争迷雾(不稳定):在路由修补过程中触发新的 router.routes 身份/重排 (#11740)
  • 战争迷雾(不稳定):修复 splat 路由匹配时的初始匹配问题 (#11759)

完整更新日志: v6.24.0...v6.24.1

v6.24.0

日期: 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]);
      }
    },
  }
);

小幅改动

  • 添加对懒加载路由发现(又称“战争迷雾”)的支持 (#11626)

补丁改动

  • 修复 fetcher.submit 类型 - 移除不正确的 navigate/fetcherKey/unstable_viewTransition 选项,因为它们仅与 useSubmit 相关 (#11631)
  • 允许将假值(falsy)的 location.state 传递给 <StaticRouter> (#11495)

完整更新日志: v6.23.1...v6.24.0

v6.23.1

日期: 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

v6.23.0

日期: 2024-04-23

更新内容

数据策略(不稳定)

新的 unstable_dataStrategy API 是一个低级 API,专为需要控制 loader/action 函数数据策略的高级用例而设计。默认实现是当前的行为,即并行获取所有 loader,但此选项允许用户实现更高级的数据流,包括 Remix 的“Single Fetch”、用户空间(user-land)的中间件/上下文 API、自动 loader 缓存等。请参阅文档以获取更多信息。

注意:这是一个旨在用于高级用例的低级 API。它会覆盖 React Router 对 loader/action 执行的内部处理,如果操作不当,可能会破坏您的应用程序代码。请谨慎使用并进行适当的测试。

跳过 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

v6.22.3

日期: 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

v6.22.2

日期: 2024-02-28

补丁更新

  • 在部分注水运行时保留已注水的错误 (#11305)

完整更新日志: v6.22.1...v6.22.2

v6.22.1

日期: 2024-02-16

补丁更新

  • 修复预编码动态参数值带来的编码/解码问题 (#11199)

完整更新日志: v6.22.0...v6.22.1

v6.22.0

日期: 2024-02-01

更新内容

核心 Web 指标技术报告标识

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)
  • 添加一个 createStaticHandlerfuture.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

v6.21.3

日期: 2024-01-18

补丁更新

  • 修复使用 basenameNavLinkisPending 问题 (#11195)
  • 移除 Blocker/BlockerFunction 类型中遗留的 unstable_ 前缀 (#11187)

完整更新日志: v6.21.2...v6.21.3

v6.21.2

日期: 2024-01-11

补丁更新

  • 在可用时,为内部 fetcher keys 利用 useId (#11166)
  • 修复动态参数名中无法识别连字符的 bug (#11160)
  • 不要尝试反序列化空的 JSON 响应 (#11164)

完整更新日志: v6.21.1...v6.21.2

v6.21.1

日期: 2023-12-21

补丁更新

  • 修复当指定 v7_partialHydration 时,在初始 SPA 加载期间 route.lazy 无法正常工作的 bug (#11121)
  • 修复阻止在提交阶段卸载的持久化 fetcher 发生重新验证的 bug (#11102)
  • resolveTo 中对相对路径逻辑进行去重 (#11097)

完整更新日志: v6.21.0...v6.21.1

v6.21.0

日期: 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/NavLinkrelative="path" 的问题 (#11062)

完整更新日志v6.20.1...v6.21.0

v6.20.1

日期:2023-12-01

补丁更新

  • 回滚对 splat 路由的 useResolvedPath 修复,因为大量应用依赖于该错误行为(见 #11052)(#11078)
    • 我们计划在下一个小版本中将此修复作为未来标志重新引入(见 此评论
    • 此修复包含在版本 6.19.06.20.0 中。如果您从 6.18.0 或更早版本升级,则不会受到此修复的影响。

完整更新日志v6.20.0...v6.20.1

v6.20.0

日期:2023-11-22

[!警告] 请使用版本 6.20.1 或更高版本,而不是 6.20.0。我们发现大量应用依赖于在此版本中修复的错误行为(#11045)。我们在 6.20.1 中回滚了该修复,并将在后续版本中将其作为未来标志重新引入。详见 #11052

次要更新

  • 从公共 API 中导出 PathParam 类型 (#10719)

补丁更新

  • v7_fetcherPersist 启用时,不要重新验证已卸载的 fetcher (#11044)
  • 修复 splat 路由中 resolveTo 路径解析的错误 (#11045)
    • 这是对 #10983 的后续处理,以处理使用 getPathContributingMatches 的其他少量代码路径
    • 这删除了 `@remix-run/router` 中的 UNSAFE_getPathContributingMatches 导出,因为在 react-router/react-router-dom 层中不再需要它

完整更新日志v6.19.0...v6.20.0

v6.19.0

日期:2023-11-16

[!警告] 请使用版本 6.20.1 或更高版本,而不是 6.19.0。我们发现大量应用依赖于在此版本中修复的错误行为(#10983)。我们在 6.20.1 中回滚了该修复,并将在后续版本中将其作为未来标志重新引入。详见 #11052

更新内容

unstable_flushSync API

此版本为命令式 API(useSubmituseNavigatefetcher.submitfetcher.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)

    • ⚠️ 这修复了一个相当长期存在的 bug,特别是针对 splat 路由内的 "." 路径,它错误地丢弃了 URL 的 splat 部分。如果您在应用中通过 splat 路由内的 "." 进行相对路由,您应该仔细检查您的逻辑是否依赖于此错误行为,并进行相应更新。
  • 修复已挂载的 useFetcher 中,fetcher 的 key 更改后未被拾取的问题 (#11009)

  • 修复 useFormAction 错误地继承了子路由 action 提交的 ?index 查询参数的问题 (#11025)

  • 修复当 to 位置带有尾随斜杠时 NavLinkactive 逻辑问题 (#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

v6.18.0

日期:2023-10-31

更新内容

新的 Fetcher API

根据此 RFC,我们引入了一些新的 API,可以让你更精细地控制 fetcher 的行为。

  • 现在您可以通过 useFetcher({ key: string }) 指定自己的 fetcher 标识符,这使您可以从应用程序中的不同组件访问相同的 fetcher 实例,而无需进行 prop-drilling
  • 现在通过 useFetchers 返回的 fetcher 上暴露了 fetcher keys,这样就可以通过 key 进行查找
  • FormuseSubmit 现在支持可选的 navigate/fetcherKey props/params,以便在底层使用可选的用户指定 key 启动 fetcher 提交
    • <Form method="post" navigate={false} fetcherKey="my-key">
    • submit(data, { method: "post", navigate: false, fetcherKey: "my-key" })
    • 以这种方式调用 fetcher 是短暂且无状态的
    • 如果您需要访问这些 fetcher 的状态,您将需要利用 useFetchers()useFetcher({ key }) 在其他地方查找它

持久化未来特性标志 (future.v7_fetcherPersist)

根据上面提到的同一份 RFC,我们引入了一个新的 future.v7_fetcherPersist 标志,允许您选择启用新的 fetcher 持久化/清理行为。与在卸载时立即清理不同,fetcher 将会持续存在直到它们返回到 idle 状态。这使得在原始 fetcher 需要卸载的场景中,处理 pending/optimistic UI 更加容易。

  • 这可以算是一个长期存在的 bug 修复,因为 useFetchers() API 始终只应该反映处于 in-flight 状态的 fetcher 信息,用于 pending/optimistic UI——它并不打算反映 fetcher 数据或在 fetcher 返回到 idle 状态后仍然保留它们
  • 在选择启用此标志时,请注意以下具体的行为变化,并检查您的应用程序的兼容性
    • 在完成时仍然处于挂载状态的 fetcher 在完成后将不再出现在 useFetchers() 中 - 因为您可以通过 useFetcher().data 访问数据,它们在那里没有其他作用
    • 之前在处于 in-flight 状态时卸载的 fetcher 将不会立即被中止,而是会在它们返回到 idle 状态后才被清理
      • 当它们处于 in-flight 状态时,它们仍然会通过 useFetchers 暴露,这样您在卸载后仍然可以访问 pending/optimistic 数据
      • 如果一个 fetcher 在完成时不再处于挂载状态,那么它的结果将不会被后处理 - 例如,不会跟随重定向,错误也不会在 UI 中冒泡
      • 然而,如果在树的其他地方使用相同的 key 重新挂载了 fetcher,那么它的结果将会被处理,即使原始的 fetcher 已经卸载

小变化

  • 添加 fetcher key APIs 和 navigate=false 选项 (#10960)
  • 添加 future.v7_fetcherPersist 标志 (#10962)
  • matchPath 中添加对可选路径段的支持 (#10768)

补丁变化

  • 修复 BrowserRouter, HashRouterMemoryRouter 上的 future prop,使其接受 Partial<FutureConfig> 而不是要求包含所有标志 (#10962)
  • 修复 router.getFetcher/router.deleteFetcher 类型定义,之前错误地将 key 指定为可选参数 (#10960)

完整变更日志: 6.17.0...6.18.0

v6.17.0

日期: 2023-10-16

变化内容

视图过渡 (View Transitions) 🚀

我们很高兴在 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 实现平滑简单的过渡指南。

小变化

  • 添加对视图过渡的支持 (#10916)

补丁变化

  • sessionStorage 不可用时,在 ScrollRestoration 中记录警告并优雅地失败 (#10848)
  • 修复 RouterProviderfuture prop 类型为 Partial<FutureConfig>,以便不必指定所有标志 (#10900)
  • 如果路径包含 URL 段,允许 404 检测利用根路由错误边界 (#10852)
  • 修复 ErrorResponse 类型以避免暴露内部字段 (#10876)

完整变更日志: 6.16.0...6.17.0

v6.16.0

日期: 2023-09-13

小变化

  • 为了未来转向更严格的 TypeScript 支持,我们计划在暴露给用户的、用于用户提供数据的类型定义中,将当前对 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.datamatch.handle 的泛型 - 这两者之前就已经设置为 unknown
  • @private 类导出 ErrorResponse 移动到导出 UNSAFE_ErrorResponseImpl,因为它是一个实现细节,并且用户代码不应该构造 ErrorResponse 实例。这使我们能够导出一个通过 InstanceType 关联到该类实例的 type ErrorResponse。用户代码应该只将 ErrorResponse 作为类型使用,并且应该通过 isRouteErrorResponse 进行类型窄化。 (#10811)
  • 导出 ShouldRevalidateFunctionArgs 接口 (#10797)
  • 移除仅用于 Remix v1 向后兼容层且在 Remix v2 中不再需要的私有/内部 API(_isFetchActionRedirect, _hasFetcherDoneAnything) (#10715)

修复更新

  • 在服务器渲染中正确编码渲染的 URI,以避免水合错误 (#10769)
  • 在中止的 query/queryRoute 调用上,在错误消息中添加 method/url (#10793)
  • 修复在 route.lazy 路由上,由 loader/action 抛出的错误导致的竞态条件 (#10778)
  • 修复传递给 shouldRevalidate 的参数对象上的 actionResult 类型 (#10779)

完整更新日志: v6.15.0...v6.16.0

v6.15.0

日期: 2023-08-10

次要更新

  • 添加了一个新的 redirectDocument() 函数,它允许用户指定从 loader/action 发起的重定向应该触发文档重新加载(通过 window.location),而不是尝试通过 React Router 导航到重定向位置 (#10705)

修复更新

  • 如果在没有正在进行的重新验证时,确保 useRevalidator 在重新渲染之间具有引用稳定性 (#10707)
  • 确保 hash history 始终在 hash 路径名上包含一个前导斜杠 (#10753)
  • 修复一个影响使用 URLSearchParamsuseSearchParams hook 的 Firefox Web 扩展的边界情况 (#10620)
  • 重新排序 unstable_usePrompt 中的 effect,以避免在提示被解除阻塞并同步执行导航时抛出异常 (#10687, #10718)
  • SSR: 对于未指定的 action,在 useFormAction() 中不包含 hash,因为它无法在服务器上确定并导致水合问题 (#10758)
  • SSR: 修复 queryRoute 中的一个问题,该问题是并非总能识别抛出的 Response 实例 (#10717)
  • react-router-native: 更新 @ungap/url-search-params 依赖从 ^0.1.4^0.2.2 (#10590)

完整更新日志: v6.14.2...v6.15.0

v6.14.2

日期: 2023-07-17

修复更新

  • 添加缺失的 <Form state> 属性,用于在提交导航时填充 history.state (#10630)
  • 如果一个 defer promise 以 undefined resolve/reject,则触发一个错误,以便与 loader 和 action 的行为保持一致(它们必须返回一个值或 null) (#10690)
  • 正确处理被普通导航中断的 fetcher 重定向 (#10674)
  • 初始加载的 fetcher 在 GET 导航时不应自动重新验证 (#10688)
  • 在使用 <ScrollRestoration> 模拟 hash 滚动时,正确解码元素 ID (#10682)
  • Typescript: 增强 Route.lazy 的返回类型,禁止返回一个空对象 (#10634)
  • SSR: 支持 Error 子类(例如 ReferenceError/TypeError)的正确水合 (#10633)

完整更新日志: v6.14.1...v6.14.2

v6.14.1

日期: 2023-06-30

修复更新

  • 修复当与一个不稳定的 blocker 函数一起使用时,unstable_useBlocker 中的循环问题 (#10652)
  • 修复在后续导航时与重复使用的 blocker 相关的问题 (#10656)
  • 更新了依赖
    • @remix-run/router@1.7.1

完整更新日志: v6.14.0...v6.14.1

v6.14.0

日期: 2023-06-23

有哪些变化

JSON/文本提交

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/jsontext/plain 编码的支持。为了反映这些附加类型,useNavigation/useFetcher 现在也包含了 navigation.json/navigation.textfetcher.json/fetcher.text,它们在适用的情况下包含了 json/text 提交内容。(#10413)

补丁变更

  • 当从 submitter 元素提交表单时,在现代浏览器(支持新的 submitter 参数的浏览器)中优先使用内置的 new FormData(form, submitter),而不是之前的手动方法。(#9865)
    • 对于不支持的浏览器,我们仍然只是将提交按钮的条目附加到末尾,并且我们还为 type="image" 按钮添加了基本支持。
    • 如果开发者希望为旧版浏览器提供完全符合规范的支持,他们可以使用 formdata-submitter-polyfill
  • 在更新 React Router 状态之前调用 window.history.pushState/replaceState(而不是之后),以便在同步的 React 17 渲染期间 window.locationuseLocation 匹配。(#10448)
    • ⚠️ 注意:通常应用程序不应该依赖 window.location,应尽可能始终引用 useLocation,因为 window.location 不会始终 100% 同步(由于 popstate 事件、并发模式等)。
  • 避免为尚未完成数据加载的 fetcher 调用 shouldRevalidate。(#10623)
  • 从提供给 <ScrollRestoration getKey>location 中剥离 basename,以匹配 useLocation 的行为。(#10550)
  • 从提供给 unstable_useBlocker 函数的 location 中剥离 basename,以匹配 useLocation 的行为。(#10573)
  • 修复 StrictModeunstable_useBlocker 的 key 问题。(#10573)
  • 修复当传递数值 0 参数时 generatePath 的问题。(#10612)
  • 修复 React 17 上 tsc --skipLibCheck:false 的问题。(#10622)
  • typescript 升级到 5.1。(#10581)

完整更新日志v6.13.0...v6.14.0

v6.13.0

日期:2023-06-14

有哪些变化

从本质上讲,6.13.0 是一个补丁版本,但由于我们添加了一个新的 future flag,所以进行了 SemVer 次要版本升级。

future.v7_startTransition

总结来说6.13.06.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)

补丁变更

  • 解决了生产模式下 webpack/terser React.startTransition 压缩错误的问题。(#10588)

完整更新日志v6.12.1...v6.13.0

v6.12.1

日期: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

v6.12.0

日期: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)

补丁更新

  • 如果正在提交的 fetcher 被删除,允许其重新验证完成 (#10535)
  • 当尝试使用不可序列化状态执行 PUSH 导航时,重新抛出 DOMException (DataCloneError) (#10427)
  • 确保当 URL 哈希存在时进行重新验证 (#10516)
  • 升级 jestjsdom (#10453)
  • 更新了依赖

完整更新日志: v6.11.2...v6.12.0

v6.11.2

日期: 2023-05-17

补丁更新

  • 修复在 <RouterProvider> 内部的后代 <Routes>basename 重复的问题 (#10492)
  • 修复当 URL 哈希存在时,初始数据加载不会触发的 bug (#10493)
  • 导出 SetURLSearchParams 类型 (#10444)
  • 通过在 _internalSetRoutes 中正确地重建新路由和 manifest,修复 Remix HMR 驱动的错误边界问题 (#10437)

完整更新日志: v6.11.1...v6.11.2

v6.11.1

日期: 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

v6.11.0

日期: 2023-04-28

次要更新

  • useFetcher 中启用 basename 支持 (#10336)
    • 如果你之前通过手动前置 basename 来解决这个问题,那么你需要从 fetcher 调用中移除手动前置的 basename(例如 fetcher.load('/basename/route') -> fetcher.load('/route')
  • 更新了依赖

补丁更新

  • 当使用 RouterProvider 时,useNavigate / useSubmit / fetcher.submit 在位置变化时现在是稳定的,因为我们可以通过 @remix-run/router 实例处理相对路由,并摆脱对 useLocation() 的依赖 (#10336)
    • 当使用 BrowserRouter 时,这些 hook 在位置变化时仍然不稳定,因为它们仍然依赖于 useLocation()
  • Fetcher 不应再因搜索参数变化或路由到相同 URL 而重新验证,并且只会因 action 提交或 router.revalidate 调用而重新验证 (#10344)
  • 修复在路由定义中,使用 Component 代替 element 时出现的意外重新渲染 (#10287)
  • <Link to="//"> 和其他无效 URL 值上优雅地失败 (#10367)
  • 对于 <RouterProvider>@remix-run/router 内部路由状态同步,从 useSyncExternalStore 切换到 useState。我们发现了一些隐秘的 bug,其中路由状态更新在其他正常的 useState 更新之前传播,这可能在 useEffect 调用中埋下隐患。 (#10377, #10409)
  • 将默认错误边界捕获的 loader/action 错误记录到开发环境控制台,便于堆栈跟踪评估 (#10286)
  • 修复当 RouterProvider 存在错误时,阻止后代 <Routes> 渲染的 bug (#10374)
  • 通过在 layout effect 中设置 activeRef,修复在渲染周期中 useNavigate 的检测问题,从而允许将 navigate 函数传递给子组件并在那里的 useEffect 中调用 (#10394)
  • 允许 useRevalidator() 解决由 loader 触发错误边界的场景 (#10369)
  • 增强 LoaderFunction / ActionFunction 的返回类型,以防止 undefined 成为有效返回值 (#10267)
  • 确保在对没有 loader 的路由调用 fetcher.load 时抛出正确的 404 错误 (#10345)
  • 解耦正在重新验证的 fetcher 和触发它们的来源之间的 AbortController 使用,使得卸载/删除正在重新验证的 fetcher 不会影响正在进行的触发导航/重新验证 (#10271)

完整更新日志: v6.10.0...v6.11.0

v6.10.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)
  • 如果 fetcher action 发生重定向,则向 shouldRevalidate 提供 fetcher 提交数据 (#10208)
  • 在路由初始化期间正确处理 lazy() 错误 (#10201)
  • 移除对 DeferredDatainstanceof 检查,以在 SSR 打包场景中应对 ESM/CJS 边界 (#10247)
  • 更新到最新的 @remix-run/web-fetch@4.3.3 (#10216)

完整更新日志: v6.9.0...v6.10.0

v6.9.0

日期: 2023-03-10

更新内容

Component/ErrorBoundary 路由属性

React Router 现在支持一种替代方式,将路由的 elementerrorElement 字段定义为 React 组件,而不是 React 元素。你可以选择将 React 组件传递给新的 ComponentErrorBoundary 字段。两者之间没有功能上的区别,所以你可以选择你喜欢的方法 😀。你不应该同时定义两者,但如果你这样做了,则 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)路由模块

为了保持应用打包体积小并支持路由的代码分割,我们引入了一个新的 lazy() 路由属性。这是一个异步函数,用于解析路由定义中非路由匹配的部分(loader, action, element/Component, errorElement/ErrorBoundary, shouldRevalidate, handle)。

惰性路由在初始加载以及导航或 fetcher 调用的 loadingsubmitting 阶段被解析。你不能惰性定义路由匹配属性(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 实现 方面做出的巨大贡献。

次要更新

  • 添加对 route.Component/route.ErrorBoundary 属性的支持 (#10045)
  • 添加对 route.lazy 的支持 (#10045)

补丁更新

  • 改进对 上下文提供者 的记忆化处理,以避免不必要的重新渲染 (#9983)
  • 修复在某些情况下 generatePath 错误地应用参数的问题 (#10078)
  • [react-router-dom-v5-compat] 添加遗漏的 data router API 重导出 (#10171)

完整更新日志: v6.8.2...v6.9.0

v6.8.2

日期: 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)
  • 确保在 createStaticHandlerquery() 方法中,defer loader 响应的状态码和头部得到保留 (#10077)
  • invariant 改为 UNSAFE_invariant 导出,因为它仅供内部使用 (#10066)

完整更新日志: v6.8.1...v6.8.2

v6.8.1

日期: 2023-02-06

补丁更新

  • 移除针对 POP 导航的不准确控制台警告并更新活动阻塞器逻辑 (#10030)
  • 仅在绝对 URL 重定向时检查不同来源 (#10033)
  • 改进了 Link 组件中的绝对 URL 检测(现在也支持 mailto: URL) (#9994)
  • 修复部分对象(仅 search 或 hash)路径名丢失当前路径值的问题 (#10029)

完整更新日志: v6.8.0...v6.8.1

v6.8.0

日期: 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 */}

补丁更新

  • 修复了重新验证 fetcher 的 shouldRevalidate 调用中的两个独立问题 (#9948)
    • shouldRevalidate 函数之前仅在显式重新验证场景(如 mutation 后、手动调用 useRevalidator,或在 Remix 中使用 X-Remix-Revalidate 头设置 cookie)下被调用。在适用于导航 loader 重新验证的隐式重新验证场景(如搜索参数更改或点击当前页面链接)下未正确调用。现在已在这些额外场景下正确调用。
    • 传递的参数之前是错误的,且彼此不一致,因为 current*/next* 参数反映的是静态的 fetcher.load URL(因此它们是相同的)。相反,它们应该反映触发重新验证的导航(就像 form* 参数那样)。现在这些参数已正确反映触发的导航。
  • 修复通过 useSearchParams 移除搜索参数的 bug (#9969)
  • <fetcher.Form> 上遵守 preventScrollReset 属性 (#9963)
  • 修复哈希路由在手动 URL 更改时的导航问题 (#9980)
  • <ScrollRestoration> 使用 pagehide 事件代替 beforeunload 事件。这有更好的跨浏览器支持,特别是在移动端 Safari 上。 (#9945)
  • 对于仅因哈希变化触发的 mutation 提交,不要短路 (#9944)
  • 移除 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

v6.7.0

日期: 2023-01-18

次要更新

  • 添加 unstable_useBlocker/unstable_usePrompt 钩子,用于阻止应用 location origin 内部的导航 (#9709, #9932)
  • <Form> 添加 preventScrollReset 属性 (#9886)

补丁更新

  • useBeforeUnload 添加透传事件监听器选项参数 (#9709)
  • 修复 generatePath 在存在可选参数时的问题 (#9764)
  • 更新 <Await> 以接受 ReactNode 作为 children 函数的返回结果 (#9896)
  • 改进 actions/loaders 中的绝对重定向 URL 检测 (#9829)
  • 修复使用 memory histories 时 URL 创建的问题 (#9814)
  • 修复提交重定向时滚动重置的问题 (#9886)
  • 修复同源绝对重定向导致的 404 bug (#9913)
  • 简化测试中针对 jsdom bug 的变通方法 (#9824)

完整更新日志: v6.6.2...v6.7.0

v6.6.2

日期: 2023-01-09

补丁更新

  • 确保 SSR 期间 useId 的一致性 (#9805)

完整更新日志: v6.6.1...v6.6.2

v6.6.1

日期: 2022-12-23

补丁更新

  • 在 action 重定向时,在 shouldRevalidate 中包含提交信息 (#9777, #9782)
  • 在 action 重定向到当前位置时,重置 actionData (#9772)

完整更新日志: v6.6.0...v6.6.1

v6.6.0

日期: 2022-12-21

更新内容

此次次要版本发布主要是为了稳定数据路由的 SSR API,因为作为 React Router-ing Remix 工作的一部分,我们已在 Remix 中连接了新的 RouterProvider

次要更新

  • 移除 createStaticHandler/createStaticRouter/StaticRouterProviderunstable_ 前缀 (#9738)
  • 添加 useBeforeUnload() 钩子 (#9664)

补丁更新

  • 支持大写的 <Form method>useSubmit method 值 (#9664)
  • 修复 <button formmethod> 表单提交覆盖问题 (#9664)
  • 修复提交时的显式 replace 操作以及提交到新路径时的 PUSH 操作问题 (#9734)
  • 阻止在 errorElement 中使用 useLoaderData (#9735)
  • StaticRouterProvider 正确水合 Error 对象 (#9664)
  • 对于带有 hydrationData 的 SSR 应用,跳过初始滚动恢复 (#9664)
  • 修复 Loader/Action 数据在出错时未正确清除的几个 bug (#9735)

完整更新日志: v6.5.0...v6.6.0

v6.5.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

小改动

  • 允许可选路由和可选静态片段 (#9650)

补丁更改

  • 停止错误地匹配部分命名参数,例如 <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"
  ...
}
  • 在 SSR 文档 action 请求后,持久化 loader request 的 headers (#9721)
  • 修复发送到 revalidating loaders 的请求,使其反映为 GET 请求 (#9660)
  • 修复深度嵌套可选 segment 的问题 (#9727)
  • GET 表单现在在加载导航时暴露一个提交(submission) (#9695)
  • 修复多个错误冒泡到同一个 error boundary 时的跟踪问题 (#9702)

完整更新日志: v6.4.5...v6.5.0

v6.4.5

Date: 2022-12-07

补丁更改

  • 修复发送到 revalidating loaders 的请求,使其反映为 GET 请求 (#9680)
  • 移除 instanceof Response 检查,转而使用 isResponse (#9690)
  • 修复在 Cloudflare Pages 或其他非浏览器环境中创建 URL 的问题 (#9682, #9689)
  • 为 static handler 的 query/queryRoute 添加 requestContext 支持 (#9696)
    • 注意,不稳定的 API queryRoute(path, routeId) 已更改为 queryRoute(path, { routeId, requestContext })

完整更新日志: v6.4.4...v6.4.5

v6.4.4

Date: 2022-11-30

补丁更改

  • 如果一个 action/loader 函数返回 undefined,则抛出错误,因为 revalidation 需要知道 loader 是否之前已执行过。undefined 还会导致 SSR stringification 过程中出现 hydration 问题。你应始终确保你的 loader/action 返回一个值,如果你不想返回任何内容,可以返回 null。(#9511)
  • 正确处理重定向到外部域名 (#9590, #9654)
  • 在 307/308 重定向时保留 HTTP 方法 (#9597)
  • 在 static data router 中支持 basename (#9591)
  • 增强了 ErrorResponse 的 body,以便在内部 403/404/405 场景中包含更具描述性的文本
  • 修复 NavLink 和后代 <Routes> 中编码字符的问题 (#9589, #9647)
  • 使用内置 hydration 时,正确序列化/反序列化 ErrorResponse 实例 (#9593)
  • 在 static data router 中支持 basename (#9591)
  • 更新了依赖
    • @remix-run/router@1.0.4
    • react-router@6.4.4

完整更新日志: v6.4.3...v6.4.4

v6.4.3

Date: 2022-11-01

补丁更改

  • 使用 createHashRouter 时生成正确的 <a href> 值 (#9409)
  • 更好地处理 URL 和路由路径中特殊字符的编码和匹配问题 (#9477, #9496)
  • index 路由也有 path 时,生成正确的 formAction 路径名 (#9486)
  • 尊重 NavLink 上的 relative=path 属性 (#9453)
  • 修复 NavLink 在根 URL 上的行为 (#9497)
  • 传递 locationArg 时,useRoutes 应该能够返回 null (#9485)
  • 修复 createMemoryRouterinitialEntries 的类型问题 (#9498)
  • loader/action 重定向中支持 basename 和相对路由 (#9447)
  • 在查找正确的提交 action 函数时,忽略无路径的布局路由 (#9455)
  • @remix-run/router 添加 UMD 构建 (#9446)
  • 修复 Firefox 中本地文件执行时的 createURL 问题 (#9464)

完整更新日志: v6.4.2...v6.4.3

v6.4.2

Date: 2022-10-06

补丁更改

  • 尊重 useFormAction 中的 basename (#9352)
  • 修复 IndexRouteObjectNonIndexRouteObject 类型,使 hasErrorElement 成为可选 (#9394)
  • 增强数据路由 hook 无效用法的控制台错误消息 (#9311)
  • 如果一个 index 路由有子路由(children),将会导致运行时错误。我们已增强了 RouteObject/RouteProps 类型,以便在 TypeScript 中提示此错误。(#9366)

完整更新日志: v6.4.1...v6.4.2

v6.4.1

Date: 2022-09-22

补丁更改

  • 保留 initialEntries 中的状态 (#9288)
  • 为 fetcher 对 index 路由的 GET 提交保留 ?index (#9312)

完整更新日志: v6.4.0...v6.4.1

v6.4.0

Date: 2022-09-13

更新内容

Remix Data API

哇,这次更新太大了!6.4.0 将 Remix 的所有数据加载和修改 API 都引入了进来。这里有一个快速的高层概述,但建议你查看 文档,特别是 特性概览教程

新增 react-router API

  • 使用 createMemoryRouter 创建你的路由
  • 使用 <RouterProvider> 渲染你的路由
  • 使用路由 loader 加载数据,使用路由 action 修改数据
  • 使用路由 errorElement 处理错误
  • 使用 deferAwait 延迟加载非关键数据

新增 react-router-dom API

  • 使用 createBrowserRouter / createHashRouter 创建你的路由
  • 使用新的 <Form> 组件提交数据
  • 使用 useFetcher() 执行页面内数据加载和修改
  • 使用 deferAwait 延迟加载非关键数据
  • 使用 <ScrollRestoration> 管理滚动位置
  • 使用 <Link relative="path"> 执行相对路径导航 (#9160)

补丁变更

  • 路径解析现在与末尾斜杠无关 (#8861)
  • useLocation<Routes location> 组件内部返回作用域内的位置 (#9094)
  • 如果定义了 <Link replace> prop,则遵守该设置 (#8779)

完整变更日志: v6.3.0...v6.4.0

v6.3.0

日期: 2022-03-31

次要变更

  • 添加了 v5 到 v6 的向后兼容包 💜 (#8752)。官方指南可以在 这个讨论 中找到。

完整变更日志: v6.2.2...v6.3.0

v6.2.2

日期: 2022-02-28

补丁变更

  • 修复了以特殊 URL 安全字符开头的嵌套 splat 路由问题 (#8563)
  • 修复了在某些情况下 index 路由缺少路由上下文的错误 (#8497)

完整变更日志: v6.2.1...v6.2.2

v6.2.1

日期: 2021-12-17

补丁变更

  • 此版本将内部 history 依赖更新到 5.2.0

完整变更日志: v6.2.0...v6.2.1

v6.2.0

日期: 2021-12-17

次要变更

  • 我们现在使用静态可分析的 CJS 导出。这使得在 Node ESM 脚本中可以使用命名导入 (查看提交)。

补丁变更

  • 修复了 RouteProps element 类型,它应该是 ReactNode (#8473)
  • 修复了顶层路由的 useOutlet 错误 (#8483)

完整变更日志: v6.1.1...v6.2.0

v6.1.1

日期: 2021-12-11

补丁变更

  • 在 v6.1.0 中,我们无意中发布了一个新的、未文档化的 API,可能会引入错误 (#7586)。我们将 HistoryRouter 标记为 unstable_HistoryRouter,因为这个 API 在新的主要版本发布之前可能需要更改。

完整变更日志: v6.1.0...v6.1.1

v6.1.0

日期: 2021-12-10

次要变更

  • <Outlet> 现在可以接收一个 context prop。这个值会传递给子路由,并且可以通过新的 useOutletContext hook 访问。详情请参阅 API 文档。 (#8461)
  • <NavLink> 现在可以接收一个子函数来访问其 props。 (#8164)
  • 改进了 useMatchmatchPath 的 TypeScript 签名。例如,当你调用 useMatch("foo/:bar/:baz") 时,路径会被解析,返回类型将是 PathMatch<"bar" | "baz">。 (#8030)

补丁变更

  • 修复了对嵌套路由中 base64 编码 ID 的支持问题 (#8291)
  • 改进了一些错误消息 (#8202)

完整变更日志: v6.0.2...v6.1.0

v6.0.2

日期: 2021-11-09

补丁变更

完整变更日志: v6.0.1...v6.0.2

v6.0.1

日期: 2021-11-05

补丁变更

  • 添加了默认的 <StaticRouter location> 值 (#8243)
  • 添加了在 <Routes> 内部使用 <Route> 的不变式检查,以帮助用户进行更改 (#8238)。

完整变更日志: v6.0.0...v6.0.1

v6.0.0

日期: 2021-11-03

React Router v6 发布了!

请阅读 我们的博客文章,了解 v6 中的所有精彩内容,包括 关于如何从 React Router v5 和 Reach Router 升级的说明

文档和示例 CC 4.0