更新日志.md
本页内容

React Router 发布版本

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

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

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

v7.8.0

日期:2025-08-07

新功能

统一命名 loaderData

你是否曾注意到框架提供给你的加载器数据值存在差异?比如,在你的组件 props 中我们称之为 loaderData,但在你的匹配项中却是 match.data?是的,我们也注意到了——还有一些眼尖的 React Router 用户在一份提案中提出了这个问题。我们已经在一些遗留的地方,在现有的 data 字段旁边添加了新的 loaderData 字段,以便与新的 Route.* API 中使用的 loaderData 命名保持一致。

中间件 API 的改进/修复 (不稳定)

7.8.0 中最大的一组变更是针对 unstable_middleware API,因为我们正在逐步使其稳定。如果你已经采用中间件 API 进行早期测试,请仔细阅读下面的中间件变更。我们希望尽快稳定这些 API,所以请就其当前状态向我们提供任何反馈!

次要变更

  • react-router - 为 LinksPrefetchPageLinks 添加 nonce 属性 (#14048)
  • react-router - 在现有 data 参数/属性旁边添加 loaderData 参数/属性,以在所有地方提供 loaderDataactionData 之间的一致性和清晰度 (#14047)
    • 更新的类型:Route.MetaArgs, Route.MetaMatch, MetaArgs, MetaMatch, Route.ComponentProps.matches, UIMatch
    • 已为现有的 data 属性添加 @deprecated 警告,以引导用户使用新的 loaderData 属性,为在未来的主版本中移除 data 属性做准备。

补丁变更

  • react-router - 防止在 fetcher.submit 重新验证期间导航时出现“未找到相应的 fetcher 结果”的控制台错误 (#14114)

  • react-router - 将懒加载路由发现清单 URL 的生成切换为使用独立的 URLSearchParams 实例,而不是 URL.searchParams,以避免在 Chrome 中出现严重的性能瓶颈 (#14084)

  • react-router - 调整内部 RSC 对 React.use 的使用,以避免在使用 React 18 时出现 Webpack 编译错误 (#14113)

  • react-router - 在 TypeScript 声明文件中移除对 @types/node 的依赖 (#14059)

  • react-router - 修复 UIMatch 的类型,以反映 loaderData/data 属性可能是 undefined (#12206)

    • 当渲染 ErrorBoundary 时,并非所有活动匹配项都有加载器数据可用,因为可能是它们的 loader 抛出错误触发了边界

    • UIMatch.data 类型没有正确处理这种情况,并且总是反映数据的存在,导致在渲染 ErrorBoundary 时出现意外的运行时错误

    • ⚠️ 这可能会导致您的代码中出现一些未受保护的 match.data 访问的类型错误——您应该在这些场景中正确地为 undefined 值进行保护。

      // app/root.tsx
      export function loader() {
        someFunctionThatThrows(); // ❌ Throws an Error
        return { title: "My Title" };
      }
      
      export function Layout({ children }: { children: React.ReactNode }) {
        let matches = useMatches();
        let rootMatch = matches[0] as UIMatch<Awaited<ReturnType<typeof loader>>>;
        //  ^ rootMatch.data is currently incorrectly typed here, so TypeScript does
        //    not complain if you do the following which throws an error at runtime:
        let { title } = rootMatch.data; // 💥
      
        return <html>...</html>;
      }
      
  • @react-router/dev - 修复 Vite 插件中没有 mkdir 的重命名问题 (#14105)

不稳定变更

⚠️ 不稳定功能不建议在生产环境中使用

RSC

  • react-router - 修复数据模式问题,即当 shouldRevalidate 返回 false 时,返回错误的路由会被 <Outlet /> 替换 (#14071)
  • react-router - 代理服务器操作的副作用重定向,用于文档和 callServer 请求 (#14131)

中间件

  • react-router - 更改 RouterProviderHydratedRouterunstable_RSCHydratedRouter 上的 unstable_getContext 签名,使其返回一个 unstable_RouterContextProvider 实例,而不是内部用于构造该实例的 Map (#14097)

    • 更多信息请参阅文档
    • ⚠️ 如果您已采用 unstable_getContext 属性,这是一个重大变更。
  • react-router - 即使没有加载器存在,也在客户端导航上运行客户端中间件 (#14106)

  • react-router - 将内部中间件实现转换为使用新的 unstable_generateMiddlewareResponse API (#14103)

  • react-router - 确保在启用中间件的情况下,资源路由错误通过 handleError 处理 (#14078)

  • react-router - 如果未调用 next,则传播从服务器中间件返回的 Response (#14093)

  • react-router - 允许服务器中间件返回 data() 值,这些值将被转换为 Response (#14093, #14128)

  • react-router - 更新中间件错误处理,使 next 函数永不抛出错误,而是在适当的 ErrorBoundary 处理任何中间件错误,并通过祖先的 next 函数返回 Response (#14118)

    • 更多信息请参阅错误处理文档
    • ⚠️ 这会改变现有功能,因此如果您当前将 next 调用包装在 try/catch 中,您应该可以移除它们。
  • react-router - 将 next 之前的客户端中间件错误冒泡到适当的祖先错误边界 (#14138)

  • react-router - 当启用中间件时,使 context 参数为只读 (Readonly<unstable_RouterContextProvider>),这样 TypeScript 将不允许您在加载器、操作或中间件中向其写入任意字段。(#14097)

  • react-router - 在 staticHandler.query/staticHandler.queryRoute 中重命名并更改 unstable_respond API 的签名/功能 (#14103)

    • 这仅影响在非框架模式 SSR 期间使用 createStaticHandler() 进行手动数据加载的用户

    • 为了更清晰,该 API 已重命名为 unstable_generateMiddlewareResponse

    • 主要的功能变化是,我们现在不再在调用 unstable_respond 之前运行加载器/操作并交给你结果,而是将一个 query/queryRoute 函数作为参数传递,你可以在你的回调中执行加载器/操作,从而获得对预处理和错误处理的完全控制。

    • query 版本的 API 现在签名为 (query: (r: Request) => Promise<StaticHandlerContext | Response>) => Promise<Response>

    • queryRoute 版本的 API 现在签名为 (queryRoute: (r: Request) => Promise<Response>) => Promise<Response>

    • 这允许更高级的用法,例如在调用 query 前后运行逻辑以及直接处理从查询中抛出的错误。

    • ⚠️ 如果您已采用 staticHandlerunstable_respond API,这是一个重大变更。

      let response = await staticHandler.query(request, {
        requestContext: new unstable_RouterContextProvider(),
        async unstable_generateMiddlewareResponse(query) {
          try {
            // At this point we've run middleware top-down so we need to call the
            // handlers and generate the Response to bubble back up the middleware
            let result = await query(request);
            if (isResponse(result)) {
              return result; // Redirects, etc.
            }
            return await generateHtmlResponse(result);
          } catch (error: unknown) {
            return generateErrorResponse(error);
          }
        },
      });
      
  • @react-router/{architect,cloudflare,express,node} - 当启用 future.unstable_middleware 时,更改 getLoadContext 签名 (type GetLoadContextFunction),使其返回一个 unstable_RouterContextProvider 实例,而不是用于在内部构建实例的 Map (#14097)

    • 这也移除了 type unstable_InitialContext 的导出。
    • 更多信息请参阅中间件 getLoadContext 文档
    • ⚠️ 如果您已采用中间件并正在使用带有 getLoadContext 函数的自定义服务器,这是一个重大变更。

按包分类的变更

完整更新日志: v7.7.1...v7.8.0

v7.7.1

日期:2025-07-24

补丁变更

  • @react-router/dev - 更新到 Prettier v3,用于在运行 react-router reveal --no-typescript 时进行格式化 (#14049)

不稳定变更

⚠️ 不稳定功能不建议在生产环境中使用

  • react-router - RSC 数据模式:修复当 shouldRevalidate 返回 false 时,带有错误的路由不会被强制重新验证的错误 (#14026)
  • react-router - RSC 数据模式:修复在渲染错误边界时出现的“在位置 "/..." 匹配的叶子路由没有 element 或 Component”警告 (#14021)

完整更新日志: v7.7.0...v7.7.1

v7.7.0

日期:2025-07-16

新功能

不稳定的 RSC API

我们很高兴通过以下新 API 引入对数据模式中 RSC 的实验性支持

更多信息,请查看博客文章RSC 文档

次要变更

  • create-react-router - 添加 Deno 作为受支持和可检测的包管理器。请注意,此检测仅适用于 Deno 2.0.5 及以上版本。如果您使用的是旧版 Deno,则必须将 --package-manager CLI 标志设置为 deno。(#12327)
  • @react-router/remix-config-routes-adapter - 与 DefineRoutesFunction 一起导出 DefineRouteFunction 类型 (#13945)

补丁变更

  • react-router - 在验证 cookie 签名时处理 InvalidCharacterError (#13847)

  • react-router - 将 searchParams 的副本传递给 setSearchParams 回调函数,以避免内部 searchParams 实例的突变 (#12784)

    • 如果您在导航被阻塞时改变了当前有状态的 `searchParams`,这会导致错误,因为内部实例会与 `useLocation().search` 不同步。
  • react-router - 在 turbo-stream v2 分支中支持无效的 Date (#13684)

  • react-router - 在框架模式下,在初始渲染后清除开发环境中的关键 CSS (#13872, #13995)

  • react-router - 从 patchRoutesOnNavigationpath 参数中剥离 fetcher 调用的搜索参数 (#13911)

  • react-router - 在 useRevalidator() 调用时跳过滚动恢复,因为它们不是新的位置 (#13671)

  • react-router - 在 ssr 设置为 false 的情况下,支持预渲染配置中的未编码 UTF-8 路由 (#13699)

  • react-router - 如果 URL 哈希不是有效的 URI 组件,则不抛出错误 (#13247)

  • react-router - 从单一获取响应中移除 Content-Length 标头 (#13902)

  • react-router - 修复在 createRoutesStub 中由中间件功能引入的回归问题 (#13946)

    • 作为这项工作的一部分,我们更改了签名以与新的中间件 API 对齐,但没有使其与先前的 AppLoadContext API 向后兼容。

    • 这使得 `createRoutesStub` 在您选择使用中间件和更新的 `context` 类型时可以工作,但对于尚未选择使用中间件的用户则会破坏 `createRoutesStub`。

    • 我们已经撤销了此更改,并以一种方式重新实现它,使两组用户都可以利用它。

    • ⚠️ 如果您已采用不稳定的中间件功能并使用更新后的 API 的 createRoutesStub,这可能是一个破坏性的错误。

      // If you have not opted into middleware, the old API should work again
      let context: AppLoadContext = {
        /*...*/
      };
      let Stub = createRoutesStub(routes, context);
      
      // If you have opted into middleware, you should now pass an instantiated
      // `unstable_routerContextProvider` instead of a `getContext` factory function.
      let context = new unstable_RouterContextProvider();
      context.set(SomeContext, someValue);
      let Stub = createRoutesStub(routes, context);
      
  • @react-router/dev - 更新 vite-node^3.2.2 以支持 Vite 7 (#13781)

  • @react-router/dev - 在开发模式下正确处理 https 协议 (#13746)

  • @react-router/dev - 修复 Vite 的 build.cssCodeSplit 选项被禁用时样式丢失的问题 (#13943)

  • @react-router/dev - 允许路由配置文件使用 .mts.mjs 扩展名 (#13931)

  • @react-router/dev - 修复当 cwd 与项目根目录不同时预渲染文件的位置 (#13824)

  • @react-router/dev - 改进在构建期间找不到 chunk 时的 chunk 错误日志记录 (#13799)

  • @react-router/dev - 修复了错误配置的 externalConditions,该配置为外部依赖启用了 module 条件,并破坏了某些包(如 Emotion)的构建 (#13871)

不稳定变更

⚠️ 不稳定功能不建议在生产环境中使用

  • 为数据模式添加不稳定的 RSC 支持 (#13700)

按包分类的变更

完整更新日志: v7.6.3...v7.7.0

v7.6.3

日期:2025-06-27

补丁变更

  • react-router - 不要为 useRouteLoaderData<typeof clientLoader> 序列化类型 (#13752)

    • 要使类型能够区分 clientLoaderserverLoader,您必须为 clientLoader 的参数添加注解

      //                                   👇 annotation required to skip serializing types
      export function clientLoader({}: Route.ClientLoaderArgs) {
        return { fn: () => "earth" };
      }
      
      function SomeComponent() {
        const data = useRouteLoaderData<typeof clientLoader>("routes/this-route");
        const planet = data?.fn() ?? "world";
        return <h1>Hello, {planet}!</h1>;
      }
      
  • @react-router/cloudflare - 从 peerDependencies 中移除 tsup (#13757)

  • @react-router/dev - 添加 Vite 7 支持 (#13748)

  • @react-router/dev - 在提供自定义 entry.server.(j|t)sx 文件时跳过 package.json 解析检查 (#13744)

  • @react-router/dev - 添加对路由 id 不为 'root' 的验证 (#13792)

  • @react-router/fs-routes @react-router/remix-config-routes-adapter - 使用 replaceAll 来规范化 Windows 文件系统的斜杠 (#13738)

  • @react-router/node - 移除旧的 "install" 包导出 (#13762)

完整更新日志: v7.6.2...v7.6.3

v7.6.2

日期:2025-06-03

补丁变更

  • create-react-router - 更新 tar-fs (#13675)

  • react-router - (内部) 对内部 headers() 函数处理进行轻微重构,以便与 RSC 一起使用 (#13639)

  • react-router @react-router/dev - 通过将路由模块组件属性逻辑从 Vite 插件移至 react-router,避免在框架模式下产生额外的 with-props chunk (#13650)

  • @react-router/dev - 当启用了 future.unstable_viteEnvironmentApi 并且配置了绝对的 Vite base 时,确保在开发过程中正确处理关键 CSS (#13598)

  • @react-router/dev - 更新 vite-node (#13673)

  • @react-router/dev - 修复非 {.js,.jsx,.ts,.tsx} 路由(如 .mdx)的类型生成问题 (#12453)

  • @react-router/dev - 修复可选动态参数的 href 类型 (#13725)

    7.6.1 引入了对使用可选静态段的 `href` 的修复,但这些修复导致了 7.6.0 中可选动态参数工作方式的回归。

    // 7.6.0
    href("/users/:id?"); // ✅
    href("/users/:id?", { id: 1 }); // ✅
    
    // 7.6.1
    href("/users/:id?"); // ❌
    href("/users/:id?", { id: 1 }); // ❌
    

    现在,可选的静态段被扩展为不同的路径用于 `href`,但可选的动态参数则不会。这样 `href` 可以明确地引用一个确切的 URL 路径,同时将路径选项的数量保持在最低限度。

    // 7.6.2
    
    // path: /users/:id?/edit?
    href("
    //    ^ suggestions when cursor is here:
    //
    //    /users/:id?
    //    /users/:id?/edit
    

    此外,您可以从组件 props 传递 `params`,而无需手动进行类型收窄。

    declare const params: { id?: number };
    
    // 7.6.0
    href("/users/:id?", params);
    
    // 7.6.1
    href("/users/:id?", params); // ❌
    "id" in params ? href("/users/:id", params) : href("/users"); // works... but is annoying
    
    // 7.6.2
    href("/users/:id?", params); // restores behavior of 7.6.0
    

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

v7.6.1

日期:2025-05-25

补丁变更

  • react-router - 部分还原在 7.1.4 中为减少对 matchRoutes 调用而添加的优化,因为它暴露了其他问题 (#13562)

  • react-router - 更新 Route.MetaArgs 以反映 data 可能为 undefined (#13563)

    • 这主要适用于路由 loader 向其自身的 ErrorBoundary 抛出错误的情况,但也出现在 404 错误的情况下,该错误会渲染根 ErrorBoundary/meta,但根 loader 并未运行,因为没有路由匹配。
  • react-router - 当懒加载路由发现被导航中断时,避免初始 fetcher 执行 404 错误 (#13564)

  • react-router - 正确地用 `href` 替换 splat `*` (#13593)

    • href("/products/*", { "*": "/1/edit" }); // -> /products/1/edit
  • @react-router/architect - 将 @architect/functions^5.2.0 更新至 ^7.0.0 (#13556)

  • @react-router/dev - 防止对 app/ 目录外的路由文件进行类型生成 (#12996)

  • @react-router/dev - 在清理服务器构建中的资源时,向 build 命令输出添加额外的日志记录 (#13547)

  • @react-router/dev - 当在 Vite 配置中启用了 build.ssrEmitAssets 时,不要从服务器构建中清理资源 (#13547)

  • @react-router/dev - 修复当同一路由用于多个路径时的类型生成问题 (#13574)

    • 例如,`routes/route.tsx` 在这里被用于 4 个不同的路径

      import { type RouteConfig, route } from "@react-router/dev/routes";
      export default [
        route("base/:base", "routes/base.tsx", [
          route("home/:home", "routes/route.tsx", { id: "home" }),
          route("changelog/:changelog", "routes/route.tsx", { id: "changelog" }),
          route("splat/*", "routes/route.tsx", { id: "splat" }),
        ]),
        route("other/:other", "routes/route.tsx", { id: "other" }),
      ] satisfies RouteConfig;
      
    • 以前,类型生成会任意选择其中一个路径作为“胜出者”,并基于该路径为路由模块生成类型。

    • 现在,typegen 会在必要时为同一路由文件的备用路径创建联合类型

  • @react-router/dev - 改进了 params 的类型 (#13543)

    • 例如

      // routes.ts
      import { type RouteConfig, route } from "@react-router/dev/routes";
      
      export default [
        route("parent/:p", "routes/parent.tsx", [
          route("route/:r", "routes/route.tsx", [
            route("child1/:c1a/:c1b", "routes/child1.tsx"),
            route("child2/:c2a/:c2b", "routes/child2.tsx"),
          ]),
        ]),
      ] satisfies RouteConfig;
      
    • 以前,routes/routeparams 被计算为 { p: string, r: string }

    • 这错误地忽略了可能来自子路由的参数

    • 如果访问 /parent/1/route/2/child1/3/4,实际传递给 routes/route 的 params 类型将是 { p: string, r: string, c1a: string, c1b: string }

    • 现在,params 能感知子路由,自动补全将把子参数作为可选参数包含进来

      params.|
      //     ^ cursor is here and you ask for autocompletion
      // p: string
      // r: string
      // c1a?: string
      // c1b?: string
      // c2a?: string
      // c2b?: string
      
    • 你还可以缩小 params 的类型范围,因为它被实现为包含 routes/route 的每个页面的参数的规范化联合类型

      if (typeof params.c1a === 'string') {
        params.|
        //     ^ cursor is here and you ask for autocompletion
        // p: string
        // r: string
        // c1a: string
        // c1b: string
      }
      
  • @react-router/dev - 修复了可选段的 href (#13595)

    • 类型生成现在会将带有可选段的路径扩展为其对应的非可选路径

    • 例如,路径 /user/:id? 会被扩展为 /user/user/:id,以更精确地模拟可访问的 URL

    • 然后,href 使用这些扩展后的(非可选)路径来为你的应用构建类型安全的路径

      // original: /user/:id?
      // expanded: /user & /user/:id
      href("/user"); // ✅
      href("/user/:id", { id: 1 }); // ✅
      
    • 对于静态可选路径来说,这一点变得更加重要,因为之前没有好的方法来指明可选段是否应包含在最终路径中

      // original: /products/:id/detail?
      
      // before
      href("/products/:id/detail?"); // ❌ How can we tell `href` to include or omit `detail?` segment with a complex API?
      
      // now
      // expanded: /products/:id & /products/:id/detail
      href("/product/:id"); // ✅
      href("/product/:id/detail"); // ✅
      

不稳定变更

⚠️ 不稳定功能不建议在生产环境中使用

  • @react-router/dev - 将内部的 react-router/route-module 导出重命名为 react-router/internal (#13543)
  • @react-router/dev - 从生成的 +types/* 文件中移除了 Info 导出 (#13543)
  • @react-router/dev - 在生成 SRI 清单时,跨 Node 版本规范化 dirent 条目路径 (#13591)

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

v7.6.0

日期:2025-05-08

新功能

routeDiscovery 配置选项

我们在 7.6.0 版本中增加了一个新的配置选项,让你能更好地控制路由懒发现(Lazy Route Discovery)功能。如果你在同一台服务器上运行多个 RR 应用程序,现在可以配置 /__manifest 路径;或者,如果你的应用程序足够小,不需要此功能,也可以完全禁用它。

// react-router.config.ts

export default {
  // You can modify the manifest path used:
  routeDiscovery: { mode: "lazy", manifestPath: "/custom-manifest" }

  // Or you can disable this feature entirely and include all routes in the
  // manifest on initial document load:
  routeDiscovery: { mode: "initial" }

  // If you don't specify anything, the default config is as follows, which enables
  // Lazy Route Discovery and makes manifest requests to the `/__manifest` path:
  // routeDiscovery: { mode: "lazy", manifestPath: "/__manifest" }
} satisfies Config;

未来标志的自动类型

一些未来标志会改变 React Router 中类型的工作方式。以前,你必须手动选择加入新的类型。例如,对于 future.unstable_middleware

// react-router.config.ts

// Step 1: Enable middleware
export default {
  future: {
    unstable_middleware: true,
  },
};

// Step 2: Enable middleware types
declare module "react-router" {
  interface Future {
    unstable_middleware: true; // 👈 Enable middleware types
  }
}

你需要自己保持运行时未来标志与这些标志的类型同步。这既令人困惑又容易出错。

现在,React Router 将自动为未来标志启用类型。这意味着你只需要指定运行时的未来标志

// react-router.config.ts

// Step 1: Enable middleware
export default {
  future: {
    unstable_middleware: true,
  },
};

// No step 2! That's it!

在幕后,React Router 会将相应的 declare module 生成到 .react-router/types 中。目前这是在 .react-router/types/+register.ts 中完成的,但这是一个实现细节,将来可能会改变。

次要变更

  • react-router - 在 react-router.config.ts 中增加了一个新的 routeDiscovery 选项,用于配置路由懒发现的行为 (#13451)

  • react-router - 在 createRoutesStub 中增加了对路由组件 props 的支持 (#13528)

    • 这允许你使用 props 而不是 hooks 来对路由组件进行单元测试

      let RoutesStub = createRoutesStub([
        {
          path: "/",
          Component({ loaderData }) {
            let data = loaderData as { message: string };
            return <pre data-testid="data">Message: {data.message}</pre>;
          },
          loader() {
            return { message: "hello" };
          },
        },
      ]);
      
      render(<RoutesStub />);
      
      await waitFor(() => screen.findByText("Message: hello"));
      
  • @react-router/dev - 为未来标志提供自动类型 (#13506)

补丁变更

你可能会注意到这个列表比平时要长一些!团队上周埋头苦干,花了一周时间修复 bug,以减少自 v7 发布以来激增的问题数量。

  • react-router - 修复了 react-router 模块对 NodeNext 的增强功能 (#13498)
  • react-router - 不要在 react-router/dom 的 CJS 导出中打包 react-router (#13497)
  • react-router - 修复了一个 bug,即当一个正在重新验证的 loader 重定向时,一个正在提交的 fetcher 会卡在 loading 状态 (#12873)
  • react-router - 修复了当服务器 loader 返回 undefined 时出现的 hydration 错误 (#13496)
  • react-router - 修复了数据模式下初始加载时 404 的场景 (#13500)
  • react-router - 稳定了 useRevalidatorrevalidate 函数 (#13542)
  • react-router - 在框架模式下,如果 clientAction 抛出一个 data() 结果,则保留状态码 (#13522)
  • react-router - 对路径中开头的双斜杠进行防御性处理,以避免 URL 构造函数抛出 Invalid URL 错误 (#13510)
    • 请注意,我们不会清理/规范化这些路径——我们只是检测它们,以避免 new URL("//", window.location.origin) 抛出错误
  • react-router - 移除了 navigator.connection.saveDataNavigator 声明,以避免在用户代码中干扰除 saveData 之外的任何其他类型 (#13512)
  • react-router - 修复了在 .data 请求中,当路由的最后一个 URL 段是动态参数时,handleErrorparams 值不正确的问题 (#13481)
  • react-router - 在路由懒发现中检测到清单版本不匹配时,不要在重新加载前触发 ErrorBoundary UI (#13480)
  • react-router - 内联 turbo-stream@2.4.1 依赖,并修复了 Map/Set 实例的解码顺序问题 (#13518)
  • react-router - 仅在开发期间渲染开发警告 (#13461)
  • react-router - 对于已中止的 dataStrategy 请求,跳过后处理 (#13521)
    • 这解决了非面向用户的控制台错误,如 Cannot read properties of undefined (reading 'result')
  • @react-router/dev - 如果父目录中存在 package.json,则支持没有 package.json 的项目根目录 (#13472)
  • @react-router/dev - 当通过 CLI 的 --config/-c 标志提供自定义 Vite 配置文件路径时,如果未明确提供项目根目录,则默认为包含 Vite 配置文件的目录 (#13472)
  • @react-router/dev - 在 routes.ts 上下文中,确保 import.meta.env.MODE 尊重 --mode 标志 (#13485)
    • 以前,routes.ts 上下文中的 import.meta.env.MODE 对于 devtypegen --watch 命令总是 "development",否则解析为 "production"。这些默认值仍然存在,但如果提供了 --mode 标志,现在将优先使用该标志。
  • @react-router/dev - 确保 CLI 命令中项目根目录解析逻辑的一致性 (#13472)
  • @react-router/dev - 当使用 vite-node 执行 react-router.config.tsroutes.ts 时,确保忽略 PostCSS 配置文件 (#13489)
  • @react-router/dev - 在开发期间提取关键 CSS 时,确保它从客户端环境加载,以避免因处理 SSR 环境不同的插件而产生的问题 (#13503)
  • @react-router/dev - 修复了在使用 HTTPS 的开发过程中出现的 “Status message is not supported by HTTP/2” 错误 (#13460)
  • @react-router/dev - 在开发过程中创建或删除 react-router.config.ts 时更新配置 (#12319)
  • @react-router/dev - 在 Vite 构建开始前,跳过不必要的 routes.ts 评估 (#13513)
  • @react-router/dev - 修复了由生成的类型引起的 TS2300: Duplicate identifier 错误 (#13499)
  • 以前,具有相同完整路径的路由会在生成的 href 类型 (.react-router/types/+register.ts) 中导致重复条目,从而引发类型检查错误

不稳定变更

⚠️ 不稳定功能不建议在生产环境中使用

  • react-router - 修复了中间件用例中错误冒泡的几个 bug (#13538)
  • @react-router/dev - 当启用 future.unstable_viteEnvironmentApi 时,如果未配置 environments.client.build.assetsDir,确保 Vite 配置中的 build.assetsDir 生效 (#13491)

按包分类的变更

完整更新日志: v7.5.3...v7.6.0

v7.5.3

日期:2025-04-28

补丁变更

  • react-router - 修复了一个 bug,即冒泡的 action 错误会导致处理错误的 ErrorBoundary 路由处的 loaderData 被清除 (#13476)
  • react-router - 处理来自 clientLoader.hydrate 初始加载执行的重定向 (#13477)

完整更新日志: v7.5.2...v7.5.3

v7.5.2

日期:2025-04-24

安全公告

修复了 2 个安全漏洞,这些漏洞可能因发送用于构建时 SPA 模式和预渲染的特定头信息而导致缓存投毒攻击 (GHSA-f46r-rw29-r322, GHSA-cpj6-fhp6-mr6j)。

补丁变更

  • react-router - 调整通过头信息实现预渲染/SPA 模式的方法 (#13453)
  • react-router - 更新单次获取(Single Fetch)功能,以处理 Remix v2 中 ?_data 请求使用的 204 重定向 (#13364)
    • 这使得应用程序可以从 React Router 范围之外(例如,express/hono 中间件)触发 .data 请求的重定向,就像在实现单次获取之前的 Remix v2 中一样
    • 这算是一个权宜之计——推荐的方法是从根路由中间件进行重定向
    • 要使用此功能,你可以从 .data 请求返回如下响应
      • 设置 204 状态码
      • 设置 X-Remix-Redirect: <new-location> 头信息
      • 可选地,设置 X-Remix-Replace: trueX-Remix-Reload-Document: true 头信息以复制 replace()/redirectDocument() 功能
    • ⚠️ 请注意,这些响应依赖于实现细节,可能会在没有 SemVer 主要版本发布的情况下发生变化,建议你为应用程序设置集成测试,以确认此功能在每次 React Router 升级后都能正常工作

完整更新日志: v7.5.1...v7.5.2

v7.5.1

日期:2025-04-17

补丁变更

  • react-router - 当使用基于对象的 route.lazy API 时,如果在 hydration 后懒加载路由,HydrateFallbackhydrateFallbackElement 属性现在会被跳过 (#13376)

    • 如果你将这些属性的代码移动到单独的文件中,由于 hydrate 属性已经未使用(如果路由在 hydration 期间不存在),你就可以完全避免下载它们。例如

      createBrowserRouter([
        {
          path: "/show/:showId",
          lazy: {
            loader: async () => (await import("./show.loader.js")).loader,
            Component: async () =>
              (await import("./show.component.js")).Component,
            HydrateFallback: async () =>
              (await import("./show.hydrate-fallback.js")).HydrateFallback,
          },
        },
      ]);
      
  • react-router - 修复了单次获取的 bug,即向上导航到重用的父路由时不会发出重新验证请求 (#13253)

  • react-router - 在使用 ssr:false + prerender 配置时,当参数值改变时,正确地重新验证预渲染的路径 (#13380)

  • react-router - 修复了当 loader 返回重定向时预渲染的问题 (#13365)

  • react-router - 如果路由没有 loader,不要自动将 null 添加到 staticHandler.query()context.loaderData 中 (#13223)

    • 这是一个无意中为 React Router v7 保留的 Remix v2 实现细节
    • 现在我们允许从 loader 返回 undefined,我们之前对 loaderData[routeId] !== undefined 的检查已不再足够,并已更改为 routeId in loaderData 检查——这些 null 值可能会对这个新检查造成问题
    • ⚠️ 如果你正在使用 createStaticHandler()/<StaticRouterProvider> 进行手动 SSR,并使用 context.loaderData 来控制客户端的 <RouterProvider> hydration 行为,这可能是一个“破坏性 bug 修复”

不稳定变更

⚠️ 不稳定功能不建议在生产环境中使用

  • react-router - 当 getLoadContext 没有更新以返回一个 Map 时,添加了更好的错误消息 (#13242)
  • react-router - 当启用中间件时,更新 LoaderFunctionArgs/ActionFunctionArgs 的上下文类型 (#13381)
  • react-router - 向 dataStrategy 添加了一个新的 unstable_runClientMiddleware 参数,以在自定义 dataStrategy 实现中启用中间件执行 (#13395)
  • react-router - 在 dataStrategy 中添加了对新的 unstable_shouldCallHandler/unstable_shouldRevalidateArgs API 的支持 (#13253)

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

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.unstable_lazyMiddleware API,这将是一个破坏性变更,因为它已被移除,取而代之的是 route.lazy.unstable_middleware。有关更多信息,请参阅下面的“不稳定变更”部分。

次要变更

  • react-router - 为 route.lazy 添加了基于对象的精细 API,以支持单个路由属性的懒加载 (#13294)

补丁变更

  • @react-router/dev - 更新可选的 wrangler peer 依赖范围以支持 wrangler v4 (#13258)
  • @react-router/dev - 在子编译器中恢复依赖优化,以修复在使用 vite-plugin-cloudflare 并导入 Node.js 内置模块时出现的 depsOptimizer is required in dev mode 错误 (#13317)

不稳定变更

⚠️ 不稳定功能不建议在生产环境中使用

  • react-router - 引入 future.unstable_subResourceIntegrity 标志,该标志会为浏览器将加载的脚本生成一个带有 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 操作和潜在的缓存污染 (GHSA-4q56-crqp-v477/CVE-2025-31137)。

补丁变更

  • react-router - 对 route.lazy 函数的调用进行去重 (#13260)
  • @react-router/dev - 修复预渲染错误消息中的路径 (#13257)
  • @react-router/dev - 当 moduleDetection 设置为 force 时,修复虚拟模块的类型生成问题 (#13267)
  • @react-router/express - 改进对 x-forwarded-host 头的验证,以防止潜在的安全问题 (#13309)

不稳定变更

⚠️ 不稳定功能不建议在生产环境中使用

  • react-router - 修复 unstable_MiddlewareFunction 上的类型,以避免当中间件不返回值时出现类型错误 (#13311)
  • react-router - 添加对 route.unstable_lazyMiddleware 函数的支持,以允许懒加载中间件逻辑 (#13210)
    • ⚠️ 我们目前不建议采用此 API,因为我们很可能会在中间件稳定版发布前对其进行更改
    • ⚠️ 如果你的应用当前从 route.lazy 返回 unstable_middleware,这可能是一个破坏性变更
    • route.lazy 的返回值中不再支持 route.unstable_middleware 属性
    • 如果你想懒加载中间件,必须使用 route.unstable_lazyMiddleware
  • @react-router/dev - 当同时启用 future.unstable_middlewarefuture.unstable_splitRouteModules 时,尽可能将 unstable_clientMiddleware 路由导出拆分到单独的 chunk 中 (#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 - 在路由懒发现中加载祖先无路径/索引路由,以支持向上的非急切发现路由 (#13203)
  • react-router - 修复 ssr:true 应用中仅有 clientLoader 的路由的 shouldRevalidate 行为 (#13221)
  • @react-router/dev - 修复了与其他使用 configureServer 和/或 configurePreviewServer 钩子的 Vite 插件的冲突 (#13184)

不稳定变更

⚠️ 不稳定功能不建议在生产环境中使用

  • react-router - 如果中间件抛出错误,确保我们只通过 next() 冒泡错误本身,而不再泄露 MiddlewareError 的实现细节 (#13180)
    • ⚠️ 如果你在中间件中 catchnext() 函数抛出的错误,这可能是一个破坏性变更
  • react-router - 修复启用中间件时 RequestHandlerloadContext 参数类型 (#13204)
  • react-router - 将 Route.unstable_MiddlewareFunction 的返回值从 Response | void 更新为 Response | undefined (#13199)
  • @react-router/dev - 当 future.unstable_splitRouteModules 设置为 "enforce" 时,允许根路由导出可拆分和不可拆分的模块,因为它总是在一个 chunk 中 (#13238)
  • @react-router/dev - 当启用 future.unstable_viteEnvironmentApi 时,允许覆盖默认 SSR 环境的插件(如 @cloudflare/vite-plugin)放置在 React Router 插件之前或之后 (#13183)

按包分类的变更

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

v7.3.0

日期:2025-03-06

次要变更

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

补丁变更

  • react-router - 在活动会话期间,检测并处理新部署时的清单版本不一致问题 (#13061)
    • 在框架模式下,路由懒发现现在将检测新部署后活动会话中的清单版本不匹配问题
    • 导航到未发现的路由时,这种不匹配将触发目标路径的文档重新加载
    • 对未发现的路由进行 fetcher 调用时,这种不匹配将触发当前路径的文档重新加载
  • react-router - 在 SPA 模式下,在开发服务器中跳过资源路由流程 (#13113)
  • react-router - 修复使用 basename 时单次获取 _root.data 请求的问题 (#12898)
  • react-router - 修复包含 RecordloaderDataactionData 的类型 (#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 选项的用户,服务器包 ID 中不再支持连字符,因为它们也需要是有效的 Vite 环境名称。
  • @react-router/dev - 通过从开发服务器请求中剥离 HTTP/2 伪头,修复使用 HTTPS 时的开发服务器问题 (#12830)
  • @react-router/dev - 在使用 cloudflareDevProxy Vite 插件时,在第一次开发服务器请求时懒加载 Cloudflare 平台代理,以避免创建不必要的 workerd 进程 (#13016)
  • @react-router/dev - 修复了布局路由及其相应索引路由在类型生成中重复条目的问题 (#13140)
  • @react-router/express - 更新 expresspeerDependency 以包含 v5 (https://github.com/remix-run/react-router/pull/13064) (#12961)

不稳定变更

⚠️ 不稳定功能不建议在生产环境中使用

  • react-router - 为客户端数据路由器添加 context 支持(不稳定)(#12941)
  • react-router - 支持路由上的中间件(不稳定)(#12941)
  • @react-router/dev - 修复当 ssr 环境被另一个插件配置为自定义 Vite.DevEnvironment 而不是默认的 Vite.RunnableDevEnvironment 时,future.unstable_viteEnvironmentApi 出现的错误 (#13008)
  • @react-router/dev - 当启用 future.unstable_viteEnvironmentApissr 环境禁用了 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。如果你希望为每个请求在上下文中填充初始数据,可以在应用的根部提供一个 unstable_getContext 函数。

  • 库模式 - createBrowserRouter(routes, { unstable_getContext })
  • 框架模式 - <HydratedRouter unstable_getContext>

此函数应返回一个类型为 unstable_InitialContext 的值,这是一个上下文和初始值的 Map<unstable_RouterContext, unknown>

const loggerContext = unstable_createContext<(...args: unknown[]) => void>();

function logger(...args: unknown[]) {
  console.log(new Date.toISOString(), ...args);
}

function unstable_getContext() {
  let map = new Map();
  map.set(loggerContext, logger);
  return map;
}

中间件(不稳定)

中间件是在 future.unstable_middleware 标志后面实现的。要启用它,你必须在 react-router.config.ts 文件中启用该标志和类型。

import type { Config } from "@react-router/dev/config";
import type { Future } from "react-router";

declare module "react-router" {
  interface Future {
    unstable_middleware: true; // 👈 Enable middleware types
  }
}

export default {
  future: {
    unstable_middleware: true, // 👈 Enable middleware
  },
} satisfies Config;

⚠️ 中间件是不稳定的,不应在生产环境中采用。在路由模块加载 clientMiddleware 时,至少存在一个已知的反优化问题,我们将在稳定版发布前解决此问题。

⚠️ 启用中间件会对传递给你的 loader/action 函数的 context 参数产生破坏性变更——更多信息请参见下文。

启用后,路由可以定义一个中间件函数数组,这些函数将在路由处理器运行之前按顺序运行。这些函数接受与 loader/action 相同的参数,外加一个额外的 next 参数来运行剩余的数据管道。这使得中间件可以在处理器执行前后执行逻辑。

// Framework mode
export const unstable_middleware = [serverLogger, serverAuth]; // server
export const unstable_clientMiddleware = [clientLogger]; // client

// Library mode
const routes = [
  {
    path: "/",
    // Middlewares are client-side for library mode SPA's
    unstable_middleware: [clientLogger, clientAuth],
    loader: rootLoader,
    Component: Root,
  },
];

这是一个可以在根路由上放置的客户端日志中间件的简单示例

const clientLogger: Route.unstable_ClientMiddlewareFunction = async (
  { request },
  next,
) => {
  let start = performance.now();

  // Run the remaining middlewares and all route loaders
  await next();

  let duration = performance.now() - start;
  console.log(`Navigated to ${request.url} (${duration}ms)`);
};

请注意,在上面的示例中,next/middleware 函数不返回任何东西。这是特意设计的,因为在客户端没有像在服务器上运行的中间件那样需要通过网络发送的“响应”。数据都是由有状态的 router 在幕后处理的。

对于服务器端中间件,next 函数将返回 React Router 将通过网络发送的 HTTP Response,从而让你有机会根据需要进行更改。你可以抛出一个新的响应来短路并立即响应,或者你可以返回一个新的或修改过的响应来覆盖 next() 返回的默认响应。

const serverLogger: Route.unstable_MiddlewareFunction = async (
  { request, params, context },
  next,
) => {
  let start = performance.now();

  // 👇 Grab the response here
  let res = await next();

  let duration = performance.now() - start;
  console.log(`Navigated to ${request.url} (${duration}ms)`);

  // 👇 And return it here (optional if you don't modify the response)
  return res;
};

你可以从中间件抛出一个 redirect 来短路任何剩余的处理

import { sessionContext } from "../context";
const serverAuth: Route.unstable_MiddlewareFunction = (
  { request, params, context },
  next,
) => {
  let session = context.get(sessionContext);
  let user = session.get("user");
  if (!user) {
    session.set("returnTo", request.url);
    throw redirect("/login", 302);
  }
};

请注意,在这种情况下,当你不需要进行任何后处理时,你不需要调用 next 函数或返回一个 Response

这是另一个使用服务器中间件检测 404 并检查 CMS 是否有重定向的示例

const redirects: Route.unstable_MiddlewareFunction = async ({
  request,
  next,
}) => {
  // attempt to handle the request
  let res = await next();

  // if it's a 404, check the CMS for a redirect, do it last
  // because it's expensive
  if (res.status === 404) {
    let cmsRedirect = await checkCMSRedirects(request.url);
    if (cmsRedirect) {
      throw redirect(cmsRedirect, 302);
    }
  }

  return res;
};

有关 middleware API/设计的更多信息,请参阅决策文档

中间件 context 参数

当启用中间件时,你的应用程序将在 loader 和 action 中使用不同类型的 context 参数以提供更好的类型安全性。context 不再是 AppLoadContext,而是一个 ContextProvider 的实例,你可以与类型安全的上下文(类似于 React.createContext)一起使用。

import { unstable_createContext } from "react-router";
import { Route } from "./+types/root";
import type { Session } from "./sessions.server";
import { getSession } from "./sessions.server";

let sessionContext = unstable_createContext<Session>();

const sessionMiddleware: Route.unstable_MiddlewareFunction = ({
  context,
  request,
}) => {
  let session = await getSession(request);
  context.set(sessionContext, session);
  //                          ^ must be of type Session
};

// ... then in some downstream middleware
const loggerMiddleware: Route.unstable_MiddlewareFunction = ({
  context,
  request,
}) => {
  let session = context.get(sessionContext);
  //  ^ typeof Session
  console.log(session.get("userId"), request.method, request.url);
};

// ... or some downstream loader
export function loader({ context }: Route.LoaderArgs) {
  let session = context.get(sessionContext);
  let profile = await getProfile(session.get("userId"));
  return { profile };
}

如果你正在使用带有 getLoadContext 函数的自定义服务器,从服务器适配器层传递的初始上下文值的返回值不再是一个对象,而应返回一个 unstable_InitialContext (Map<RouterContext, unknown>)。

let adapterContext = unstable_createContext<MyAdapterContext>();

function getLoadContext(req, res): unstable_InitialContext {
  let map = new Map();
  map.set(adapterContext, getAdapterContext(req));
  return map;
}

unstable_SerializesTo

unstable_SerializesTo 为 Apollo 等其他库和框架作者添加了一种在单次获取中注册自定义序列化类型的方法。它通过一个品牌类型实现,该品牌类型的品牌属性被设为可选,以便于转换任意值。

// without the brand being marked as optional
let x1 = 42 as unknown as unstable_SerializesTo<number>;
//          ^^^^^^^^^^

// with the brand being marked as optional
let x2 = 42 as unstable_SerializesTo<number>;

然而,这破坏了 loaderDataactionData 中任何 Record 类型的类型推断,因为这些类型现在会(错误地)匹配 unstable_SerializesTo。这影响了所有用户,而不仅仅是那些依赖 unstable_SerializesTo 的用户。为了解决这个问题,unstable_SerializesTo 的品牌属性被标记为必需(required)而非可选(optional)。

对于使用 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 模式”运行的路径结合使用的能力。

  • 如果您指定 ssr:false 而没有 prerender 配置,这被视为“SPA 模式”,生成的 index.html 文件将只渲染到根路由,并能够为任何有效的应用程序路径进行水合(hydrate)。
  • 如果您指定 ssr:false 并带有 prerender 配置,但*不*包含 / 路径(例如 prerender: ['/blog/post']),我们仍然会生成一个“SPA 模式”的 index.html 文件,该文件可以为应用程序中的任何路径进行水合。
  • 如果您指定 ssr:false 并在 prerender 配置中包含 / 路径,生成的 index.html 文件将是针对根索引路由的,因此我们现在还会在 __spa-fallback.html 中生成一个单独的“SPA 模式”文件,您可以为非预渲染的路径提供服务/水合。

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

允许在 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 应用禁用惰性路由发现(Lazy Route Discovery),而不仅仅是“SPA 模式”,因为没有运行时服务器来处理由搜索参数配置的 __manifest 请求 (#12894)。
    • 我们之前只对“SPA 模式”禁用了此功能,但我们意识到它应该适用于所有 ssr:false 应用。
    • 在那些 prerender 场景中,我们会预渲染 /__manifest 文件,但这会对静态文件服务器的行为做出一些不必要的假设。
  • react-router - 在 SPA 模式下不应用单一获取(Single Fetch)重新验证的去优化,因为没有服务器 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 配置时,只对根 HydrateFallback 进行 SSR(SPA 模式)。
    • 当存在 prerender 配置但当前路径未被预渲染时,只对根 HydrateFallback 进行 SSR(SPA 后备)。
    • 对非预渲染路径的 .data 请求返回 404。
  • react-router - 提高框架模式下 CSS 副作用的预取性能 (#12889)。
  • react-router - 在惰性路由发现中正确处理被中断的 manifest 请求 (#12915)。
  • @react-router/dev - 处理 Vite 配置中的自定义 envDir (#12969)。
  • @react-router/dev - 修复 CLI 解析,以允许无参数使用 npx react-router (#12925)。
  • @react-router/dev - 在使用 prerender:true 时跳过仅包含 action 的资源路由 (#13004)。
  • @react-router/dev - 在使用 ssr:false 时增强无效导出检测 (#12948)。
    • 在所有 ssr:false 的路由中禁止使用 headers/action 函数,因为没有运行时服务器来运行它们。
    • loader 函数的情况更为微妙,取决于给定的路由是否被预渲染。
      • 当使用 ssr:false 而没有 prerender 配置时,只有 root 路由可以有 loader
      • 当使用 ssr:false 并带有 prerender 配置时,只有与 prerender 路径匹配的路由可以有 loader
  • @react-router/dev - 在 ssr:false + prerender 应用中,对以下边缘情况在构建时报错:(#13021)
    • 父路由只有一个 loader(没有 clientLoader)。
    • 父路由被预渲染。
    • 父路由有未被预渲染的子路由。
    • 这意味着当子路径通过 SPA 后备加载时,父路由将没有任何 loaderData,因为没有服务器来运行 loader
    • 这可以通过添加一个父 clientLoader 或预渲染子路径来解决。
    • 如果您添加一个 clientLoader,在非预渲染路径上调用 serverLoader() 将会抛出 404。
  • @react-router/dev - 将预渲染的资源路由 .data 文件限制为仅目标路由 (#13004)。
  • @react-router/dev - 修复二进制文件的预渲染 (#13039)。
  • @react-router/dev - 修复重复参数的类型生成(typegen)(#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 环境 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} />;
}

在此示例中,我们有一个最小的 clientLoader 导出,它进行了一个基本的 fetch 调用,而默认的组件导出则要大得多。这对性能来说是个问题,因为它意味着如果我们要客户端导航到此路由,必须在客户端 loader 开始运行之前下载整个路由模块。

将其可视化为时间线

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

Get Route Module:  |--=======|
Run clientLoader:            |-----|
Render:                            |-|

相反,我们希望将其优化为以下形式

Get clientLoader:  |--|
Get Component:     |=======|
Run clientLoader:     |-----|
Render:                     |-|

为实现此优化,React Router 将在生产构建过程中将路由模块拆分为多个较小的模块。在这种情况下,我们将得到两个独立的虚拟模块 — 一个用于客户端 loader,另一个用于组件及其依赖项。

export async function clientLoader() {
  return await fetch("https://example.com/api").then((response) =>
    response.json(),
  );
}
import { MassiveComponent } from "~/components";

export default function Component({ loaderData }) {
  return <MassiveComponent data={loaderData} />;
}

💡 此优化在框架模式下自动应用,但您也可以通过 route.lazy 在库模式下实现它,并将您的路由编写在多个文件中,如我们关于惰性加载路由模块的博客文章中所述。

现在这些作为单独的模块可用,客户端 loader 和组件可以并行下载。这意味着客户端 loader 可以在准备好后立即执行,而不必等待组件。

当使用更多路由模块 API 时,这种优化效果更加明显。例如,当使用 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} />;
}

此路由仍然可以工作,但由于客户端 loader 和组件都依赖于在同一文件中定义的 shared 函数,它将被去优化为单个路由模块。

为避免这种情况,您可以将导出之间共享的任何代码提取到单独的文件中。例如

export const shared = () => console.log("hello");

然后您可以在路由模块中导入此共享代码,而不会触发去优化

import { MassiveComponent } from "~/components";
import { shared } from "./shared";

export async function clientLoader() {
  shared();
  return await fetch("https://example.com/api").then((response) =>
    response.json(),
  );
}

export default function Component({ loaderData }) {
  shared();
  return <MassiveComponent data={loaderData} />;
}

由于共享代码在其自己的模块中,React Router 现在能够将此路由模块拆分为两个独立的虚拟模块。

import { shared } from "./shared";

export async function clientLoader() {
  shared();
  return await fetch("https://example.com/api").then((response) =>
    response.json(),
  );
}
import { MassiveComponent } from "~/components";
import { shared } from "./shared";

export default function Component({ loaderData }) {
  shared();
  return <MassiveComponent data={loaderData} />;
}

如果您的项目对性能特别敏感,可以将 unstable_splitRouteModules future 标志设置为 "enforce"

export default {
  future: {
    unstable_splitRouteModules: "enforce",
  },
};

如果任何路由模块无法被拆分,此设置将引发错误。

Error splitting route module: routes/example/route.tsx

- clientLoader

This export could not be split into its own chunk because it shares code with other exports. You should extract any shared code into its own module and then import it within the route module.

各软件包的变更

完整变更日志v7.1.5...v7.2.0

v7.1.5

日期:2025-01-31

补丁变更

  • react-router - 修复在 7.1.4 版本中由 #12800 引入的回归问题,该问题导致在使用惰性路由发现(patchRoutesOnNavigation)的应用中,导航到 splat 路由内的哈希路由时出现问题 (#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 - 在单一获取响应中正确处理不能有主体的状态码(204 等)(#12760)。
  • react-router - 当抛出 data() 结果时,正确地将头信息作为 errorHeaders 冒泡 (#12846)。
    • 避免 Set-Cookie 头信息重复,如果它们也从 headers 中返回的话。
  • react-router - 停止对返回原始字符串/对象的资源路由报错,而是将它们序列化为 text/plainapplication/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

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/data 文件不匹配的问题 (#12684)。
  • @react-router/dev - 在运行时启用时使用 module-sync 服务器条件。这修复了在使用对 React Router 有 peer 依赖的库时,Node 22.10.0+ 开发过程中的 React 上下文不匹配问题(例如 useHref() may be used only in the context of a <Router> component.)(#12729)。
  • @react-router/dev - 修复 react-refresh 的 source maps (#12686)。

完整变更日志v7.1.1...v7.1.2

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 - 抛出未包装的单一获取 redirect,以与单一获取之前的行为保持一致 (#12506)。
  • react-router - 在推断 loader 数据类型时忽略重定向 (#12527)。
  • react-router - 移除 <Link prefetch> 警告,该警告在惰性路由发现的世界中容易出现误报 (#12485)。
  • create-react-router - 修复缺失的 fs-extra 依赖 (#12556)。
  • @react-router/dev/@react-router/serve - 如果 NODE_ENV 尚未设置,则正确初始化它,以与 React 19 兼容 (#12578)。
  • @react-router/dev - 从 ServerRouter 中移除遗留/未使用的 abortDelay 属性,并更新默认的 entry.server.tsx 以使用新的 streamTimeout 值以支持单一获取 (#12478)。
    • abortDelay 功能已在 v7 中移除,因为它与 Remix v2 的 defer 实现耦合,但这个属性的移除被遗漏了。
    • 如果您仍在 entry.server 文件中使用此属性,您的应用可能没有按预期中止流,您需要采用与单一获取一同引入的新的 streamTimeout 值。
  • @react-router/fs-routes - 如果路由目录缺失,在 flatRoutes 中抛出错误 (#12407)。

各软件包的变更

完整变更日志v7.0.2...v7.1.0

v7.0.2

日期:2024-12-02

补丁变更

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

完整变更日志v7.0.1...v7.0.2

v7.0.1

日期:2024-11-22

补丁变更

  • @react-router/dev - 确保在 Vite 开发服务器重启时清理类型生成(typegen)文件监视器 (#12331)。
  • @react-router/dev - 将路由 error 作为属性传递给 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 包中。
    • 为了简化迁移,react-router-dom 在 v7 中仍作为 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 方法来填充 fetch API。
  • react@18, react-dom@18

采纳的 Future Flag 行为

Remix 和 React Router 遵循API 开发策略,利用“Future Flags”来避免在主要版本中引入大量重大变更。相反,重大变更在次要版本中通过标志引入,允许用户在方便时选择加入。在下一个主要版本中,所有 future flag 行为都成为默认行为。

以下先前通过标志启用的行为现在是 React Router v7 中的默认行为:

  • React Router v6 flags
    • future.v7_relativeSplatPath
    • future.v7_startTransition
    • future.v7_fetcherPersist
    • future.v7_normalizeFormMethod
    • future.v7_partialHydration
    • future.v7_skipActionStatusRevalidation
  • Remix v2 flags
    • 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 钩子取代,因为它会传递 buildManifest 参数。您仍然可以根据需要将构建清单写入磁盘,但您很可能会发现在 buildEnd 钩子内部编写任何依赖于构建清单的逻辑更为方便。(#11573)

如果您之前使用 manifest 选项,可以将其替换为将清单写入磁盘的 buildEnd 钩子,如下所示:

// react-router.config.ts
import { type Config } from "@react-router/dev/config";
import { writeFile } from "node:fs/promises";

export default {
  async buildEnd({ buildManifest }) {
    await writeFile(
      "build/manifest.json",
      JSON.stringify(buildManifest, null, 2),
      "utf-8"
    );
  },
} satisfies Config;

暴露的 Router Promises

因为 React 19 将对在渲染过程中处理 promise 提供一流支持(通过 React.useuseAction),我们现在可以放心地暴露那些以前返回 undefined 的 API 的 promises。

  • useNavigate()
  • useSubmit()
  • useFetcher().load
  • useFetcher().submit
  • useRevalidator().revalidate()

其他值得注意的变更

routes.ts

当使用 React Router Vite 插件时,路由在 app/routes.ts 中定义。路由配置通过 routes 导出,符合 RouteConfig 类型。提供了路由辅助函数 routeindexlayout,以使声明式类型安全的路由定义更容易。

// app/routes.ts
import {
  type RouteConfig,
  route,
  index,
  layout,
} from "@react-router/dev/routes";

export const routes: RouteConfig = [
  index("./home.tsx"),
  route("about", "./about.tsx"),

  layout("./auth/layout.tsx", [
    route("login", "./auth/login.tsx"),
    route("register", "./auth/register.tsx"),
  ]),

  route("concerts", [
    index("./concerts/home.tsx"),
    route(":city", "./concerts/city.tsx"),
    route("trending", "./concerts/trending.tsx"),
  ]),
];

对于从 Remix 迁移到 React Router 的用户,您仍然可以使用 @react-router/fs-routes 包在 routes.ts 文件中配置基于文件系统的路由。一个能重现默认 Remix 设置的最小路由配置如下所示:

// app/routes.ts
import { type RouteConfig } from "@react-router/dev/routes";
import { flatRoutes } from "@react-router/fs-routes";

export const routes: RouteConfig = flatRoutes();

如果您想从文件系统路由迁移到基于配置的路由,您可以通过将异步函数 flatRoutes 的结果展开到基于配置的路由数组中,来混合和匹配这两种方法。

// app/routes.ts
import { type RouteConfig, route } from "@react-router/dev/routes";
import { flatRoutes } from "@react-router/fs-routes";

export const routes: RouteConfig = [
  // Example config-based route:
  route("/hello", "./routes/hello.tsx"),

  // File system routes scoped to a different directory:
  ...(await flatRoutes({
    rootDirectory: "fs-routes",
  })),
];

如果您之前使用 Remix 的 routes 选项来采用其他文件系统路由约定,您可以使用 @react-router/remix-config-routes-adapter 将这些约定适配到新的 RouteConfig 格式。

例如,如果您在 Remix v2 中使用 Remix v1 的路由约定,您可以结合使用 @react-router/remix-config-routes-adapter@remix-run/v1-route-convention 将其适配到 React Router。

// app/routes.ts
import { type RouteConfig } from "@react-router/dev/routes";
import { remixConfigRoutes } from "@react-router/remix-config-routes-adapter";
import { createRoutesFromFolders } from "@remix-run/v1-route-convention";

export const routes: RouteConfig = remixConfigRoutes(async (defineRoutes) => {
  return createRoutesFromFolders(defineRoutes, {
    ignoredFilePatterns: ["**/.*", "**/*.css"],
  });
});

另外请注意,如果您之前使用 Remix 的 routes 选项来定义基于配置的路由,您也可以使用 @react-router/remix-config-routes-adapter 以极少的代码改动将其适配到新的 RouteConfig 格式。虽然这提供了一条快速的迁移路径,但我们建议您将任何基于配置的路由从 Remix 迁移到新的 RouteConfig 格式,因为这是一次相当直接的迁移。

// app/routes.ts
-import { type RouteConfig } from "@react-router/dev/routes";
+import { type RouteConfig, route } from "@react-router/dev/routes";
-import { remixConfigRoutes } from "@react-router/remix-config-routes-adapter";

-export const routes: RouteConfig = remixConfigRoutes(async (defineRoutes) => {
-  defineRoutes((route) => {
-    route("/parent", "./routes/parent.tsx", () => [
-      route("/child", "./routes/child.tsx"),
-    ]);
-  });
-});
+export const routes: RouteConfig = [
+  route("/parent", "./routes/parent.tsx", [
+    route("/child", "./routes/child.tsx"),
+  ]),
+];

类型安全改进

React Router 现在为您的每个路由模块生成类型,并将类型化的 props 传递给路由模块组件的导出(#11961, #12019)。您可以通过从 ./+types/<不带扩展名的路由文件名> 导入来访问这些类型。

更多详情,请参阅 操作指南 > 路由模块类型安全原理解析 > 类型安全

预渲染

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

export default defineConfig({
  plugins: [
    reactRouter({
      async prerender({ getStaticPaths }) {
        let slugs = await fakeGetSlugsFromCms();
        return [
          ...getStaticPaths(),
          ...slugs.map((slug) => `/product/${slug}`),
        ];
      },
    }),
    tsconfigPaths(),
  ],
});

async function fakeGetSlugsFromCms() {
  await new Promise((r) => setTimeout(r, 1000));
  return ["shirt", "hat"];
}

主要变更 (react-router)

  • 移除原有的 defer 实现,转而通过单次 fetch 和 turbo-stream 使用原生 promise(#11744)。
    • 这会从 React Router 中移除以下导出项:
      • defer
      • AbortedDeferredError
      • type TypedDeferredData
      • UNSAFE_DeferredData
      • UNSAFE_DEFERRED_SYMBOL
  • 将多个包合并到 react-router#11505
    • @remix-run/router
    • react-router-dom
    • @remix-run/server-runtime
    • @remix-run/testing
    • 请注意,为了方便迁移,react-router-dom 包仍然保留,但它只是重新导出了 react-router 的所有 API。
  • 放弃对 Node 16 的支持,React Router SSR 现在需要 Node 18 或更高版本(#11391, #11690)。
  • 移除 future.v7_startTransition 标志(#11696)。
  • 从以下 API 中暴露底层的 router promise,以便在 React 19 的 API 中进行组合:(#11521)
  • 移除 future.v7_normalizeFormMethod future 标志(#11697)。
  • 导入/导出项清理(#11840)。
    • 移除了以下之前从 @remix-run/router 导出的公共 API:
      • 类型
        • AgnosticDataIndexRouteObject
        • AgnosticDataNonIndexRouteObject
        • AgnosticDataRouteMatch
        • AgnosticDataRouteObject
        • AgnosticIndexRouteObject
        • AgnosticNonIndexRouteObject
        • AgnosticRouteMatch
        • AgnosticRouteObject
        • TrackedPromise
        • unstable_AgnosticPatchRoutesOnMissFunction
        • Action -> 通过 react-router 导出为 NavigationType
        • 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
    • 移除了以下之前从 react-router 导出的公共 API:
      • Hash
      • Pathname
      • Search
  • 从内部化的 @remix-run/router 包中移除 future.v7_prependBasename#11726
  • 从内部化的 @remix-run/router 包中移除 future.v7_throwAbortReason#11728
  • 为所有包添加 exports 字段(#11675
  • RemixContext 重命名为 FrameworkContext#11705
  • 将最低 React 版本更新至 18(#11689
  • PrefetchPageDescriptorPageLinkDescriptor 替换(#11960
  • 移除 future.v7_partialHydration 标志(#11725
    • 这也移除了 <RouterProvider fallbackElement> 属性
      • 要进行迁移,请将 fallbackElement 移至根路由的 hydrateFallbackElement/HydrateFallback
    • 还值得注意的是,此 future 标志有一个相关的破坏性变更
      • 在没有 future.v7_partialHydration 的情况下(使用 fallbackElement 时),state.navigation 在初始加载期间会被填充
      • 在有 future.v7_partialHydration 的情况下,state.navigation 在初始加载期间保持 "idle" 状态
  • 移除 future.v7_relativeSplatPath future 标志(#11695
  • 移除剩余的 future 标志(#11820
    • React Router v7_skipActionErrorRevalidation
    • Remix v3_fetcherPersistv3_relativeSplatPathv3_throwAbortReason
  • createRemixStub 重命名为 createRoutesStub#11692
  • 移除 @remix-run/router 中已弃用的 detectErrorBoundary 选项,推荐使用 mapRouteProperties#11751
  • 添加 react-router/dom 子路径导出,以正确地将 react-dom 作为可选的 peerDependency#11851
    • 这确保我们不会在 <RouterProvider> 中盲目地 import ReactDOM from "react-dom" 来访问 ReactDOM.flushSync(),因为这会破坏在非 DOM 环境中使用 createMemoryRouter 的情况。
    • 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
  • 对于从 Remix 迁移到 React Router 的用户,在使用 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/routerRouteManifest
      • 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 - 对于从 Remix 迁移到 React Router 的用户,现在 @react-router/cloudflare 包为 React Router 用户提供了所有来自 @remix-run/cloudflare-pages 的导出。不再有单独的 Cloudflare Pages 包。(#11801
  • @react-router/cloudflare - @remix-run/cloudflare-workers 包已被弃用。从 Remix 迁移到 React Router 的用户应直接使用 @react-router/cloudflare 包。有关如何在 Cloudflare Workers 上下文中使用 @react-router/cloudflare 的指导,请参阅 Cloudflare Workers 模板。(#11801
  • @react-router/dev - 对于从 Remix 迁移到 React Router 的用户,vitePlugincloudflareDevProxyVitePlugin 导出项已被重命名和移动。(#11904
  • @react-router/dev - 对于使用 Vite 插件 buildEnd 钩子的、从 Remix 迁移到 React Router 的用户,解析后的 reactRouterConfig 对象不再包含 publicPath 属性,因为这属于 Vite,而不是 React Router(#11575)。
  • @react-router/dev - 对于从 Remix 迁移到 React Router 的用户,Vite 插件的 manifest 选项已被移除(#11573)。
  • @react-router/dev - 将默认的 isbot 版本更新为 v5,并放弃对 isbot@3 的支持(#11770)。
    • 如果您的 package.json 中有 isbot@4isbot@5
      • 您无需做任何更改。
    • 如果您的 package.json 中有 isbot@3,并且您的仓库中有自己的 entry.server.tsx 文件:
      • 您无需做任何更改。
      • 您可以独立于 React Router v7 的升级,将 isbot 升级到 isbot@5
    • 如果您的 package.json 中有 isbot@3,并且您的仓库中没有自己的 entry.server.tsx 文件:
      • 您正在使用 React Router v7 提供的内部默认入口,您需要在 package.json 中将 isbot 升级到 isbot@5
  • @react-router/dev - 对于从 Remix 迁移到 React Router 的用户,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 配置中启用了 Vite 的 build.manifest。这大大降低了在生产环境中意外提供 Vite 清单的风险,因为它们仅在明确要求时才存在。因此,我们现在可以假设用户知道他们需要自己管理这些额外的文件,并且 React Router 可以安全地生成更标准的 Vite 构建输出。

次要变更

  • react-router - 将 params、loader 数据和 action 数据作为路由组件导出的 props (#11961)
  • react-router - 添加路由模块类型生成 (#12019)
  • react-router - 移除重复的 RouterProvider 实现 (#11679)
  • react-router - 稳定 unstable_dataStrategy (#11969)
  • react-router - 稳定 unstable_patchRoutesOnNavigation (#11970)
  • react-router - 在使用 Remix SSR 时,为 Link/NavLink 添加预取支持 (#11402)
  • react-router - 增强 ScrollRestoration,使其能够在 SSR 文档加载时正确恢复滚动位置 (#11401)
  • @react-router/dev - 在 React Router vite 插件中添加对 prerender 配置的支持,以支持现有的 SSG 用例 (#11539)
  • @react-router/dev - 移除内部的 entry.server.spa.tsx 实现,该实现与 Single Fetch 异步注水方法不兼容 (#11681)
  • @react-router/serve: 更新 express.static 配置以支持新的 prerender API (#11547)
    • build/client/assets 文件夹中的资源像以前一样提供,并带有一年不可变的 Cache-Control 响应头
    • assets 之外的静态文件,例如预渲染的 .html.data 文件,不带特定的 Cache-Control 响应头
    • .data 文件以 Content-Type: text/x-turbo 提供
      • 出于某种原因,通过 express.static 添加此配置时,似乎也会为 .data 文件添加一个 Cache-Control: public, max-age=0 响应头

补丁变更

  • substring 替换 substr (#12080)
  • react-router - 修复使用 data() 从 loader/action 返回的重定向 (#12021)
  • @react-router/dev - 为资源路由启用预渲染 (#12200)
  • @react-router/dev - 解析相对于扁平输出文件结构的配置目录 (#12187)

按包分类的变更

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

React Router v6 版本发布

v6.30.1

日期: 2025-05-20

补丁变更

  • 部分回滚在 6.29.0 中添加的优化,该优化旨在减少对 matchRoutes 的调用,因为它引发了其他问题 (#13623)
  • v7_relativeSplatPath 设置为 false 时,停止记录无效警告 (#13502)

完整变更日志: v6.30.0...v6.30.1

v6.30.0

日期: 2025-02-27

次要变更

  • fetcherKey 作为参数添加到 patchRoutesOnNavigation (#13109)

补丁变更

  • 修复在 6.29.0 中通过 #12169 引入的回归问题,该问题导致在使用惰性路由发现(patchRoutesOnNavigation)的应用中,导航到 splat 路由内的哈希路由时出现问题 (#13108)

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

v6.29.0

日期:2025-01-30

次要变更

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

补丁变更

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

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

v6.28.2

日期:2025-01-16

补丁变更

  • 修复在未选择加入 future.v7_fetcherPersist 时手动使用 fetcher key 的问题 (#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 版本,我们为您尚未选择加入的任何 future 标志添加了弃用警告。请使用这些标志以便更好地为最终升级到 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 (useSubmit, fetcher.load, fetcher.submit) (文档)
  • unstable_viewTransitionviewTransition (<Link>, <Form>, useNavigate, useSubmit) (文档)

次要变更

  • 稳定用于导航和 fetcher 的 unstable_flushSync 选项 (#11989)
  • 稳定用于导航的 unstable_viewTransition 选项和相应的 unstable_useViewTransitionState 钩子 (#11989)
  • 稳定 unstable_dataStrategy (#11974)
  • 稳定 unstable_patchRoutesOnNavigation (#11973)
    • 为方便起见,添加新的 PatchRoutesOnNavigationFunctionArgs 类型 (#11967)

补丁变更

  • 修复当上一次提交已经存在 ?index 参数时,提交到当前上下文路由(带有 index 子路由的父路由)的 bug (#12003)
  • 修复 useFormAction 的 bug - 当移除 ?index 参数时,不会保留其他非 Remix 的 index 参数 (#12003)
  • 修复在并发获取期间,fetcher 的 preventScrollReset 无法在重定向后持久化的问题 (#11999)
  • 避免因连续的重新验证调用导致 fetcher 中止时产生不必要的 console.error (#12050)
  • 修复使用 partialHydration 时,在带错误的注水过程中的 bug (#12070)
  • 移除内部缓存,以修复被中断的 patchRoutesOnNavigation 调用问题 (#12055)
    • ⚠️ 如果您依赖于 unstable_ API 中的此行为,这可能是一个破坏性变更
    • 我们过去在内部缓存进行中的 patchRoutesOnNavigation 调用,以便具有相同起点和终点的多个导航只会执行该函数一次并使用相同的 promise
    • 然而,如果导航被中断(并且 request.signal 被中止),这种方法与 patch 的短路行为相冲突,因为第一次调用的 patch 将会是无操作
    • 此缓存还对有效的缓存键做了一些假设——并且对可能发生的任何其他应用程序状态更改一无所知
    • 因此,缓存已被移除,因为在大多数情况下,对异步路由的 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 的破坏性变更
    • 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 调用
  • 通过重定向保留已选择启用的视图过渡(#11925
  • 通过路由器的重新验证调用保留待处理的视图过渡(#11917
  • 修复当 blocker.proceed 被快速/同步调用时,blocker 的使用问题(#11930

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

v6.26.1

日期: 2024-08-15

补丁更新

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

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

v6.26.0

日期: 2024-08-01

次要更新

  • 在客户端导航重定向时,新增一个 replace(url, init?) 方法作为 redirect(url, init?) 的替代方案,它会执行 history.replaceState 而不是 history.pushState#11811
  • 为配合 Remix Single Fetch 功能,新增一个 unstable_data() API(#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 的内部清理过程,以避免在导航时发生无效的重新验证(#11839
  • 修复同时使用 future.v7_partialHydrationunstable_patchRoutesOnMiss 时的初始 hydration(注水)行为(#11838
    • 在初始 hydration 期间,router.state.matches 现在将包含任何部分匹配项,以便我们可以渲染祖先的 HydrateFallback 组件

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

v6.25.1

日期: 2024-07-17

补丁更新

  • 对一些 RouterProvider 内部实现进行 Memoize 优化,以减少不必要的重新渲染(#11803

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

v6.25.0

日期: 2024-07-16

更新内容

稳定化 v7_skipActionErrorRevalidation

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

  • 当启用此标志时,返回/抛出 4xx/5xx Response 的 action 默认将不会触发重新验证
  • 这也将 shouldRevalidateunstable_actionStatus 参数稳定为 actionStatus

次要更新

  • future.unstable_skipActionErrorRevalidation 稳定为 future.v7_skipActionErrorRevalidation#11769

补丁更新

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

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

v6.24.1

日期: 2024-07-03

补丁更新

  • 从警告信息中移除对 polyfill.io 的引用,因为该域名已被出售,并被发现会提供恶意软件(#11741
  • 导出 NavLinkRenderProps 类型,以便于为自定义 NavLink 回调函数添加类型(#11553
  • 当使用 future.v7_relativeSplatPath 时,正确解析 splat 路由中的相对路径,这些 splat 路由是无路径路由的子路由(#11633
  • Fog of War (不稳定):在路由修补期间触发新的 router.routes 身份/重排(#11740
  • Fog of War (不稳定):修复当 splat 路由匹配时的初始匹配问题(#11759

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

v6.24.0

日期: 2024-06-24

更新内容

路由懒发现(又名“战争迷雾”)

我们非常激动地在 v6.24.0 中发布我们新的“路由懒发现”API!有关背景信息,请查看原始的 RFC长话短说,自从我们在 v6.4 通过 <RouterProvider> 引入数据 API 以来,我们一直对其中一个权衡感到有些遗憾,那就是缺少一个能与我们在 <BrowserRouter>/<Routes> 应用中相媲美的、引人注目的代码分割方案。我们在 v6.9.0 中通过 route.lazy 向着改进这一方案迈出了一小步,而在 v6.24.0 中,我们则走完了剩下的路。

有了“战争迷雾”,你现在可以通过传递给 createBrowserRouter (及其内存/哈希路由对应版本) 的新选项 unstable_patchRoutesOnMiss 来懒加载路由树的部分内容。这为你提供了一种方式,可以在 React Router 无法匹配给定路径时介入,并在导航(或 fetcher 调用)期间向路由树中修补新的路由。

这里有一个非常小的例子,但请参考文档以获取更多信息和用例。

const router = createBrowserRouter(
  [
    {
      id: "root",
      path: "/",
      Component: RootComponent,
    },
  ],
  {
    async unstable_patchRoutesOnMiss({ path, patch }) {
      if (path === "/a") {
        // Load the `a` route (`{ path: 'a', Component: A }`)
        let route = await getARoute();
        // Patch the `a` route in as a new child of the `root` route
        patch("root", [route]);
      }
    },
  },
);

次要更新

  • 增加对路由懒发现(又名“战争迷雾”)的支持(#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 的导入从 index.ts 改回 react-router-dom#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 的“单一请求”、用户级中间件/上下文 API、自动 loader 缓存等。请参阅文档获取更多信息。

注意: 这是一个专为高级用例设计的底层 API。它会覆盖 React Router 内部对 loader/action 执行的处理,如果使用不当,将会破坏你的应用代码。请谨慎使用并进行适当的测试。

跳过 Action 错误时的重新验证 (不稳定)

目前,所有活跃的 loader 在任何 action 提交后都会重新验证,无论 action 的结果如何。然而,在大多数情况下,来自 action4xx/5xx 响应意味着实际上没有数据被更改,重新验证是不必要的。我们引入了一个新的 future.unstable_skipActionErrorRevalidation 标志来改变这里的行为,并计划在未来版本的 React Router 中将其设为默认行为。

启用此标志后,返回/抛出 4xx/5xx 响应状态的 action 将不再自动重新验证。如果启用此标志后你仍需要在 4xx/5xx 结果后进行重新验证,你仍然可以通过从 shouldRevalidate 返回 true 来实现——该函数现在还会收到一个新的 unstable_actionStatus 参数以及 actionResult,这样你就可以根据 action 响应的状态做出决策,而无需将其编码到 action 数据中。

次要更新

  • 新增一个 unstable_dataStrategy 配置选项(#11098, #11377
  • @remix-run/router - 新增一个 future.unstable_skipActionRevalidation 未来标志(#11098
  • @remix-run/router - SSR: 向 staticHandler.query 方法新增一个 skipLoaderErrorBubbling 选项,以禁用静态处理器对错误的冒泡,用于 Remix 的单一请求实现(#11098, (#11377))

完整更新日志: v6.22.3...v6.23.0

v6.22.3

日期: 2024-03-07

补丁更新

  • 修复了一个 future.v7_partialHydration 的 bug,该 bug 会在 SSR loader 错误冒泡到父级边界时,在 hydration 期间重新运行边界以下的 loader(#11324
  • 修复了一个 future.v7_partialHydration 的 bug,该 bug 会在路由没有 loader 时将路由器视为未初始化(#11325

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

v6.22.2

日期: 2024-02-28

补丁更新

  • 在部分 hydration 运行时保留已 hydration 的错误(#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 用户体验报告(CrUX)数据集中的真实用户体验和 HTTP Archive 中的 Web 技术检测,我们可以了解到架构决策(如选择 CMS 平台或 JavaScript 框架)如何影响网站的 CWV 性能。

他们使用一个名为 wappalyzer 的工具,通过查找特定的脚本、全局 JS 变量或其他识别特征来识别给定网站正在使用的技术。例如,对于 Remix 应用程序,他们查找全局变量 __remixContext 来识别网站正在使用 Remix。

有人向我们指出,React Router 无法被可靠地识别,因为没有可识别的全局特征。他们目前正在寻找名称中包含 react-router 的外部脚本。这可以识别从 CDN(如 unpkg)使用 React Router 的网站,但会漏掉绝大多数从 npm 注册表安装 React Router 并将其捆绑到其 JS 文件中的网站。这导致严重低估了 React Router 在网络上的使用情况。

6.22.0 版本开始,使用 react-router-dom 的网站将开始添加一个 window.__reactRouterVersion 变量,该变量将被设置为 SemVer 主版本号的字符串值(即 window.__reactRouterVersion = "6";),以便它们可以被正确识别。

次要更新

  • 为 CWV 报告检测添加 window.__reactRouterVersion#11222
  • 新增一个 createStaticHandlerfuture.v7_throwAbortReason 标志,当请求被中止时,抛出 request.signal.reason(默认为 DOMException),而不是像 new Error("query() call aborted: GET /path") 这样的 Error#11104
    • 请注意,DOMException 是在 Node v17 中添加的,因此在 Node 16及以下版本中你不会得到 DOMException

补丁更新

  • 如果将 ErrorResponse 状态码传递给 getStaticContextFormError,则尊重该状态码(#11213

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

v6.21.3

日期: 2024-01-18

补丁更新

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

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

v6.21.2

日期: 2024-01-11

补丁更新

  • 在可用时,利用 useId 作为内部 fetcher 的 key(#11166
  • 修复动态参数名称中无法识别破折号的 bug(#11160
  • 不要尝试反序列化空的 JSON 响应(#11164

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

v6.21.1

日期: 2023-12-21

补丁更新

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

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

v6.21.0

日期: 2023-12-13

更新内容

future.v7_relativeSplatPath

我们在 6.19.0 中修复了一个 splat 路由的路径解析 bug,但后来发现大量应用程序依赖于这个有 bug 的行为,所以我们在 6.20.1 中撤销了该修复(参见 #10983, #11052, #11078)。

有 bug 的行为是,在 splat 路由内解析相对路径时的默认行为会忽略当前路由路径的任何 splat (*) 部分。当启用未来标志时,splat 部分将被包含在 splat 路由内的相对路径逻辑中。

更多信息,请参阅 useResolvedPath 文档和/或详细的更新日志条目

部分 Hydration

我们为 @remix-run/router 添加了一个新的 future.v7_partialHydration 未来标志,用于在服务器端渲染时启用数据路由器的部分 hydration。这允许你提供的 hydrationData.loaderData 只包含部分初始匹配路由 loader 的值,而不是全部。当启用此标志时,路由器将在 router.initialize() 期间为没有 hydration loader 数据的路由调用 loader 函数,并且在执行未 hydration 的路由时,它将渲染到最深层提供的 HydrateFallback(直到第一个没有 hydration 数据的路由)。(#11033

次要更新

  • 新增一个 future.v7_relativeSplatPath 标志,以实现一个对 splat 路由内相对路由的破坏性 bug 修复。(#11087
  • 新增一个 future.v7_partialHydration 未来标志,用于在服务器端渲染时启用数据路由器的部分 hydration(#11033

补丁更新

  • ErrorBoundary 中正确处理假值(falsy)的错误值(#11071
  • 捕获并冒泡在尝试从 loader/action 函数中解包响应时抛出的错误(#11061
  • 修复在匹配的路由之外渲染 Link/NavLink 时的 relative="path" 问题(#11062

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

v6.20.1

日期: 2023-12-01

补丁更新

  • 由于大量应用程序依赖于有 bug 的行为,撤销了对 splat 路由的 useResolvedPath 修复 (参见 #11052) (#11078)
    • 我们计划在下一个次要版本中通过一个未来标志重新引入此修复 (参见此评论)
    • 此修复包含在版本 6.19.06.20.0 中。如果你从 6.18.0 或更早版本升级,你不会受到此修复的影响。

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

v6.20.0

日期: 2023-11-22

[!WARNING] 请使用 6.20.1 或更高版本,而不是 6.20.0。我们发现大量应用依赖于此版本中修复的一个有 bug 的行为 (#11045)。我们在 6.20.1 中撤销了该修复,并将在后续版本中通过一个未来标志重新引入它。详情请见 #11052

次要更新

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

补丁更新

  • 当启用 v7_fetcherPersist 时,不重新验证已卸载的 fetcher (#11044)
  • 修复 splat 路由中 resolveTo 路径解析的 bug (#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

[!WARNING] 请使用 6.20.1 或更高版本,而不是 6.19.0。我们发现大量应用依赖于此版本中修复的一个有 bug 的行为 (#10983)。我们在 6.20.1 中撤销了该修复,并将在后续版本中通过一个未来标志重新引入它。详情请见 #11052

更新内容

unstable_flushSync API

此版本为命令式 API (useSubmit, useNavigate, fetcher.submit, fetcher.load) 带来了一个新的 unstable_flushSync 选项,让用户可以选择为待处理/乐观 UI 启用同步 DOM 更新。

function handleClick() {
  submit(data, { flushSync: true });
  // Everything is flushed to the DOM so you can focus/scroll to your pending/optimistic UI
  setFocusAndOrScrollToNewlyAddedThing();
}

次要更新

  • useNavigate/useSubmit/fetcher.load/fetcher.submit 添加 unstable_flushSync 选项,以选择不使用 React.startTransition,而使用 ReactDOM.flushSync 进行状态更新 (#11005)
  • useBlocker 钩子中移除了 unstable_ 前缀,因为它已经使用了足够长的时间,我们对该 API 充满信心 (#10991)
    • 由于浏览器处理 window.confirm 的方式存在差异,导致 React Router 无法保证一致/正确的行为,我们不打算从 unstable_usePrompt 中移除该前缀

补丁更新

  • 修复 useActionData,使其返回正确的上下文 action 数据,而不是树中的任何 action 数据 (#11023)

  • 修复 useResolvedPath 中的一个 bug,该 bug 会导致在 splat 路由中使用 useResolvedPath(".") 时丢失 URL 路径的 splat 部分。 (#10983)

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

  • 修复 useFormAction,它错误地从子路由 action 提交中继承了 ?index 查询参数 (#11025)

  • 修复当 to location 带有尾部斜杠时 NavLinkactive 逻辑 (#10734)

  • 修复类型,使 unstable_usePrompt 除了 boolean 外,还可以接受 BlockerFunction (#10991)

  • 修复 relative="path" 的 bug,其中相对路径计算从完整的位置路径名开始,而不是从当前上下文的路由路径名开始。 (#11006)

    <Route path="/a">
      <Route path="/b" element={<Component />}>
        <Route path="/c" />
      </Route>
    </Route>;
    
    function Component() {
      return (
        <>
          {/* This is now correctly relative to /a/b, not /a/b/c */}
          <Link to=".." relative="path" />
          <Outlet />
        </>
      );
    }
    

完整更新日志: 6.18.0...6.19.0

v6.18.0

日期: 2023-10-31

更新内容

新的 Fetcher API

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

  • 您现在可以通过 useFetcher({ key: string }) 指定自己的 fetcher 标识符,这允许您从应用程序中的不同组件访问同一个 fetcher 实例,而无需进行属性传递(prop-drilling)。
  • Fetcher 的键(key)现在会暴露在 useFetchers 返回的 fetcher 上,以便可以通过 key 来查找它们。
  • FormuseSubmit 现在支持可选的 navigate/fetcherKey 属性/参数,以允许在底层以用户可选指定的 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(待定/乐观 UI)变得*非常*容易。

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

次要变更

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

补丁变更

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

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

v6.17.0

日期: 2023-10-16

变更内容

视图过渡 🚀

我们很高兴地宣布,React Router 开始实验性支持 视图过渡 API!您现在可以触发导航性的 DOM 更新,将其包装在 document.startViewTransition 中,从而在您的单页应用(SPA)导航中启用 CSS 动画过渡。

在您的 React Router 应用中启用视图过渡的最简单方法是通过新的 <Link unstable_viewTransition> 属性。这将导致导航的 DOM 更新被包裹在 document.startViewTransition 中,从而为 DOM 更新启用过渡。在没有任何额外 CSS 样式的情况下,您的页面将获得一个基本的交叉淡入淡出动画效果。

如果您需要为您的动画应用更精细的样式,您可以利用 unstable_useViewTransitionState hook,它会告诉您过渡是否正在进行中,您可以用它来应用类或样式。

function ImageLink(to, src, alt) {
  const isTransitioning = unstable_useViewTransitionState(to);
  return (
    <Link to={to} unstable_viewTransition>
      <img
        src={src}
        alt={alt}
        style={{
          viewTransitionName: isTransitioning ? "image-expand" : "",
        }}
      />
    </Link>
  );
}

您也可以使用 <NavLink unstable_viewTransition> 简写方式,它将为您管理 hook 的使用,并在过渡期间自动为 <a> 标签添加一个 transitioning 类。

a.transitioning img {
  view-transition-name: "image-expand";
}
<NavLink to={to} unstable_viewTransition>
  <img src={src} alt={alt} />
</NavLink>

关于视图过渡的示例用法,请查看我们的 fork 版本,它基于出色的 Astro Records 演示。

有关使用视图过渡 API 的更多信息,请参考 Google Chrome 团队的《使用视图过渡 API 实现平滑简单的过渡》指南。

次要变更

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

补丁变更

  • sessionStorage 不可用时,在 ScrollRestoration 中记录警告并优雅地失败 (#10848)
  • 修复 RouterProviderfuture 属性类型,使其为 Partial<FutureConfig>,这样就不必指定所有标志 (#10900)
  • 允许 404 检测在路径包含 URL 段时利用根路由的错误边界 (#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 实例。这使我们可以导出一个 type ErrorResponse,它通过 InstanceType 与该类的实例相对应。用户的代码应该只将 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)
  • 确保哈希历史记录中的哈希路径名始终以斜杠开头 (#10753)
  • 修复了影响 Firefox 中使用 URLSearchParamsuseSearchParams hook 的 web 扩展的一个边缘情况 (#10620)
  • 重新排序 unstable_usePrompt 中的 effect,以避免在提示被解除阻塞且同步执行导航时抛出异常 (#10687, #10718)
  • SSR:对于未指定的 action,在 useFormAction() 中不包含哈希值,因为它无法在服务器上确定,并会导致水合问题 (#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 resolve/reject 结果为 undefined 时触发一个错误,以匹配 loader 和 action 必须返回值或 null 的行为 (#10690)
  • 正确处理被普通导航中断的 fetcher 重定向 (#10674)
  • 初始加载的 fetcher 不应在 GET 导航时自动重新验证 (#10688)
  • 在使用 <ScrollRestoration> 模拟哈希滚动时,正确解码元素 ID (#10682)
  • Typescript: 增强 Route.lazy 的返回类型,禁止返回空对象 (#10634)
  • SSR: 支持对 Error 的子类(如 ReferenceError/TypeError)进行正确的水合 (#10633)

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

v6.14.1

日期: 2023-06-30

补丁变更

  • 修复 unstable_useBlocker 在与不稳定的 blocker 函数一起使用时出现的循环问题 (#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)
  • 修复 unstable_useBlockerStrictMode 中的 key 问题 (#10573)
  • 修复 generatePath 在传递数值 0 作为参数时的问题 (#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 本质上是一个补丁发布,但由于我们添加了一个新的未来标志,所以它带来了 SemVer 的次要版本号提升。

future.v7_startTransition

简而言之6.13.06.12.0 相同,但是我们将 React.startTransition 的使用移到了一个可选的 future.v7_startTransition 未来标志 后面,因为我们发现在实际应用中,有些应用程序目前使用 Suspense 的方式与 React.startTransition 不兼容。

因此,在 6.13.0 中,默认行为将不再利用 React.startTransition

<BrowserRouter>
  <Routes>{/*...*/}</Routes>
</BrowserRouter>

<RouterProvider router={router} />

如果您希望启用 React.startTransition,请将未来标志传递给您的路由器组件。

<BrowserRouter future={{ v7_startTransition: true }}>
  <Routes>{/*...*/}</Routes>
</BrowserRouter>

<RouterProvider router={router} future={{ v7_startTransition: true }}/>

我们建议大家尽早采用这个标志,以便更好地兼容 React 的并发模式,但如果您遇到问题,可以继续不使用 React.startTransition 直到 v7。问题通常归结于在渲染周期中创建全新的 promise,所以如果您在选择加入 React.startTransition 时遇到问题,您应该将您的 promise 创建移出渲染周期,或者将其放在 useMemo 后面。

次要变更

  • React.startTransition 的使用移至一个未来标志之后 (#10596)

补丁变更

  • 解决 webpack/terser 在生产模式下对 React.startTransition 的压缩 bug (#10588)

完整变更日志: v6.12.1...v6.13.0

v6.12.1

日期: 2023-06-08

[!WARNING] 请使用 6.13.0 或更高版本,而不是 6.12.0/6.12.1。这些版本存在一些 Webpack 构建/压缩问题,导致构建失败或在您的生产包中产生无效的压缩代码。详情请见 #10569#10579

补丁变更

  • 调整 React.startTransition 的特性检测,以修复 webpack + react 17 的编译错误 (#10569)

完整变更日志: v6.12.0...v6.12.1

v6.12.0

日期: 2023-06-06

[!WARNING] 请使用 6.13.0 或更高版本,而不是 6.12.0/6.12.1。这些版本存在一些 Webpack 构建/压缩问题,导致构建失败或在您的生产包中产生无效的压缩代码。详情请见 #10569#10579

变更内容

支持 React.startTransition

6.12.0 版本中,我们通过将内部路由器状态更新包裹在 React.startTransition 中,为挂起(suspending)组件提供了更好的支持。这意味着,例如,如果目标路由中的某个组件挂起,而您没有提供 Suspense 边界来显示回退界面,React 将延迟渲染新 UI,并继续显示旧 UI,直到该异步操作完成。这对于等待图片或 CSS 文件加载等情况可能很有用(技术上讲,您也可以用它来加载数据,但我们仍然推荐使用 loader 来实现此目的 😀)。要快速了解此用法,请查看 Ryan 在 Twitter 上的演示

次要变更

  • React.startTransition 包裹内部路由器状态更新 (#10438)

补丁变更

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

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

v6.11.2

日期: 2023-05-17

补丁变更

  • 修复 <RouterProvider> 内后代 <Routes> 中的 basename 重复问题 (#10492)
  • 修复当存在哈希值时,初始数据加载不会启动的 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)
  • “相同哈希”的导航不再重新运行 loader,以匹配浏览器行为(即 /path#hash -> /path#hash) (#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 在 location 变化时现在是稳定的,因为我们可以通过 @remix-run/router 实例处理相对路由,从而摆脱对 useLocation() 的依赖 (#10336)
    • 当使用 BrowserRouter 时,这些钩子在 location 变化时仍然是不稳定的,因为它们仍然依赖于 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)
  • 通过在布局 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,使其除了检查 errorElement 外,还检查路由上的 ErrorBoundary (#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 元素。如果你愿意,可以向新的 ComponentErrorBoundary 字段传递一个 React 组件。两者在功能上没有区别,所以请选择你喜欢的方式 😀。你不应该同时定义两者,但如果这样做了,Component/ErrorBoundary 将会“获胜”。

JSON 语法示例

// Both of these work the same:
const elementRoutes = [{
  path: '/',
  element: <Home />,
  errorElement: <HomeError />,
}]

const componentRoutes = [{
  path: '/',
  Component: Home,
  ErrorBoundary: HomeError,
}]

function Home() { ... }
function HomeError() { ... }

JSX 语法示例

// Both of these work the same:
const elementRoutes = createRoutesFromElements(
  <Route path='/' element={<Home />} errorElement={<HomeError /> } />
);

const componentRoutes = createRoutesFromElements(
  <Route path='/' Component={Home} ErrorBoundary={HomeError} />
);

function Home() { ... }
function HomeError() { ... }

引入懒加载路由模块

为了保持你的应用程序包体积小并支持路由的代码分割,我们引入了一个新的 lazy() 路由属性。这是一个异步函数,它会解析你的路由定义中与路由匹配无关的部分(loaderactionelement/ComponenterrorElement/ErrorBoundaryshouldRevalidatehandle)。

懒加载路由在初始加载时以及在导航或 fetcher 调用的 loadingsubmitting 阶段进行解析。你不能懒加载定义路由匹配的属性(pathindexchildren),因为我们只在匹配到已知路由后才执行你的懒加载路由函数。

你的 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

补丁变更

  • 改进上下文提供程序的 memoization,以避免不必要的重新渲染(#9983
  • 修复 generatePath 在某些情况下错误应用参数的问题(#10078
  • [react-router-dom-v5-compat] 添加遗漏的数据路由器 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 导航的不准确控制台警告,并更新活动 blocker 逻辑(#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 中用于设置 cookie 的 X-Remix-Revalidate 标头)。它没有在同样适用于导航 loader 重新验证的*隐式*重新验证场景下被正确调用,例如搜索参数更改或点击已在当前页面的链接。现在,它在这些额外场景中也能被正确调用。
    • 传递的参数不正确且相互不一致,因为 current*/next* 参数反映的是静态的 fetcher.load URL(因此是相同的)。相反,它们应该反映触发重新验证的导航(就像 form* 参数那样)。这些参数现在能正确反映触发的导航。
  • 修复通过 useSearchParams 移除搜索参数时的错误(#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 useLayoutEffectconsole.error#9820

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

v6.7.0

日期: 2023-01-18

次要变更

  • 添加 unstable_useBlocker/unstable_usePrompt hooks,用于在应用 location 源内阻止导航(#9709, #9932
  • <Form> 添加 preventScrollReset 属性(#9886

补丁变更

  • useBeforeUnload 添加了透传的事件监听器选项参数(#9709
  • 修复存在可选参数时 generatePath 的问题(#9764
  • 更新 <Await>,使其子函数返回结果接受 ReactNode#9896
  • 改进了 actions/loaders 中绝对重定向 URL 的检测(#9829
  • 修复使用 memory history 时的 URL 创建问题(#9814
  • 修复提交重定向时的滚动重置问题(#9886
  • 修复同源绝对重定向的 404 错误(#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 重定向到当前 location 时重置 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() hook(#9664

补丁变更

  • 支持大写的 <Form method>useSubmit 方法值(#9664
  • 修复 <button formmethod> 表单提交的覆盖问题(#9664
  • 修复提交时的显式 replace 和提交到新路径时的 PUSH 问题(#9734
  • 禁止在 errorElement 中使用 useLoaderData#9735
  • StaticRouterProvider 正确地进行 Error 对象的 hydration(#9664
  • 对于带有 hydrationData 的 SSR 应用,跳过初始的滚动恢复(#9664
  • 修复了几个在出错时 loader/action 数据未被正确清除的错误(#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
  • 修复发送到重新验证 loader 的请求,使其反映为 GET 请求(#9660
  • 修复深度嵌套的可选段问题(#9727
  • GET 表单现在在加载导航时会暴露一个 submission(#9695
  • 修复多个错误冒泡到同一错误边界时的错误边界跟踪问题(#9702

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

v6.4.5

日期: 2022-12-07

补丁变更

  • 修复发送到重新验证 loader 的请求,使其反映为 GET 请求(#9680
  • 移除 instanceof Response 检查,改为使用 isResponse#9690
  • 修复在 Cloudflare Pages 或其他非浏览器环境中创建 URL 的问题(#9682, #9689
  • 向静态处理程序的 query/queryRoute 添加 requestContext 支持(#9696
    • 请注意,queryRoute(path, routeId) 的不稳定 API 已更改为 queryRoute(path, { routeId, requestContext })

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

v6.4.4

日期: 2022-11-30

补丁变更

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

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

v6.4.3

日期: 2022-11-01

补丁变更

  • 使用 createHashRouter 时生成正确的 <a href> 值(#9409
  • 更好地处理 URL 和路由路径中特殊字符的编码/匹配(#9477, #9496
  • index 路由同时有 path 时,生成正确的 formAction 路径名(#9486
  • 遵循 NavLink 上的 relative=path 属性(#9453
  • 修复根 URL 的 NavLink 行为(#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

日期: 2022-10-06

补丁变更

  • useFormAction 中遵循 basename#9352
  • 修复 IndexRouteObjectNonIndexRouteObject 类型,使 hasErrorElement 成为可选(#9394
  • 增强了对无效使用数据路由器 hooks 的控制台错误消息(#9311
  • 如果一个索引路由有子路由,将会导致运行时错误。我们加强了 RouteObject/RouteProps 类型,以便在 TypeScript 中暴露此错误。(#9366

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

v6.4.1

日期: 2022-09-22

补丁变更

  • 保留 initialEntries 的 state(#9288
  • 对于向索引路由的 fetcher get 提交,保留 ?index#9312

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

v6.4.0

日期: 2022-09-13

变更内容

Remix 数据 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> 组件内返回作用域内的 location(#9094
  • 如果定义了 <Link replace> 属性,则遵循它(#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
  • 修复了在某些情况下索引路由缺少路由上下文的错误(#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 脚本中可以使用命名导入(查看提交)。

补丁变更

  • 修复了 RoutePropselement 类型,它应该是一个 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 属性。这个值会传递给子路由,并可以通过新的 useOutletContext hook 访问。详情请见 API 文档。(#8461
  • <NavLink> 现在可以接收一个子函数以访问其属性。(#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

补丁变更

  • <Link> 添加了 reloadDocument 属性。这使得 <Link> 能够像普通锚点标签一样,在导航后重新加载文档,同时保持相对 to 的解析(#8283

完整更新日志: 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
编辑