CHANGELOG.md
在此页上

React Router 发布历史

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

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

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

v7.5.1

日期:2025-04-17

补丁更改

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

    • 如果你将这些属性的代码移到单独的文件中,由于这些水合属性(如果在水合期间路由不存在)之前也未被使用,你可以完全避免下载它们。例如

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

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

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

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

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

不稳定更改

⚠️ 不稳定功能不建议用于生产环境

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

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

v7.5.0

日期:2025-04-04

更新内容

route.lazy 对象 API

我们引入了一个新的 route.lazy API,它允许你对路由属性的懒加载进行更精细的控制,这是 route.lazy() 函数签名无法实现的。这对于框架模式和对性能要求较高的库模式应用非常有用。

createBrowserRouter([
  {
    path: "/show/:showId",
    lazy: {
      loader: async () => (await import("./show.loader.js")).loader,
      action: async () => (await import("./show.action.js")).action,
      Component: async () => (await import("./show.component.js")).Component,
    },
  },
]);

⚠️ 如果你采用了已移除并由 route.lazy.unstable_middleware 取代的 route.unstable_lazyMiddleware API,这是一个破坏性更改。有关更多信息,请参阅下面的“不稳定更改”部分。

微小更改

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

补丁更改

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

不稳定更改

⚠️ 不稳定功能不建议用于生产环境

  • react-router - 引入 future.unstable_subResourceIntegrity flag,启用为浏览器将加载的脚本生成带有 integrity 的 importmap (#13163)
  • react-router - 移除对 route.unstable_lazyMiddleware 属性的支持 (#13294)
    • 为了懒加载中间件,你可以使用新的基于对象的 route.lazy.unstable_middleware API
  • @react-router/dev - 当 future.unstable_viteEnvironmentApi 启用时,确保在配置了自定义 Vite base 时,开发环境中的关键 CSS 可以正常工作 (#13305)

按包划分的更改

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

v7.4.1

日期:2025-03-28

安全公告

修复了一个安全漏洞,该漏洞允许通过 HostX-Forwarded-Host 头部进行 URL 操作和潜在的缓存污染,原因是端口净化不足。

补丁更改

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

不稳定更改

⚠️ 不稳定功能不建议用于生产环境

  • react-router - 修复 unstable_MiddlewareFunction 上的类型,以避免当中间件没有返回值时出现的类型错误 (#13311)
  • react-router - 添加对 route.unstable_lazyMiddleware 函数的支持,以允许懒加载中间件逻辑 (#13210)
    • ⚠️ 我们目前不建议采用此 API,因为它在中间件稳定版本发布之前很可能会更改
    • ⚠️ 如果你的应用目前从 route.lazy 返回 unstable_middleware,这可能是一个破坏性更改
    • route.lazy 的返回值不再支持 route.unstable_middleware 属性
    • 如果你想懒加载中间件,必须使用 route.unstable_lazyMiddleware
  • @react-router/dev - 当 future.unstable_middlewarefuture.unstable_splitRouteModules 都启用时,在可能的情况下将 unstable_clientMiddleware 路由导出分割到单独的 chunks 中 (#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 - 如果一个 middleware 抛出错误,确保我们只通过 next() 向上冒泡错误本身,并且不再暴露 MiddlewareError 的实现细节 (#13180)
    • ⚠️ 如果你在 middleware 中 catchnext() 函数抛出的错误,这可能是一个破坏性变更。
  • react-router - 修复启用 middleware 时 RequestHandlerloadContext 参数类型 (#13204)
  • react-router - 更新 Route.unstable_MiddlewareFunction 的返回值类型为 Response | undefined,而非 Response | void (#13199)
  • @react-router/dev - 当 future.unstable_splitRouteModules 设置为 "enforce" 时,允许根路由同时导出可拆分和不可拆分模块,因为它们始终位于同一个 chunk 中 (#13238)
  • @react-router/dev - 当 future.unstable_viteEnvironmentApi 启用时,允许覆盖默认 SSR 环境的插件(例如 @cloudflare/vite-plugin)可以放置在 React Router 插件之前或之后 (#13183)

按包分类的变化

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

v7.3.0

日期:2025-03-06

次要变化

  • patchRoutesOnNavigation 添加 fetcherKey 参数 (#13061)

补丁变化

  • react-router - 检测和处理新部署在活跃会话期间出现的 manifest 倾斜问题 (#13061)
    • 在 framework 模式下,延迟路由发现(Lazy Route Discovery)现在可以在新部署后检测活跃会话中的 manifest 版本不匹配
    • 在导航到未发现的路由时,这种不匹配将触发目标路径的文档重载
    • 在对未发现路由执行 fetcher 调用时,这种不匹配将触发当前路径的文档重载
  • react-router - 在 SPA 模式下,跳过开发服务器中的资源路由流程 (#13113)
  • react-router - 修复使用 basename 时单次 fetch _root.data 请求的问题 (#12898)
  • react-router - 修复包含 Records 的 loaderDataactionData 的类型定义问题 (#13139)
    • ⚠️ 对于已采用 unstable_SerializesTo 的用户来说,这是一个破坏性变更 - 更多信息请参见下方“不稳定变化”部分中的说明。
  • @react-router/dev - 修复对自定义客户端 build.rollupOptions.output.entryFileNames 的支持 (#13098)
  • @react-router/dev - 修复当 serverBundles 选项已被配置或由 preset(例如 @vercel/react-routervercelPreset)提供时,prerender 选项的使用问题 (#13082)
  • @react-router/dev - 修复对自定义 build.assetsDir 的支持 (#13077)
  • @react-router/dev - 移除未使用的依赖项 (#13134)
  • @react-router/dev - 在“SPA 模式”服务器构建中,除了根路由外,对所有路由进行 stub,以避免当路由模块或其依赖项导入非 SSR 友好模块时出现问题 (#13023)
  • @react-router/dev - 移除未使用的 Vite 文件系统 watcher (#13133)
  • @react-router/dev - 修复当 serverBundles 选项已配置时,对自定义 SSR 构建输入的支 (#13107)
    • ⚠️ 请注意,对于同时使用 future.unstable_viteEnvironmentApiserverBundles 选项的用户,服务器 bundle ID 中不再支持连字符,因为它们还需要是有效的 Vite 环境名称。
  • @react-router/dev - 通过从开发服务器请求中剥离 HTTP/2 伪头部来修复使用 HTTPS 时的开发服务器问题 (#12830)
  • @react-router/dev - 当使用 cloudflareDevProxy Vite 插件时,在第一次开发服务器请求时延迟加载 Cloudflare 平台代理,以避免创建不必要的 workerd 进程 (#13016)
  • @react-router/dev - 修复 layout 路由及其对应 index 路由在 typegen 中出现重复条目的问题 (#13140)
  • @react-router/express - 更新 expresspeerDependency 以包含 v5 (https://github.com/remix-run/react-router/pull/13064) (#12961)

不稳定变化

⚠️ 不稳定功能不建议用于生产环境

  • react-router - 为客户端数据路由器添加 context 支持 (unstable) (#12941)
  • react-router - 支持在路由上使用 middleware (unstable) (#12941)
  • @react-router/dev - 修复当 ssr 环境已被另一个插件配置为自定义的 Vite.DevEnvironment 而非默认的 Vite.RunnableDevEnvironment 时,使用 future.unstable_viteEnvironmentApi 出现的错误 (#13008)
  • @react-router/dev - 当 future.unstable_viteEnvironmentApi 启用且 ssr 环境的 optimizeDeps.noDiscovery 被禁用时,定义 optimizeDeps.entriesoptimizeDeps.include (#13007)

客户端 context (unstable)

您的应用程序的 clientLoader/clientAction 函数(或 library 模式下的 loader/action)现在将在客户端接收一个 context 参数。这是 unstable_RouterContextProvider 的一个实例,您可以将其与类型安全的上下文(类似于 React.createContext)一起使用,并且在与相应的 unstable_clientMiddleware API 结合使用时最为有用。

import { unstable_createContext } from "react-router";

type User = {
  /*...*/
};

const userContext = unstable_createContext<User>();

const sessionMiddleware: Route.unstable_ClientMiddlewareFunction = async ({
  context,
}) => {
  let user = await getUser();
  context.set(userContext, user);
};

export const unstable_clientMiddleware = [sessionMiddleware];

export function clientLoader({ context }: Route.ClientLoaderArgs) {
  let user = context.get(userContext);
  let profile = await getProfile(user.id);
  return { profile };
}

类似于服务器端请求,每次导航(或 fetcher 调用)都会创建一个新的 context。如果您有希望在每次请求的 context 中填充的初始数据,可以在应用程序的根部提供一个 unstable_getContext 函数。

  • Library 模式 - createBrowserRouter(routes, { unstable_getContext })
  • Framework 模式 - <HydratedRouter unstable_getContext>

此函数应返回一个类型为 unstable_InitialContext 的值,它是一个 Map<unstable_RouterContext, unknown>,包含 context 及其初始值。

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

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

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

Middleware (unstable)

Middleware 是通过 future.unstable_middleware 标志实现的。要启用它,您必须在 react-router.config.ts 文件中启用该标志和相应的类型。

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

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

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

⚠️ Middleware 是不稳定的,不应在生产环境中使用。对于 clientMiddleware 的路由模块加载,至少存在一个已知的性能退化问题,我们将在稳定版本发布前解决。

⚠️ 启用 middleware 会对传递给您的 loader/action 函数的 context 参数造成破坏性变更 - 更多信息请参见下方。

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

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

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

这是一个简单的客户端日志记录 middleware 示例,可以放在根路由上。

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

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

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

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

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

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

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

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

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

您可以从 middleware 中抛出一个 redirect 来短路任何剩余的处理。

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

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

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

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

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

  return res;
};

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

Middleware context 参数

启用 middleware 后,您的应用程序将在 loaders 和 actions 中使用不同类型的 context 参数,以提供更好的类型安全性。context 不再是 AppLoadContext,而是一个 ContextProvider 的实例,您可以将其与类型安全的上下文(类似于 React.createContext)一起使用。

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

let sessionContext = unstable_createContext<Session>();

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

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

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

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

let adapterContext = unstable_createContext<MyAdapterContext>();

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

unstable_SerializesTo

unstable_SerializesTo 为 Single Fetch 添加了一种注册自定义序列化类型的方式,供 Apollo 等其他库和框架作者使用。它使用了一种标记类型(branded type)实现,该类型的标记属性(branded property)被设为可选,以便轻松地转换(casting)任意值。

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

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

然而,这破坏了 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 数据,我们现在还为路由上的 HydrateFallback 组件公开了 loaderData 作为可选 prop。

  • 只要 HydrateFallback 正在渲染是因为*子*路由正在加载,此值就会被定义。
  • 如果 HydrateFallback 正在渲染是因为路由本身有自己的正在注水的 clientLoader,此值将是 undefined
    • 在 SPA 模式下,这将允许您将 loader 根数据渲染到 SPA 模式的 HTML 文件中。

次要更新

  • react-router - 新增类型安全的 href 工具函数,确保链接指向应用中的实际路径 (#13012)
  • @react-router/dev - 当使用 ssr:false 预渲染 / 路由时,生成一个“SPA 回退”HTML 文件 (#12948)
  • @react-router/dev - 允许在 SPA 模式下根路由中使用 loader,因为它可以在构建时调用/服务器渲染 (#12948)
    • Route.HydrateFallbackProps 现在也接收 loaderData

补丁更新

  • react-router - 对所有 ssr:false 应用(不仅仅是“SPA 模式”)禁用路由懒发现,因为没有运行时服务器来处理通过搜索参数配置的 __manifest 请求 (#12894)
    • 我们之前只在“SPA 模式”下禁用了此功能,但现在意识到它应该适用于所有 ssr:false 应用。
    • 在那些 prerender 场景中,我们会预渲染 /__manifest 文件,但这对静态文件服务器的行为做了一些不必要的假设。
  • react-router - 在 SPA 模式下不应用 Single Fetch 重新验证去优化(revalidation de-optimization),因为没有服务器 HTTP 请求 (#12948)
  • react-router - 正确处理跨越预渲染/SPA 边界的重新验证 (#13021)
    • 在某些路由被预渲染而另一些路由通过 SPA 回退提供服务的“混合”应用中,如果路径未被预渲染,我们需要避免发起 .data 请求,因为该请求会返回 404。
    • 然而,我们在客户端不知道所有的预渲染路径。
      • ssr:false 模式下,所有的 loader 数据都是静态的,因为它是在构建时生成的。
      • 路由必须使用 clientLoader 来实现任何动态功能。
      • 因此,如果一个路由只有 loader 而没有 clientLoader,我们默认禁用重新验证,因为没有新的数据可以检索。
      • 如果我们在 Single Fetch 的 dataStrategy 中没有带有 shouldLoad=true 的服务器端 loader,我们会短路并跳过 Single Fetch 的 .data 请求逻辑。
      • 这确保了路由在提交后不会导致 .data 请求返回 404。
  • react-router - 在设置 ssr:false 时,使开发服务器行为与静态文件服务器行为保持一致 (#12948)
    • 当不存在 prerender 配置时,仅 SSR 到根 HydrateFallback(SPA 模式)。
    • 当存在 prerender 配置但当前路径未被预渲染时,仅 SSR 到根 HydrateFallback(SPA 回退)。
    • 对非预渲染路径的 .data 请求返回 404。
  • react-router - 在框架模式下改进 CSS 副作用的预加载性能 (#12889)
  • react-router - 在路由懒发现中正确处理中断的 manifest 请求 (#12915)
  • @react-router/dev - 在 Vite 配置中处理自定义的 envDir (#12969)
  • @react-router/dev - 修复 CLI 解析,允许使用不带参数的 npx react-router 命令 (#12925)
  • @react-router/dev - 使用 prerender:true 时跳过仅包含 action 的资源路由 (#13004)
  • @react-router/dev - 增强在使用 ssr:false 时对无效导出的检测 (#12948)
    • 在所有 ssr:false 的路由中,headers/action 函数都是被禁止的,因为没有运行时服务器来运行它们。
    • loader 函数更复杂,取决于给定的路由是否被预渲染。
      • 当使用 ssr:false 但没有 prerender 配置时,只有 root 路由可以有 loader
      • 当使用 ssr:false 并带有 prerender 配置时,只有被 prerender 路径匹配到的路由才能有 loader
  • @react-router/dev - 在 ssr:false + prerender 应用中,针对以下边缘情况在构建时报错:(#13021)
    • 父路由只有 loader(没有 clientLoader
    • 父路由被预渲染
    • 父路由有未被预渲染的子路由
    • 这意味着当子路径通过 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 prop 中 id 的值。
      • 例如,路径 /a/1/b/2/c/3 在运行时将产生 { id: 3 } 的值。
    • 之前,为参数生成的类型错误地将重复参数建模为数组。
      • 例如,/a/1/b/2/c/3 生成的类型是 { id: [1,2,3] }
    • 为了与运行时行为保持一致,生成的类型现在正确地建模了路径参数的“最后一个生效”(last one wins)语义。
      • 例如,/a/1/b/2/c/3 现在生成的类型是 { id: 3 }
  • @react-router/dev - 修复 react-router --version 命令加载 package.json 的路径问题 (#13012)

不稳定更新

⚠️ 不稳定功能不建议用于生产环境

  • react-router - 为库作者添加 unstable_SerializesTo 标记类型,用于注册可通过 React Router 流式格式 (turbo-stream) 序列化的类型 (#12264)
  • @react-router/dev - 通过 future.unstable_splitRouteModules 添加在框架模式下分割路由模块的不稳定支持 (#11871)
  • @react-router/dev - 添加 future.unstable_viteEnvironmentApi 标志以启用实验性的 Vite Environment API 支持 (#12936)

分割路由模块 (不稳定)

⚠️ 此功能目前处于不稳定状态,通过 future.unstable_splitRouteModules 标志启用。我们欢迎任何感兴趣的用户在本地试用并提供反馈,但目前不建议在生产环境中使用。

如果您确实选择在生产环境中使用此标志,请确保对您的生产构建进行充分测试,以确保优化按预期工作。

路由模块 API 的一个便利之处在于,路由所需的一切都在一个文件中。不幸的是,在使用 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 调用,而默认组件导出则大得多。这对性能是一个问题,因为它意味着如果我们在客户端导航到这个路由,必须在 client loader 开始运行之前下载整个路由模块。

以时间线形式可视化:

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

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

相反,我们希望将其优化为如下所示:

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

为了实现此优化,React Router 会在生产构建过程中将路由模块分割成多个更小的模块。在这种情况下,我们最终会得到两个独立的虚拟模块——一个用于 client loader,另一个用于组件及其依赖。

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

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

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

现在这些可以作为单独的模块使用,client loader 和组件可以并行下载。这意味着 client loader 一旦就绪就可以立即执行,而无需等待组件。

当使用更多路由模块 API 时,此优化会更加显著。例如,当使用 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} />;
}

此路由仍然有效,但由于 client loader 和组件都依赖于同一文件中定义的 shared 函数,它将被反优化为一个单个路由模块。

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

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

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

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

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

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

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

import { shared } from "./shared";

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

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

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

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

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

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

- clientLoader

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

按包划分的变更

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

v7.1.5

日期:2025-01-31

补丁变更

  • react-router - 修复了在 7.1.4 中通过 #12800 引入的回归问题,该问题导致使用 Lazy Route Discovery (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 - 在 Single Fetch 响应中正确处理无法包含主体的状态码 (204 等) (#12760)
  • react-router - 抛出 data() 结果时,将 header 正确地作为 errorHeaders 冒泡 (#12846)
    • 如果 headers 也返回了 Set-Cookie headers,则避免重复
  • react-router - 停止对返回原始字符串/对象的资源路由报错,转而将其序列化为 text/plainapplication/json 响应 (#12848)
    • 这仅适用于在不带 .data 扩展名的情况下作为资源路由访问时
    • 当从 Single Fetch .data 请求访问时,它们仍将通过 turbo-stream 进行编码
  • react-router - 优化 Lazy Route Discovery 路径发现,优先使用 body 级别的单个 querySelectorAll 调用,而非子树级别的多次调用 (#12731)
  • react-router - 通过尽可能跳过冗余的 matchRoutes 调用来优化路由匹配 (#12800, #12882)
  • react-router - 内部重组以清理一些重复的路由模块类型 (#12799)

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

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 - 在从 loader 数据中过滤掉 redirect 响应时,不依赖于 symbol (#12694)
    • 此前,一些项目会遇到类型检查错误,例如
      error TS4058: Return type of exported function has or is using name 'redirectSymbol' from external module "node_modules/..." but cannot be named.
      
    • 现在 symbol 不再用于 redirect 响应类型,这些错误应该不会再出现
  • @react-router/dev - 修复 Vite v6 中的默认外部条件 (#12644)
    • 这修复了某些 npm 包的解析问题
  • @react-router/dev - 修复路径缺少前导斜杠时预渲染 html/data 文件的不匹配问题 (#12684)
  • @react-router/dev - 在运行时启用时使用 module-sync 服务器条件。这修复了在 Node 22.10.0+ 上开发期间,当使用依赖于 React Router 的库时出现的 React 上下文不匹配问题(例如 useHref() may be used only in the context of a <Router> component.)(#12729)
  • @react-router/dev - 修复 react-refresh Source map (#12686)

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

v7.1.1

日期:2024-12-23

补丁变更

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

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

v7.1.0

日期:2024-12-20

次要变更

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

补丁变更

  • react-router - 抛出未包裹的 Single Fetch redirect 以与 Single Fetch 之前的行为对齐 (#12506)
  • react-router - 推断 loader 数据类型时忽略重定向 (#12527)
  • react-router - 移除 <Link prefetch> 警告,该警告在 Lazy Route Discovery 环境中容易产生误报 (#12485)
  • create-react-router - 修复缺少 fs-extra 依赖项的问题 (#12556)
  • @react-router/dev/@react-router/serve - 如果尚未设置,则正确初始化 NODE_ENV 以兼容 React 19 (#12578)
  • @react-router/dev - 从 ServerRouter 中移除遗留/未使用的 abortDelay 属性,并更新默认的 entry.server.tsx 以使用 Single Fetch 的新 streamTimeout 值 (#12478)
    • abortDelay 功能已在 v7 中移除,因为它与 Remix v2 的 defer 实现耦合,但此属性的移除被遗漏了
    • 如果你的 entry.server 文件中仍在使用此属性,你的应用程序可能没有像你预期那样中断流,你需要采用 Single Fetch 引入的新 streamTimeout
  • @react-router/fs-routes - 如果 routes 目录缺失,在 flatRoutes 中抛出错误 (#12407)

按包划分的变更

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

v7.0.2

日期:2024-12-02

补丁变更

  • react-router - 临时仅在导出映射中使用一个构建,以便包可以对 react router 有 peer dependency (#12437)
  • @react-router/dev - 支持 moduleResolution Node16NodeNext (#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 dev server 重启时清理 typegen 文件监听器 (#12331)
  • @react-router/dev - 将路由 error 作为 prop 传递给 ErrorBoundary (#12338)

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

v7.0.0

日期:2024-11-21

重大变更

包重组

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

移除的适配器重新导出

Remix v2 过去常常通过各种运行时包 (node, cloudflare, deno) 重新导出所有通用的 @remix-run/server-runtime API,这样您就不需要在 package.json 中额外添加 @remix-run/server-runtime 依赖项。随着包合并到 react-router 中,这些通用 API 现在不再通过运行时适配器重新导出。您应该从 react-router 导入所有通用 API,并且只从运行时包导入运行时特定的 API。

// Runtime-specific APIs
import { createFileSessionStorage } from "@react-router/node";
// Runtime-agnostic APIs
import { redirect, useLoaderData } from "react-router";

移除的 API

以下 API 已在 React Router v7 中移除

  • json
  • defer
  • unstable_composeUploadHandlers
  • unstable_createMemoryUploadHandler
  • unstable_parseMultipartFormData

最低版本

React Router v7 需要以下最低版本:

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

已采用的 Future Flag 行为

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

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

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

Vite 编译器

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

重命名了 vitePlugincloudflareDevProxyVitePlugin

对于迁移到 React Router 的 Remix 用户,vitePlugincloudflareDevProxyVitePlugin 导出已被重命名和移动(#11904)。

-import {
-  vitePlugin as remix,
-  cloudflareDevProxyVitePlugin,
-} from "@remix/dev";

+import { reactRouter } from "@react-router/dev/vite";
+import { cloudflareDevProxy } from "@react-router/dev/vite/cloudflare";

移除了 manifest 选项

对于迁移到 React Router 的 Remix 用户,Vite 插件的 manifest 选项已被移除。manifest 选项已被更强大的 buildEnd hook 取代,因为它会传递 buildManifest 参数。如果需要,您仍然可以将构建 manifest 写入磁盘,但您很可能会发现将依赖于构建 manifest 的任何逻辑写入 buildEnd hook 本身更方便。(#11573

如果您之前使用了 manifest 选项,可以用一个将 manifest 写入磁盘的 buildEnd hook 来替代它,示例如下:

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

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

暴露的 Router Promises

由于 React 19 将在渲染阶段对处理 promise 提供一流支持(通过 React.useuseAction),我们现在可以放心地暴露先前返回 undefined 的 API 所对应的 promise。

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

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

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

export const routes: RouteConfig = flatRoutes();

如果您想从文件系统路由迁移到基于配置的路由,可以通过将异步函数 flatRoutes 的结果展开到基于配置的路由数组中来混合搭配使用。

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

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

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

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

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

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

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

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

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

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

类型安全改进

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

更多详情请参阅操作指南 > 路由模块类型安全解释说明 > 类型安全

预渲染

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

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

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

主要变更(react-router

  • 移除了原始的 defer 实现,转而使用通过 single fetch 和 turbo-stream 的原生 promise(#11744)。
    • 这将从 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 flag(#11696
  • 暴露以下 API 的底层 router promise,以便在 React 19 API 中进行组合:(#11521
  • 移除 future.v7_normalizeFormMethod future flag(#11697
  • 导入/导出清理(#11840
    • 移除了之前是 @remix-run/router 公共 API 的以下导出:
      • types
        • 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 flag(#11725
    • 这也移除了 <RouterProvider fallbackElement> prop。
      • 迁移时,将 fallbackElement 移到您的根路由上的 hydrateFallbackElement/HydrateFallback
    • 另外值得注意的是,这个 future flag 还有一个相关的重大变更:
      • 在没有 future.v7_partialHydration(使用 fallbackElement 时)的情况下,state.navigation 在初始加载期间会被填充。
      • 使用 future.v7_partialHydration 后,state.navigation 在初始加载期间保持 "idle" 状态。
  • 移除 future.v7_relativeSplatPath future flag(#11695
  • 移除剩余的 future flags(#11820
    • React Router v7_skipActionErrorRevalidation
    • Remix v3_fetcherPersist, v3_relativeSplatPath, v3_throwAbortReason
  • createRemixStub 重命名为 createRoutesStub#11692
  • 移除 @remix-run/router 中已废弃的 detectErrorBoundary 选项,转而使用 mapRouteProperties#11751)。
  • 添加 react-router/dom 子路径导出,以正确地将 react-dom 启用为可选的 peerDependency#11851)。
    • 这确保我们不会在 <RouterProvider> 中盲目地 import ReactDOM from "react-dom" 来访问 ReactDOM.flushSync(),因为这会破坏非 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 flag(#11731
  • 允许 loaders 和 actions 返回 undefined#11680#12057
  • entry.client 中使用 createRemixRouter/RouterProvider 替代 RemixBrowser (#11469)
  • 移除已弃用的 json 工具函数 (#12146)
    • 如果你仍然需要在应用中构建 JSON 响应,可以使用 Response.json

主要变更 (@react-router/*)

  • 移除 future.v3_singleFetch 标志 (#11522)
  • 停止支持 Node 16 和 18,最低 Node 版本更新至 20 (#11690, #12171)
    • 移除 installGlobals(),因为它不再是必需的
  • 为所有包添加 exports 字段(#11675
  • 不再通过不同的运行时/适配器包重新导出 react-router 中的 API (#11702)
  • 对于迁移到 React Router 的 Remix 用户,现在使用 cookie 和 session API 时需要 Web Crypto API 提供的 crypto 全局对象
    • 这意味着以下 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 - 对于迁移到 React Router 的 Remix 用户,@remix-run/cloudflare-pages 中的所有导出现在都在 @react-router/cloudflare 包中提供给 React Router 用户。不再有单独的 Cloudflare Pages 包。 (#11801)
  • @react-router/cloudflare - @remix-run/cloudflare-workers 包已被弃用。迁移到 React Router 的 Remix 用户应直接使用 @react-router/cloudflare 包。关于如何在 Cloudflare Workers 环境中使用 @react-router/cloudflare 的指南,请参阅 Cloudflare Workers 模板。 (#11801)
  • @react-router/dev - 对于迁移到 React Router 的 Remix 用户,vitePlugincloudflareDevProxyVitePlugin 导出已被重命名并移动。 (#11904)
  • @react-router/dev - 对于使用 Vite 插件的 buildEnd 钩子并迁移到 React Router 的 Remix 用户,解析后的 reactRouterConfig 对象不再包含 publicPath 属性,因为它属于 Vite,而不是 React Router (#11575)
  • @react-router/dev - 对于迁移到 React Router 的 Remix 用户,Vite 插件的 manifest 选项已被移除 (#11573)
  • @react-router/dev - 更新默认 isbot 版本至 v5 并停止支持 isbot@3 (#11770)
    • 如果你的 package.json 中有 isbot@4isbot@5
      • 你无需进行任何更改
    • 如果你的 package.json 中有 isbot@3 并且你的代码库中有自己的 entry.server.tsx 文件
      • 你无需进行任何更改
      • 你可以独立于 React Router v7 升级到 isbot@5
    • 如果你的 package.json 中有 isbot@3 并且你的代码库中没有自己的 entry.server.tsx 文件
      • 你正在使用 React Router v7 提供的内部默认入口,你需要将 package.json 中的 isbot 升级到 isbot@5
  • @react-router/dev - 对于迁移到 React Router 的 Remix 用户,Vite manifests(即 .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 manifests 移动到根目录 build/.vite 下,以避免在生产环境中意外提供它们,特别是客户端构建的 manifests。后来通过额外的逻辑进行了改进,即在构建过程结束时删除这些 Vite manifest 文件,除非应用的 Vite 配置中启用了 Vite 的 build.manifest。这大大降低了在生产环境中意外提供 Vite manifests 的风险,因为只有明确请求时它们才会存在。因此,我们现在可以假定用户知道需要自行管理这些额外文件,React Router 可以安全地生成更标准的 Vite 构建输出。

次要变更

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

补丁变更

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

按包划分的变更

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

React Router v6 发布

v6.30.0

日期: 2025-02-27

次要变更

  • patchRoutesOnNavigation 添加 fetcherKey 参数 (#13109)

补丁更新

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

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

v6.29.0

日期:2025-01-30

次要更新

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

补丁更新

  • 在生产构建中不记录 v7 弃用警告 (#12794)
  • 抛出 data() 结果时正确传递 headers (#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

Date: 2024-11-06

更新内容

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

次要更新

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

补丁更新

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

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

v6.27.0

Date: 2024-10-11

更新内容

稳定化的 API

本次发布稳定化了一系列“不稳定”的 API,为即将到来的 pending React Router v7 发布做准备(更多信息请参阅 these posts

  • unstable_dataStrategydataStrategycreateBrowserRouter 及相关函数) (文档
  • unstable_patchRoutesOnNavigationpatchRoutesOnNavigationcreateBrowserRouter 及相关函数) (文档
  • unstable_flushSyncflushSyncuseSubmit, 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 在并发 fetch 时,通过重定向无法持久化 preventScrollReset 的 bug (#11999)
  • 避免由于连续的重新验证调用导致 fetcher 中止时出现不必要的 console.error (#12050)
  • 修复使用错误进行 hydrating 时 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

Date: 2024-09-09

补丁更新

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

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

v6.26.1

Date: 2024-08-15

补丁更新

  • unstable_patchRoutesOnMiss 重命名为 unstable_patchRoutesOnNavigation 以匹配新行为 (#11888)
  • 更新 unstable_patchRoutesOnNavigation 逻辑,以便在匹配具有动态参数或通配符片段的路由时调用该方法,以防存在尚未发现的得分更高的静态路由 (#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)
  • 添加一个新的 unstable_data() API,用于 Remix Single Fetch (#11836)
    • 此 API 不打算直接用于 React Router SPA(单页应用)中。
    • 它主要用于与 createStaticHandler.query() 一起使用,以允许 loaders/actions 返回任意数据以及自定义的 status/headers,而无需强制将数据序列化为 Response 实例。
    • 这使得可以通过 unstable_dataStrategy 实现更高级的序列化策略,例如在 Remix Single Fetch 中通过 turbo-stream 进行序列化。
    • ⚠️ 这会从 HandlerResult 中移除 status 字段。
      • 如果你需要从 unstable_dataStrategy 中返回特定的 status,你应该改为通过 unstable_data() 来实现。

修订变更

  • 修复中断的 fetchers 的内部清理问题,以避免在导航时发生无效的重新验证 (#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 内部实现进行记忆化处理,以减少不必要的重新渲染 (#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` 的 actions 默认不会触发重新验证。
  • 这也将 `shouldRevalidate` 的 `unstable_actionStatus` 参数稳定为 `actionStatus`。

次要变更

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

修订变更

  • 修复回归问题,并在 `useMatch` 内部正确解码路径,以便 matches/params 反映解码后的参数 (#11789)
  • 修复从 `unstable_patchRoutesOnMiss` 抛出的错误冒泡问题 (#11786)
  • 修复使用 `unstable_patchRoutesOnMiss` 的 SSR(服务器端渲染)应用中的 hydration(水合)问题,该应用在服务器上匹配到了一个 splat route (#11790)

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

v6.24.1

日期:2024-07-03

修订变更

  • 从警告消息中移除对 `polyfill.io` 的引用,因为该域名已被出售,且现已确定用于传播恶意软件 (#11741)
  • 导出 `NavLinkRenderProps` 类型,以便更容易为自定义的 `NavLink` 回调函数添加类型标注 (#11553)
  • 当使用 `future.v7_relativeSplatPath` 时,正确解析属于无路径路由 (pathless routes) 子项的 splat routes 中的相对路径 (#11633)
  • 战争迷雾 (不稳定特性):在路由修补 (route patching) 期间,触发新的 `router.routes` 标识/重新布局 (#11740)
  • 战争迷雾 (不稳定特性):修复当 splat route 匹配时的初始匹配问题 (#11759)

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

v6.24.0

日期:2024-06-24

更新内容

懒加载路由发现 (即“战争迷雾”)

我们非常高兴在 `v6.24.0` 中发布新的“懒加载路由发现”API!有关背景信息,请参阅原始 RFC要点是: 自从我们在 v6.4 中通过 <RouterProvider> 引入 Data APIs 以来,我们一直有点遗憾,其中一个权衡是缺乏一个引人注目的代码分割方案,能够媲美我们在 <BrowserRouter>/<Routes> 应用中实现的方案。我们在 `v6.9.0` 中通过 route.lazy 向改进这一方案迈出了一小步,但在 `v6.24.0` 中,我们已经走到了终点。

借助“战争迷雾”,你现在可以通过传递给 createBrowserRouter(及其 memory/hash 对应方法)的新选项 unstable_patchRoutesOnMiss 来懒加载部分路由树。这提供了一种方式来介入 React Router 无法匹配给定路径的场景,并在导航期间(或 fetcher 调用期间)将新路由修补到路由树中。

这是一个非常小的示例,但更多信息和使用案例请参考文档

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

次要变更

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

修订变更

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

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

v6.23.1

日期:2024-05-10

修订变更

  • 允许通过 `<Await>` 解析 `undefined` (#11513)
  • 在检查 `document.startViewTransition` 可用性时,添加防御性的 `document` 检查 (#11544)
  • 将 `react-router-dom/server` 导入路径改回 `react-router-dom`,而不是 `index.ts` (#11514)
  • @remix-run/router - 在 `staticHandler.queryRoute` 上支持 `unstable_dataStrategy` (#11515)

完整更新日志v6.23.0...v6.23.1

v6.23.0

日期:2024-04-23

更新内容

数据策略 (不稳定特性)

新的 `unstable_dataStrategy` API 是一个低级别 API,设计用于高级用例,你需要在其中控制 `loader`/`action` 函数的数据策略。默认实现是当前行为,即并行抓取所有 loaders,但此选项允许用户实现更高级的数据流,包括 Remix "Single Fetch"、用户自定义的 middleware/context APIs、自动 loader 缓存等。更多信息请参阅文档

注意:这是一个低级别 API,旨在用于高级用例。这会覆盖 React Router 内部处理 `loader`/`action` 执行的方式,如果处理不当,可能会导致你的应用代码出错。请谨慎使用并进行充分测试。

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

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

启用此标记后,返回/抛出 `4xx`/`5xx` 响应状态的 action 将不再自动重新验证。如果在此标记启用时,你需要在一个 `4xx`/`5xx` 结果后进行重新验证,你仍然可以通过让 `shouldRevalidate` 返回 true 来实现——现在 `shouldRevalidate` 也接收一个新的参数 `unstable_actionStatus`,与 `actionResult` 一起,以便你可以根据 action 响应的状态做出决定,而无需将其编码到 action 数据中。

次要变更

  • 添加一个新的 `unstable_dataStrategy` 配置选项 (#11098, #11377)
  • @remix-run/router - 添加一个新的 `future.unstable_skipActionRevalidation` 未来标记 (#11098)
  • @remix-run/router - SSR(服务器端渲染):向 `staticHandler.query` 方法中添加了一个新的 `skipLoaderErrorBubbling` 选项,用于禁用静态处理程序 (static handler) 的错误冒泡,以便在 Remix 的 Single Fetch 实现中使用 (#11098, (#11377))

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

v6.22.3

日期:2024-03-07

修订变更

  • 修复一个 `future.v7_partialHydration` 的 bug,该 bug 会在 hydration(水合)时重新运行边界下方的 loaders,前提是 SSR loader 错误冒泡到父级边界 (#11324)
  • 修复一个 `future.v7_partialHydration` 的 bug,如果一个路由没有 loader,该 bug 会认为路由器未初始化 (#11325)

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

v6.22.2

日期:2024-02-28

修订变更

  • 在部分 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 用户体验报告 26 (CrUX) 数据集中的真实用户体验和 HTTP Archive 30 中的 Web 技术检测,我们可以了解内容管理系统平台或 JavaScript 框架等架构决策如何影响网站的 CWV 性能。

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

有人提请我们注意,由于 React Router 没有可识别的全局特性,因此无法可靠地识别它。他们目前正在查找名称中包含 react-router 的外部脚本。这将识别使用 CDN(例如 unpkg)加载 React Router 的网站——但会遗漏**绝大多数**从 npm 注册表安装 React Router 并将其打包到其 JS 文件中的网站。这导致 React Router 在 Web 上的使用情况被严重低估

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

次要更改

  • 包含 window.__reactRouterVersion 用于 CWV 报告检测 (#11222)
  • 添加 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 键 (#11166)
  • 修复动态参数名称中未拾取连字符的错误 (#11160)
  • 不尝试反序列化空的 JSON 响应 (#11164)

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

v6.21.1

日期: 2023-12-21

补丁更改

  • 修复在指定 v7_partialHydration 时,route.lazy 在初始 SPA 加载时不正确工作的错误 (#11121)
  • 修复阻止在 submitting 阶段卸载的持久化 fetcher 重新验证的错误 (#11102)
  • 去除 resolveTo 中相对路径逻辑的重复 (#11097)

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

v6.21.0

日期: 2023-12-13

更新内容

future.v7_relativeSplatPath

我们在 6.19.0 中修复了一个 splat 路由路径解析错误,但后来发现大量应用程序依赖于该错误行为,因此我们在 6.20.1 中回滚了该修复(参见 #10983#11052#11078)。

错误行为是,在 splat 路由内部解析相对路径时的默认行为会忽略当前路由路径中的任何 splat (`*`) 部分。启用该 future 标记后,splat 部分将包含在 splat 路由内的相对路径逻辑中。

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

部分水合

我们为 `@remix-run/router` 添加了一个新的 future.v7_partialHydration future 标记,它允许在服务器端渲染时对数据路由器进行部分水合。这允许您提供 hydrationData.loaderData,其中包含部分初始匹配路由加载器的数据,但不包含全部。启用此标记后,路由器将在 router.initialize() 期间调用没有水合加载器数据的路由的 loader 函数,并在执行未水合的路由时,渲染到最深的提供的 HydrateFallback(直到第一个没有水合数据的路由)。(#11033)

次要更改

  • 添加新的 future.v7_relativeSplatPath 标记,以实现在 splat 路由内修复相对路由的重大错误。(#11087)
  • 添加新的 future.v7_partialHydration future 标记,在服务器端渲染时启用数据路由器的部分水合 (#11033)

补丁更改

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

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

v6.20.1

日期: 2023-12-01

补丁更改

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

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

v6.20.0

日期: 2023-11-22

[!警告] 请使用 6.20.1 或更高版本代替 6.20.0。我们发现大量应用依赖于此版本中修复的错误行为 (#11045)。我们在 6.20.1 中回滚了该修复,并将在后续版本中通过 future 标记重新引入。更多详情请参阅 #11052

次要更改

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

补丁更改

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

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

v6.19.0

日期: 2023-11-16

[!警告] 请使用 6.20.1 或更高版本代替 6.19.0。我们发现大量应用依赖于此版本中修复的错误行为 (#10983)。我们在 6.20.1 中回滚了该修复,并将在后续版本中通过 future 标记重新引入。更多详情请参阅 #11052

更新内容

unstable_flushSync API

此版本为命令式 API(useSubmituseNavigatefetcher.submitfetcher.load)带来了一个新的 unstable_flushSync 选项,允许用户选择同步 DOM 更新以实现 pending/optimistic UI。

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

次要更改

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

补丁变更

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

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

    • ⚠️ 这修复了一个长期存在的 bug,特别是针对通配符路由中 incorrectly dropped 的 "." 路径。如果您在应用程序的通配符路由内部通过 "." 进行相对路由,您应该仔细检查您的逻辑是否依赖于这种有 bug 的行为,并进行相应的更新。
  • 修复了在使用保持挂载状态的 useFetcher 中,更改 key 没有被拾取的问题 (#11009)

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

  • 修复 NavLinkto location 带有末尾斜杠时的 active 逻辑 (#10734)

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

  • 修复 relative="path" bug,该 bug 导致相对路径计算从完整的 location pathname 开始,而不是从当前的上下文路由 pathname 开始。 (#11006)

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

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

v6.18.0

日期: 2023-10-31

变更内容

新的 Fetcher API

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

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

持久化 Future Flag (future.v7_fetcherPersist)

根据上述同一个 RFC,我们引入了一个新的 future.v7_fetcherPersist 标志,允许您选择启用新的 fetcher 持久化/清理行为。fetchers 将不会在卸载时立即清理,而是会一直持久化直到它们返回到 idle 状态。这使得在原始 fetcher 需要卸载的情况下,实现待处理/乐观 UI 变得容易得多

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

次要变更

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

补丁变更

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

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

v6.17.0

日期: 2023-10-16

变更内容

视图过渡 🚀

我们很高兴发布 React Router 对 View Transitions API 的实验性支持!您现在可以触发导航 DOM 更新,使其被包裹在 document.startViewTransition 中,以在应用程序的 SPA 导航上启用 CSS 动画过渡。

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

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

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

您还可以使用 <NavLink unstable_viewTransition> 简写,它将为您管理 hook 的使用,并在过渡期间自动向 <a> 添加一个 transitioning

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

有关 View Transitions 的示例用法,请查看我们对出色的 Astro Records demo 的 fork

有关使用 View Transitions API 的更多信息,请参阅 Google Chrome 团队的 使用 View Transitions API 实现平滑简单的过渡 指南。

次要变更

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

补丁变更

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

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

v6.16.0

日期: 2023-09-13

次要变更

  • 为了将来朝着更严格的 TypeScript 支持迈进,我们旨在将用户提供的数据在暴露的类型定义中,将当前使用的 any 替换为 unknown。为了在 Remix v2 中实现这一点而不引入 React Router v6 的破坏性变更,我们在许多共享类型中添加了泛型。这些在 React Router 中仍默认为 any,并在 Remix 中被 unknown 覆盖。在 React Router v7 中,我们计划将这些更改为 unknown 作为破坏性变更。 (#10843)
    • Location 现在接受一个用于 location.state 值的泛型
    • ActionFunctionArgs/ActionFunction/LoaderFunctionArgs/LoaderFunction 现在接受一个用于 context 参数的泛型(仅在通过 createStaticHandler 进行的 SSR 使用中)
    • useMatches 的返回类型(现在导出为 UIMatch)接受用于 match.datamatch.handle 的泛型 - 这两者都已经设置为 unknown
  • @private 类导出 ErrorResponse 移动到 UNSAFE_ErrorResponseImpl 导出,因为它是一个实现细节,用户代码中不应该构造 ErrorResponse 实例。这使得我们可以导出一个 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 中的副作用,以避免在提示被解除阻塞且同步执行导航时抛出异常 (#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> prop,以便在提交导航时填充 history.state (#10630)
  • 如果 defer promise 以 undefined resolve/reject,则触发错误,以匹配 loaders 和 actions 的行为(它们必须返回一个值或 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

补丁更新

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

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

v6.14.0

日期: 2023-06-23

有哪些变化

JSON/文本提交

6.14.0 添加了对通过 useSubmit/fetcher.submit 进行 JSON 和文本提交的支持,因为在客户端 SPA 中工作时,并非总是方便序列化为 FormData。要选择启用这些编码,只需指定正确的 formEncType

选择启用 application/json 编码

function Component() {
  let navigation = useNavigation();
  let submit = useSubmit();
  submit({ key: "value" }, { method: "post", encType: "application/json" });
  // navigation.formEncType => "application/json"
  // navigation.json        => { key: "value" }
}

async function action({ request }) {
  // request.headers.get("Content-Type") => "application/json"
  // await request.json()                => { key: "value" }
}

选择启用 text/plain 编码

function Component() {
  let navigation = useNavigation();
  let submit = useSubmit();
  submit("Text submission", { method: "post", encType: "text/plain" });
  // navigation.formEncType => "text/plain"
  // navigation.text        => "Text submission"
}

async function action({ request }) {
  // request.headers.get("Content-Type") => "text/plain"
  // await request.text()                => "Text submission"
}

⚠️ 默认行为将在 v7 中改变

请注意,为避免重大更改,默认行为仍会将简单的键/值 JSON 对象编码为 FormData 实例

function Component() {
  let navigation = useNavigation();
  let submit = useSubmit();
  submit({ key: "value" }, { method: "post" });
  // navigation.formEncType => "application/x-www-form-urlencoded"
  // navigation.formData    => FormData instance
}

async function action({ request }) {
  // request.headers.get("Content-Type") => "application/x-www-form-urlencoded"
  // await request.formData()            => FormData instance
}

此行为可能会在 v7 中改变,因此最好通过 formEncType: "application/x-www-form-urlencoded"formEncType: "application/json" 显式指定任何 JSON 对象提交,以方便您最终的 v7 迁移路径。

次要更新

  • 添加对 useSubmit/fetcher.submitapplication/jsontext/plain 编码的支持。为了反映这些额外类型,useNavigation/useFetcher 现在也包含 navigation.json/navigation.textfetcher.json/fetcher.text,这些字段在适用时包含 JSON/文本提交内容。 (#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 函数的位置中移除 basename,以匹配 useLocation 的行为 (#10573)
  • 修复 StrictModeunstable_useBlocker 的关键问题 (#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 实际上是一个补丁版本,但由于我们添加了一个新的 future flag,它带来了 SemVer 次要版本升级。

future.v7_startTransition

**简而言之**,6.13.06.12.0 相同,但我们将 React.startTransition 的使用移至了可选启用的 future.v7_startTransition future flag 后面,因为我们发现有些实际应用当前使用 Suspense 的方式与 React.startTransition 不兼容。

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

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

<RouterProvider router={router} />

如果您希望启用 React.startTransition,请将此 future flag 传递给您的路由组件

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

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

我们建议大家尽早采用此 flag,以便更好地兼容 React 并发模式,但如果您遇到问题,可以在 v7 之前继续不使用 React.startTransition。问题通常归结于在渲染周期中创建全新的 Promise,因此如果您在选择启用 React.startTransition 时遇到问题,应将 Promise 创建移出渲染周期或放在 useMemo 后面。

次要更新

  • React.startTransition 的用法移至 future flag 后面 (#10596)

补丁更新

  • 规避 webpack/terser 在生产模式下 React.startTransition 的压缩错误 (#10588)

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

v6.12.1

日期: 2023-06-08

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

补丁更新

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

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

v6.12.0

日期: 2023-06-06

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

有哪些变化

React.startTransition 支持

6.12.0 中,我们通过将内部路由状态更新包裹在 React.startTransition 中,为 suspending 组件提供了更好的支持。这意味着,例如,如果您在目标路由中的一个组件 suspending,并且您没有提供 Suspense 边界来显示 fallback,React 将延迟新 UI 的渲染并显示旧 UI,直到该异步操作完成。这对于等待图片或 CSS 文件加载等场景很有用(从技术上讲,您也可以用它来加载数据,但我们仍然建议使用 loaders 来实现 😀)。要快速了解此用法,请查看 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

补丁更新

  • 修复 basename<RouterProvider> 内部的后代 <Routes> 中的重复问题 (#10492)
  • 修复当 hash 存在时初始数据加载不会启动的 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)
  • "相同 hash 的" 导航不再重新运行 loaders 以匹配浏览器行为 (即 /path#hash -> /path#hash) (#10408)

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

v6.11.0

日期: 2023-04-28

次要变更

  • useFetcher 中启用 basename 支持 (#10336)
    • 如果您之前是通过手动在 fetcher 调用中前面加上 basename 来变通解决此问题,则需要移除手动添加的 basename (fetcher.load('/basename/route') -> fetcher.load('/route'))
  • 更新的依赖

补丁更新

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

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

v6.10.0

日期: 2023-03-29

更新内容

我们最近在 Remix 博客上发布了一篇题为 "让您的 Remix 应用面向未来" 的文章,介绍了我们如何确保您的 Remix 和 React Router 应用未来平滑升级的策略。React Router 6.10.0 为这些 flag(针对数据路由)添加了支持,您可以在创建路由器时指定这些 flag

const router = createBrowserRouter(routes, {
  future: {
    // specify future flags here
  },
});

您还可以查看此处此处的文档。

次要变更

future.v7_normalizeFormMethod

引入的第一个未来 flag 是 future.v7_normalizeFormMethod,它将标准化暴露的 useNavigation()/useFetcher()formMethod 字段为大写的 HTTP 方法,以与 fetch()(和某些 Remix)行为对齐。 (#10207)

  • future.v7_normalizeFormMethod 未指定或设置为 false 时 (默认 v6 行为):
    • useNavigation().formMethod 是小写的
    • useFetcher().formMethod 是小写的
  • future.v7_normalizeFormMethod === true 时:
    • useNavigation().formMethod 是大写的
    • useFetcher().formMethod 是大写的

补丁更新

  • 修复了 createStaticHandler 使其除了检查 errorElement 外,还在路由上检查 ErrorBoundary (#10190)
  • 修复了在使用 createRoutesFromElements 中的 Fragments 时路由 ID 的生成问题 (#10193)
  • 如果 fetcher action 重定向,则将 fetcher 提交信息提供给 shouldRevalidate (#10208)
  • 在路由器初始化期间正确处理 lazy() 错误 (#10201)
  • 移除了 DeferredDatainstanceof 检查,以增强在 SSR 打包场景中对 ESM/CJS 边界的兼容性 (#10247)
  • 更新到最新的 @remix-run/web-fetch@4.3.3 (#10216)

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

v6.9.0

日期: 2023-03-10

更新内容

Component/ErrorBoundary 路由属性

React Router 现在支持另一种方式定义您的路由 elementerrorElement 字段,即作为 React 组件而不是 React 元素。您可以选择将 React 组件传递给新的 ComponentErrorBoundary 字段。两者之间没有功能差异,因此请使用您偏好的方式 😀。您不应该同时定义两者,但如果这样做了,Component/ErrorBoundary 将会“胜出”

JSON 语法示例

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

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

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

JSX 语法示例

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

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

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

引入懒加载路由模块

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

懒加载路由会在初始加载时以及导航或 fetcher 调用的 loadingsubmitting 阶段解析。您不能懒加载地定义与路由匹配相关的属性 (path, index, children),因为我们只在匹配了已知路由后才执行您的懒加载路由函数。

您的 lazy 函数通常会返回动态导入的结果。

// In this example, we assume most folks land on the homepage so we include that
// in our critical-path bundle, but then we lazily load modules for /a and /b so
// they don't load until the user navigates to those routes
let routes = createRoutesFromElements(
  <Route path="/" element={<Layout />}>
    <Route index element={<Home />} />
    <Route path="a" lazy={() => import("./a")} />
    <Route path="b" lazy={() => import("./b")} />
  </Route>
);

然后在您的懒加载路由模块中,导出您想为该路由定义的属性

export async function loader({ request }) {
  let data = await fetchData(request);
  return json(data);
}

// Export a `Component` directly instead of needing to create a React Element from it
export function Component() {
  let data = useLoaderData();

  return (
    <>
      <h1>You made it!</h1>
      <p>{data}</p>
    </>
  );
}

// Export an `ErrorBoundary` directly instead of needing to create a React Element from it
export function ErrorBoundary() {
  let error = useRouteError();
  return isRouteErrorResponse(error) ? (
    <h1>
      {error.status} {error.statusText}
    </h1>
  ) : (
    <h1>{error.message || error}</h1>
  );
}

此功能的示例可以在仓库的 examples/lazy-loading-router-provider 目录中找到。更多信息,请查看 lazy 文档

🙌 非常感谢 @rossipedia 提供了初步提案概念验证实现

次要变更

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

补丁更新

  • 改进了 context providers 的 memoization,以避免不必要的重新渲染 (#9983)
  • 修复了在某些情况下 generatePath 错误地应用参数的问题 (#10078)
  • [react-router-dom-v5-compat] 添加了遗漏的数据路由 API 重新导出 (#10171)

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

v6.8.2

日期: 2023-02-27

补丁更新

  • 如果位于路由器 basename 外部,则将 <Link to> 中的同源绝对 URL 视为外部链接 (#10135)
  • 对位于路由器 basename 外部的同源绝对 URL 正确执行硬重定向 (#10076)
  • 修复了绝对 <Link to> URL 的 SSR 问题 (#10112)
  • 正确转义 StaticRouterProvider 序列化的 hydration 数据中的 HTML 字符 (#10068)
  • 修复了 useBlocker 使其在 SSR 期间返回 IDLE_BLOCKER (#10046)
  • 确保在 createStaticHandlerquery() 方法中,defer loader 的响应能够保留状态码和头部信息 (#10077)
  • invariant 更名为 UNSAFE_invariant 并导出,因为它仅供内部使用 (#10066)

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

v6.8.1

日期:2023-02-06

Patch 更新

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

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

v6.8.0

日期:2023-01-26

Minor 更新

支持在 <Link to> 中使用绝对 URL。如果 URL 与当前源 (origin) 相同,仍会执行客户端导航。如果 URL 是指向不同源 (origin),则会为新源发起全新的文档请求。 (#9900)

<Link to="https://neworigin.com/some/path">    {/* Document request */}
<Link to="//neworigin.com/some/path">          {/* Document request */}
<Link to="https://www.currentorigin.com/path"> {/* Client-side navigation */}

Patch 更新

  • 修复了 revalidating fetcher 的 shouldRevalidate 调用中的两个独立问题 (#9948)
    • shouldRevalidate 函数此前仅在明确的重新验证场景(如发生 mutation 后、手动调用 useRevalidator,或在 Remix 中使用 X-Remix-Revalidate 头部信息设置 cookie)中被调用。但在也适用于导航 loader 重新验证的隐式重新验证场景(如搜索参数变化,或点击当前页面的链接)中,它没有被正确调用。现在已在这些附加场景中正确调用该函数。
    • 传递的参数此前不正确且彼此不一致,因为 current*/next* 参数反映的是静态的 fetcher.load URL(因此它们是相同的)。实际上,它们应该反映触发重新验证的导航(就像 form* 参数那样)。现在这些参数已正确反映触发导航。
  • 修复通过 useSearchParams 移除搜索参数时出现的错误 (#9969)
  • <fetcher.Form> 上遵守 preventScrollReset 属性 (#9963)
  • 修复 hash 路由在手动更改 URL 时的导航问题 (#9980)
  • <ScrollRestoration> 使用 pagehide 代替 beforeunload 事件。这提供了更好的跨浏览器支持,尤其是在 Mobile Safari 上。 (#9945)
  • 在仅发生哈希变化的 mutation 提交时不要短路 (#9944)
  • isRouteErrorResponse 中移除 instanceof 检查,以避免服务器端打包问题 (#9930)
  • 检测 defer 调用何时只包含关键数据并移除 AbortController (#9965)
  • 在对 File 类型的 FormData 条目进行 URL 编码时,将文件名作为值发送 (#9867)
  • react-router-dom-v5-compat - 修复在使用 CompatRouter 时,SSR 环境下的 useLayoutEffect 导致 console.error 的问题 (#9820)

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

v6.7.0

日期:2023-01-18

Minor 更新

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

Patch 更新

  • useBeforeUnload 添加了传递事件监听器选项的参数 (#9709)
  • 修复 generatePath 在存在可选参数时的问题 (#9764)
  • 更新 <Await> 组件,使其 children 函数的返回值可以接受 ReactNode 类型 (#9896)
  • 改进了 actions/loaders 中的绝对重定向 URL 检测 (#9829)
  • 修复使用 memory histories 创建 URL 时的问题 (#9814)
  • 修复提交 (submission) 重定向时滚动位置未重置的问题 (#9886)
  • 修复同源 (same-origin) 绝对重定向导致的 404 错误 (#9913)
  • 简化了测试中针对 jsdom 错误的变通方案 (#9824)

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

v6.6.2

日期:2023-01-09

Patch 更新

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

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

v6.6.1

日期:2022-12-23

Patch 更新

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

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

v6.6.0

日期:2022-12-21

更新内容

此 minor 版本主要旨在稳定 Data Routers 的 SSR API,现在我们已将新的 RouterProvider 集成到 Remix 中,作为 React Router-ing Remix 工作的一部分。

Minor 更新

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

Patch 更新

  • 支持大写的 <Form method>useSubmit 方法值 (#9664)
  • 修复 <button formmethod> 覆盖表单提交方法的问题 (#9664)
  • 修复提交 (submission) 时明确指定 replace 以及提交到新路径时使用 PUSH 的问题 (#9734)
  • 禁止在 errorElement 中使用 useLoaderData (#9735)
  • StaticRouterProvider 正确 hydrate Error 对象 (#9664)
  • 对于具有 hydrationData 的 SSR 应用,跳过初始滚动恢复 (#9664)
  • 修复 loader/action 数据在出错时未正确清除的几个错误 (#9735)

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

v6.5.0

日期:2022-12-16

更新内容

此版本引入了对可选路由段 (Optional Route Segments) 的支持。现在,在任意路径段的末尾添加 ? 会使整个路径段变为可选。这适用于静态段和动态参数。

可选参数示例

  • <Route path=":lang?/about> 将匹配
    • /:lang/about
    • /about
  • <Route path="/multistep/:widget1?/widget2?/widget3?"> 将匹配
    • /multistep
    • /multistep/:widget1
    • /multistep/:widget1/:widget2
    • /multistep/:widget1/:widget2/:widget3

可选静态段示例

  • <Route path="/home?"> 将匹配
    • /
    • /home
  • <Route path="/fr?/about"> 将匹配
    • /about
    • /fr/about

Minor 更新

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

Patch 更新

  • 停止对部分命名参数(即 <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 请求后,保留 loaderrequest 中的 headers (#9721)
  • 修复发送给 revalidating loader 的请求,使其反映为 GET 请求 (#9660)
  • 修复深度嵌套可选段的问题 (#9727)
  • GET 表单现在在加载导航 (loading navigation) 上暴露一个提交 (submission) (#9695)
  • 修复多个错误冒泡到同一个错误边界 (error boundary) 时的跟踪问题 (#9702)

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

v6.4.5

日期:2022-12-07

Patch 更新

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

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

v6.4.4

日期:2022-11-30

Patch 更新

  • 如果 action/loader 函数返回 undefined,则会抛出错误,因为重新验证需要知道 loader 是否已执行过。undefined 在 SSR hydration 的字符串化过程中也会引发问题。您应始终确保 loader/action 返回一个值;如果您不想返回任何内容,可以返回 null。 (#9511)
  • 正确处理到外部域的重定向 (#9590, #9654)
  • 在 307/308 重定向时保留 HTTP 方法 (#9597)
  • 在静态数据路由器中支持 basename (#9591)
  • 增强了 ErrorResponse 主体,使其在内部 403/404/405 场景中包含更具描述性的文本
  • 修复了 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 时,生成正确的 &lt;a href&gt; 值 (#9409)
  • 更好地处理 URL 和路由路径中特殊字符的编码/匹配 (#9477, #9496)
  • index 路由也有一个 path 时,生成正确的 formAction 路径名 (#9486)
  • 遵守 NavLink 上的 relative=path 属性 (#9453)
  • 修复 NavLink 对根 URL 的行为 (#9497)
  • 传递 locationArg 时,useRoutes 应该能够返回 null (#9485)
  • 修复 createMemoryRouterinitialEntries 类型的问题 (#9498)
  • loader/action 重定向中支持 basename 和相对路由 (#9447)
  • 在寻找正确的提交 action 函数时,忽略无路径的布局路由 (#9455)
  • @remix-run/router 添加 UMD 构建 (#9446)
  • 修复 Firefox 本地文件执行中 createURL 的问题 (#9464)

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

v6.4.2

日期: 2022-10-06

补丁更新

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

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

v6.4.1

日期: 2022-09-22

补丁更新

  • 保留来自 initialEntries 的状态 (#9288)
  • 保留 ?index 用于 fetcher 对索引路由的 GET 提交 (#9312)

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

v6.4.0

日期: 2022-09-13

主要变化

Remix 数据 API

这是一个重大更新!6.4.0 版本引入了 Remix 中的所有数据加载和修改 API。这里提供一个快速的高层概述,但建议您查阅 文档,特别是功能概述教程

新的 react-router API

  • 使用 createMemoryRouter 创建路由器
  • 使用 &lt;RouterProvider&gt; 渲染路由器
  • 使用 Route loader 加载数据,使用 Route action 修改数据
  • 使用 Route errorElement 处理错误
  • 使用 deferAwait 延迟加载非关键数据

新的 react-router-dom API

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

补丁更新

  • 路径解析现在与末尾斜杠无关 (#8861)
  • &lt;Routes location&gt; 组件内部,useLocation 返回作用域内的 location (#9094)
  • 如果定义了 &lt;Link replace&gt; 属性,则遵守它 (#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

小型更新

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

补丁更新

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

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

v6.0.2

日期: 2021-11-09

补丁更新

  • &lt;Link&gt; 添加了 reloadDocument 属性。这使得 &lt;Link&gt; 可以像普通的 anchor 标签一样工作,在导航后重新加载文档,同时保留相对的 to 解析。(#8283

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

v6.0.1

日期: 2021-11-05

补丁更新

  • 添加默认的 &lt;StaticRouter location&gt; 值 (#8243)
  • 添加在使用 &lt;Routes&gt; 内部使用 &lt;Route&gt; 时的断言(invariant),以帮助人们进行更改 (#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