未来标志
On this page

未来标志和弃用

¥Future Flags and Deprecations

本指南将指导你在 Remix 应用中采用未来标志的过程。通过遵循此策略,你将能够以最少的更改升级到 Remix 的下一个主要版本。要了解有关未来标志的更多信息,请参阅 开发策略

¥This guide walks you through the process of adopting future flags in your Remix app. By following this strategy, you will be able to upgrade to the next major version of Remix with minimal changes. To read more about future flags, see Development Strategy.

我们强烈建议你在每个步骤后提交并发布,而不是一次性完成所有操作。大多数标志可以按任意顺序使用,但下文会列出例外情况。

¥We highly recommend you make a commit after each step and ship it instead of doing everything all at once. Most flags can be adopted in any order, with exceptions noted below.

更新至最新的 v2.x

¥Update to latest v2.x

首先,请更新到 v2.x 的最新次要版本,以获取最新的未来标志。升级时,你可能会看到一些弃用警告,我们将在下文介绍。

¥First update to the latest minor version of v2.x to have the latest future flags. You will likely see a number of deprecation warnings as you upgrade, which we'll cover below.

👉 更新至最新的 v2

¥👉 Update to latest v2

npm install @remix-run/{dev,react,node,etc.}@2

移除 installGlobals

¥Remove installGlobals

背景

¥Background

以前,Remix 需要安装 fetch polyfill。这是通过调用 installGlobals() 实现的。

¥Previously Remix required a fetch polyfill to be installed. This was achieved by calling installGlobals().

下一个主要版本至少需要 Node 20 才能利用内置的 fetch 支持。

¥The next major version requires a minimum of Node 20 to take advantage of the built-in fetch support.

注意:如果你在 Remix 项目中使用 Miniflare/Cloudflare Worker,请确保你的 兼容性标志 也设置为 2023-03-01 或更高版本。

¥Note: if you are using Miniflare/Cloudflare worker with your remix project, ensure your compatibility flag is set to 2023-03-01 or later as well.

👉 更新至 Node 20+

¥👉 Update to Node 20+

建议你升级到最新的 Node LTS 偶数版本。

¥It is recommended that you upgrade to the latest even-numbered version of Node LTS.

👉 移除 installGlobals

¥👉 Remove installGlobals

import { vitePlugin as remix } from "@remix-run/dev";
import { defineConfig } from "vite";

-installGlobals();

export default defineConfig({
  plugins: [remix()],
});

采用 Vite 插件

¥Adopt the Vite Plugin

背景

¥Background

Remix 不再使用其自己的封闭编译器(现在称为 "经典编译器"),而是使用 Vite。Vite 是一个功能强大、性能卓越且可扩展的 JavaScript 项目开发环境。查看 Vite 文档 提供了更多关于性能、故障排除等方面的信息。

¥Remix no longer uses its own, closed compiler (now referred to as the "Classic Compiler") and instead uses Vite. Vite is a powerful, performant and extensible development environment for JavaScript projects. View the Vite docs for more information on performance, troubleshooting, etc.

虽然这不是未来的标志,但新功能和一些功能标志仅在 Vite 插件中可用,并且经典编译器将在 Remix 的下一个版本中被移除。

¥While this is not a future flag, new features and some feature flags are only available in the Vite plugin, and the Classic Compiler will be removed in the next version of Remix.

👉 安装 Vite

¥👉 Install Vite

npm install -D vite

更新你的代码

¥Update your Code

👉 将 Remix 应用根目录下的 remix.config.js 替换为 vite.config.ts

¥👉 Replace remix.config.js with vite.config.ts at the root of your Remix app

import { vitePlugin as remix } from "@remix-run/dev";
import { defineConfig } from "vite";

export default defineConfig({
  plugins: [remix()],
});

支持的 Remix 配置选项 的子集应直接传递给插件:

¥The subset of supported Remix config options should be passed directly to the plugin:

export default defineConfig({
  plugins: [
    remix({
      ignoredRouteFiles: ["**/*.css"],
    }),
  ],
});

👉 添加 unstable_optimizeDeps(可选)

¥👉 Add unstable_optimizeDeps (optional)

许多用户发现,自动 优化依赖 帮助他们更轻松地采用 Vite 插件。因此,我们在 Vite 插件中添加了 unstable_optimizeDeps 标志。

¥Many users found that automatically optimizing dependencies helped them more easily adopt the Vite plugin. For this reason we added the unstable_optimizeDeps flag to the Vite plugin.

此标志将保持 "unstable" 状态直至 React Router v7 版本,因此在升级到 React Router v7 之前,在 Remix v2 应用中采用此标志并不重要。

¥This flag will remain in an "unstable" state until React Router v7, so it is not critical that you adopt this in your Remix v2 app before upgrading to React Router v7.

export default defineConfig({
  plugins: [
    remix({
      future: {
        unstable_optimizeDeps: true,
      },
    }),
  ],
});

👉 删除 <LiveReload/>,保留 <Scripts />

¥👉 Remove <LiveReload/>, keep <Scripts />

  import {
-   LiveReload,
    Outlet,
    Scripts,
  }

  export default function App() {
    return (
      <html>
        <head>
        </head>
        <body>
          <Outlet />
-         <LiveReload />
          <Scripts />
        </body>
      </html>
    )
  }

👉 更新 tsconfig.json

¥👉 Update tsconfig.json

更新 tsconfig.json 中的 types 字段,并确保 skipLibCheckmodulemoduleResolution 均已正确设置。

¥Update the types field in tsconfig.json and make sure skipLibCheck, module, and moduleResolution are all set correctly.

{
  "compilerOptions": {
    "types": ["@remix-run/node", "vite/client"],
    "skipLibCheck": true,
    "module": "ESNext",
    "moduleResolution": "Bundler"
  }
}

👉 更新/删除 remix.env.d.ts

¥👉 Update/remove remix.env.d.ts

移除 remix.env.d.ts 中的以下类型声明

¥Remove the following type declarations in remix.env.d.ts

- /// <reference types="@remix-run/dev" />
- /// <reference types="@remix-run/node" />

如果 remix.env.d.ts 为空,则删除它

¥If remix.env.d.ts is now empty, delete it

rm remix.env.d.ts

配置路径别名

¥Configure path aliases

Vite 默认不提供任何路径别名。如果你依赖此功能,例如将 ~ 定义为 app 目录的别名,则可以安装 vite-tsconfig-paths 插件以自动解析 Vite 中 tsconfig.json 的路径别名,与 Remix 编译器的行为一致:

¥Vite does not provide any path aliases by default. If you were relying on this feature, such as defining ~ as an alias for the app directory, you can install the vite-tsconfig-paths plugin to automatically resolve path aliases from your tsconfig.json in Vite, matching the behavior of the Remix compiler:

👉 安装 vite-tsconfig-paths

¥👉 Install vite-tsconfig-paths

npm install -D vite-tsconfig-paths

👉 将 vite-tsconfig-paths 添加到你的 Vite 配置

¥👉 Add vite-tsconfig-paths to your Vite config

import { vitePlugin as remix } from "@remix-run/dev";
import { defineConfig } from "vite";
import tsconfigPaths from "vite-tsconfig-paths";

export default defineConfig({
  plugins: [remix(), tsconfigPaths()],
});

移除 @remix-run/css-bundle

¥Remove @remix-run/css-bundle

Vite 内置支持 CSS 副作用导入、PostCSS 和 CSS 模块以及其他 CSS 打包功能。Remix Vite 插件会自动将打包的 CSS 附加到相关路由。

¥Vite has built-in support for CSS side effect imports, PostCSS and CSS Modules, among other CSS bundling features. The Remix Vite plugin automatically attaches bundled CSS to the relevant routes.

使用 Vite 时,@remix-run/css-bundle 包是多余的,因为它的 cssBundleHref 导出始终是 undefined

¥The @remix-run/css-bundle package is redundant when using Vite since its cssBundleHref export will always be undefined.

👉 卸载 @remix-run/css-bundle

¥👉 Uninstall @remix-run/css-bundle

npm uninstall @remix-run/css-bundle

👉 删除对 cssBundleHref 的引用

¥👉 Remove references to cssBundleHref

- import { cssBundleHref } from "@remix-run/css-bundle";
  import type { LinksFunction } from "@remix-run/node"; // or cloudflare/deno

  export const links: LinksFunction = () => [
-   ...(cssBundleHref
-     ? [{ rel: "stylesheet", href: cssBundleHref }]
-     : []),
    // ...
  ];

修复 links 中引用的 CSS 导入

¥Fix up CSS imports referenced in links

如果你使用的是 引用 CSS 在 links 函数中,则需要更新相应的 CSS 导入以使用 Vite 显式 ?url 导入语法。

¥If you are referencing CSS in a links function, you'll need to update the corresponding CSS imports to use Vite's explicit ?url import syntax.

👉 将 ?url 添加到 links 中使用的 CSS 导入中。

¥👉 Add ?url to CSS imports used in links

-import styles from "~/styles/dashboard.css";
+import styles from "~/styles/dashboard.css?url";

export const links = () => {
  return [
    { rel: "stylesheet", href: styles }
  ];
}

迁移 Tailwind CSS 或 Vanilla Extract

¥Migrate Tailwind CSS or Vanilla Extract

如果你使用的是 Tailwind CSS 或 Vanilla Extract,请参阅 完整迁移指南

¥If you are using Tailwind CSS or Vanilla Extract, see the full migration guide.

从 Remix 应用服务器迁移

¥Migrate from Remix App Server

👉 更新你的 devbuildstart 脚本

¥👉 Update your dev, build and start scripts

{
  "scripts": {
    "dev": "remix vite:dev",
    "build": "remix vite:build",
    "start": "remix-serve ./build/server/index.js"
  }
}

👉 配置你的 Vite 开发服务器端口(可选)

¥👉 Configure your Vite dev server port (optional)

export default defineConfig({
  server: {
    port: 3000,
  },
  plugins: [remix()],
});

迁移自定义服务器

¥Migrate a custom server

如果你正在迁移客户服务器或 Cloudflare Functions,请参阅 完整迁移指南

¥If you are migrating a customer server or Cloudflare Functions, see the full migration guide.

迁移 MDX 路由

¥Migrate MDX routes

如果你使用的是 MDX,则应该使用官方的 MDX Rollup 插件。有关分步演示,请参阅 完整迁移指南

¥If you're using MDX, you should use the official MDX Rollup plugin. See the full migration guide for a step-by-step walkthrough.

v3_fetcherPersist

背景

¥Background

fetcher 的生命周期现在基于它返回空闲状态的时间,而不是其所有者组件卸载的时间:更多信息请参见 查看 RFC

¥The fetcher lifecycle is now based on when it returns to an idle state rather than when its owner component unmounts: View the RFC for more information.

👉 启用标志

¥👉 Enable the Flag

remix({
  future: {
    v3_fetcherPersist: true,
  },
});

更新你的代码

¥Update your Code

这不太可能影响你的应用。你可能需要检查 useFetchers 的使用情况,因为它们的持续时间可能会比以前更长。根据你正在执行的操作,渲染时间可能会比以前更长。

¥It's unlikely to affect your app. You may want to check any usage of useFetchers as they may persist longer than they did before. Depending on what you're doing, you may render something longer than before.

v3_relativeSplatPath

背景

¥Background

更改多段 splats 路径(例如 dashboard/*,而不是 *)的相对路径匹配和链接。更多信息请参见 查看变更日志

¥Changes the relative path matching and linking for multi-segment splats paths like dashboard/* (vs. just *). View the CHANGELOG for more information.

👉 启用标志

¥👉 Enable the Flag

remix({
  future: {
    v3_relativeSplatPath: true,
  },
});

更新你的代码

¥Update your Code

如果你有任何包含路径 + 标题(例如 dashboard.$.tsxroute("dashboard/*"))的路由,并且其下方包含相对链接(例如 <Link to="relative"><Link to="../relative">),则需要更新你的代码。

¥If you have any routes with a path + a splat like dashboard.$.tsx or route("dashboard/*") that have relative links like <Link to="relative"> or <Link to="../relative"> beneath it, you will need to update your code.

👉 将路由拆分成两条

¥👉 Split the route into two

对于任何 splat 路由,请使用 splat 将其拆分为布局路由和子路由:

¥For any splat routes split it into a layout route and a child route with the splat:


└── routes
    ├── _index.tsx
+   ├── dashboard.tsx
    └── dashboard.$.tsx

// or
routes(defineRoutes) {
  return defineRoutes((route) => {
    route("/", "home/route.tsx", { index: true });
-    route("dashboard/*", "dashboard/route.tsx")
+    route("dashboard", "dashboard/layout.tsx", () => {
+      route("*", "dashboard/route.tsx");
    });
  });
},

👉 更新相关链接

¥👉 Update relative links

更新该路由树中所有带有相对链接的 <Link> 元素,使其包含额外的 .. 相对片段,以便继续链接到同一位置:

¥Update any <Link> elements with relative links within that route tree to include the extra .. relative segment to continue linking to the same place:

// dashboard.$.tsx or dashboard/route.tsx
function Dashboard() {
  return (
    <div>
      <h2>Dashboard</h2>
      <nav>
-        <Link to="">Dashboard Home</Link>
-        <Link to="team">Team</Link>
-        <Link to="projects">Projects</Link>
+        <Link to="../">Dashboard Home</Link>
+        <Link to="../team">Team</Link>
+        <Link to="../projects">Projects</Link>
      </nav>
    </div>
  );
}

v3_throwAbortReason

背景

¥Background

当服务器端请求中止时,例如当用户在加载器完成之前离开页面时,Remix 将抛出 request.signal.reason 错误,而不是 new Error("query() call aborted...") 之类的错误。

¥When a server-side request is aborted, such as when a user navigates away from a page before the loader finishes, Remix will throw the request.signal.reason instead of an error such as new Error("query() call aborted...").

👉 启用标志

¥👉 Enable the Flag

remix({
  future: {
    v3_throwAbortReason: true,
  },
});

更新你的代码

¥Update your Code

你可能不需要调整任何代码,除非你在 handleError 内部有自定义逻辑,用于匹配上一个错误消息以将其与其他错误区分开来。

¥You likely won't need to adjust any code, unless you had custom logic inside of handleError that was matching the previous error message to differentiate it from other errors.

v3_lazyRouteDiscovery

背景

¥Background

使用此标志后,Remix 不再在初始加载时将完整的路由清单发送给客户端。Remix 只会在清单中向上发送服务器渲染的路由,然后在用户浏览应用时获取剩余的路由。更多详细信息请参阅 docs博客文章

¥With this flag, Remix no longer sends the full route manifest up to the client on initial load. Instead, Remix only sends the server-rendered routes up in the manifest and then fetches the remaining routes as the user navigates around the application. Additional details are available in the docs and the blog post

👉 启用标志

¥👉 Enable the Flag

remix({
  future: {
    v3_lazyRouteDiscovery: true,
  },
});

更新你的代码

¥Update your Code

你无需对应用代码进行任何更改即可使用此功能。

¥You shouldn't need to make any changes to your application code for this feature to work.

如果你想在某些链接上禁用即时路由发现,你可能会发现新的 <Link discover> API 的一些用途。

¥You may find some usage for the new <Link discover> API if you wish to disable eager route discovery on certain links.

v3_singleFetch

此标志需要 Vite 插件

背景

¥Background

使用此标志后,Remix 在客户端导航期间将使用单次提取数据请求。通过将数据请求与文档请求相同处理,简化了数据加载,无需单独处理标头和缓存。对于高级用例,你仍然可以选择启用细粒度的重新验证。查看 "单次获取" 文档 了解更多信息。

¥With this flag, Remix uses a single fetch for data requests during client-side navigations. This simplifies data loading by treating data requests the same as document requests, eliminating the need to handle headers and caching differently. For advanced use cases, you can still opt into fine-grained revalidations. View the "Single Fetch" docs for more information.

👉 启用标志(和类型)

¥👉 Enable the Flag (and the types)

import { vitePlugin as remix } from "@remix-run/dev";
import { defineConfig } from "vite";
import tsconfigPaths from "vite-tsconfig-paths";

declare module "@remix-run/node" {
  // or cloudflare, deno, etc.
  interface Future {
    v3_singleFetch: true;
  }
}

export default defineConfig({
  plugins: [
    remix({
      future: {
        v3_singleFetch: true,
      },
    }),
    tsconfigPaths(),
  ],
});

更新你的代码

¥Update your Code

启用该标志后,你应该能够基本按原样使用你的代码,但以下更改应随着时间的推移而进行,并且是在下一个主要版本发布之前必需的。

¥You should be able to mostly use your code as-is with the flag enabled, but the following changes should be made over time and will be required prior to the next major version.

👉 移除 json()/defer(),使用原始对象

¥👉 Remove json()/defer() in favor of raw objects

Single Fetch 开箱即用地支持 JSON 对象和 Promises,因此你可以从 loader/action 函数返回原始数据:

¥Single Fetch supports JSON objects and Promises out of the box, so you can return the raw data from your loader/action functions:

-import { json } from "@remix-run/node";

export async function loader({}: LoaderFunctionArgs) {
  let tasks = await fetchTasks();
- return json(tasks);
+ return tasks;
}
-import { defer } from "@remix-run/node";

export async function loader({}: LoaderFunctionArgs) {
  let lazyStuff = fetchLazyStuff();
  let tasks = await fetchTasks();
- return defer({ tasks, lazyStuff });
+ return { tasks, lazyStuff };
}

如果你使用 json/defer 的第二个参数在响应中设置自定义状态或标头,则可以继续通过新的 data API 进行设置(请注意,你需要 headers 导出才能将这些标头应用于 Single Fetch 数据请求):

¥If you were using the second parameter of json/defer to set a custom status or headers on your response, you can continue doing so via the new data API (please note that you will need a headers export to apply those headers to Single Fetch data requests):

-import { json } from "@remix-run/node";
+import { data } from "@remix-run/node";

// This example assumes you already have a headers function to handle header
// merging for your document requests
export function headers() {
  // ...
}

export async function loader({}: LoaderFunctionArgs) {
  let tasks = await fetchTasks();
-  return json(tasks, {
+  return data(tasks, {
    headers: {
      "Cache-Control": "public, max-age=604800"
    }
  });
}

👉 调整服务器中止延迟

¥👉 Adjust your server abort delay

如果你在 entry.server.tsx 文件中使用了自定义 ABORT_DELAY,则应将其更改为使用 Single Fetch 所利用的新 streamTimeout API:

¥If you were using a custom ABORT_DELAY in your entry.server.tsx file, you should change that to use the new streamTimeout API leveraged by Single Fetch:

-const ABORT_DELAY = 5000;
+// Reject/cancel all pending promises after 5 seconds
+export const streamTimeout = 5000;

// ...

function handleBrowserRequest(/* ... */) {
  return new Promise((resolve, reject) => {
    const { pipe, abort } = renderToPipeableStream(
      <RemixServer
        context={remixContext}
        url={request.url}
-        abortDelay={ABORT_DELAY}
      />,
      {
        onShellReady() {
          /* ... */
        },
        onShellError(error: unknown) {
          /* ... */
        },
        onError(error: unknown) {
          /* ... */
        },
      }
    );

-    setTimeout(abort, ABORT_DELAY);
+   // Automatically timeout the React renderer after 6 seconds, which ensures
+   // React has enough time to flush down the rejected boundary contents
+   setTimeout(abort, streamTimeout + 1000);
  });
}

v3_routeConfig

此标志需要 Vite 插件

基于配置的路由是 React Router v7 中的新默认配置,通过应用目录中的 routes.ts 文件进行配置。Remix 对 routes.ts 及其相关 API 的支持旨在作为一种迁移路径,以帮助最大限度地减少将 Remix 项目迁移到 React Router v7 时所需的更改数量。虽然在 @remix-run 范围内引入了一些新的包,但这些新包的存在只是为了使 routes.ts 中的代码尽可能与 React Router v7 的等效代码相似。

¥Config-based routing is the new default in React Router v7, configured via the routes.ts file in the app directory. Support for routes.ts and its related APIs in Remix are designed as a migration path to help minimize the number of changes required when moving your Remix project over to React Router v7. While some new packages have been introduced within the @remix-run scope, these new packages only exist to keep the code in routes.ts as similar as possible to the equivalent code for React Router v7.

启用 v3_routeConfig 未来标志后,Remix 的内置文件系统路由将被禁用,你的项目将选择使用 React Router v7 的基于配置的路由。如果你希望继续使用 Remix 的基于文件的路由,我们将在下面介绍如何在 routes.ts 中启用它。

¥When the v3_routeConfig future flag is enabled, Remix's built-in file system routing will be disabled and your project will opt into React Router v7's config-based routing. If you prefer to keep using Remix's file-based routing, we cover how to enable it in routes.ts below.

更新你的代码

¥Update your code

要将 Remix 的文件系统路由和路由配置迁移到 React Router v7 中的等效设置,你可以按照以下步骤操作:

¥To migrate Remix's file system routing and route config to the equivalent setup in React Router v7, you can follow these steps:

👉 启用标志

¥👉 Enable the Flag

remix({
  future: {
    v3_routeConfig: true,
  },
});

👉 安装 @remix-run/route-config

¥👉 Install @remix-run/route-config

此软件包与 React Router v7 的 @react-router/dev/routes 的 API 匹配,使 React Router v7 的迁移尽可能简单。

¥This package matches the API of React Router v7's @react-router/dev/routes, making the React Router v7 migration as easy as possible.

npm install -D @remix-run/route-config

这提供了核心 RouteConfig 类型以及一组用于在代码中配置路由的助手。

¥This provides the core RouteConfig type as well as a set of helpers for configuring routes in code.

👉 添加一个不带任何配置路由的 app/routes.ts 文件

¥👉 Add an app/routes.ts file without any configured routes

touch app/routes.ts
import type { RouteConfig } from "@remix-run/route-config";

export default [] satisfies RouteConfig;

这是一种检查新 routes.ts 文件是否已成功获取的好方法。由于尚未定义任何路由,你的应用现在应该渲染一个空白页。

¥This is a good way to check that your new routes.ts file is being picked up successfully. Your app should now be rendering a blank page since there aren't any routes defined yet.

👉 安装 @remix-run/fs-routes 并在 routes.ts 中使用

¥👉 Install @remix-run/fs-routes and use it in routes.ts

npm install -D @remix-run/fs-routes

此软件包与 React Router v7 的 @react-router/fs-routes 的 API 匹配,使 React Router v7 的迁移尽可能简单。

¥This package matches the API of React Router v7's @react-router/fs-routes, making the React Router v7 migration as easy as possible.

如果你已将 ignoredRouteFiles 配置为 ["**/*"],则应跳过此步骤,因为你已经选择退出 Remix 的文件系统路由。

¥If you've configured ignoredRouteFiles to ["**/*"], you should skip this step since you're already opting out of Remix's file system routing.

import { flatRoutes } from "@remix-run/fs-routes";

export default flatRoutes();

👉 如果你使用了 routes 配置选项,请添加 @remix-run/routes-option-adapter 并在 routes.ts 中使用它。

¥👉 If you used the routes config option, add @remix-run/routes-option-adapter and use it in routes.ts

Remix 提供了一种机制,用于在代码中定义路由并插入其他文件系统路由约定,可通过 Vite 插件上的 routes 选项使用。

¥Remix provides a mechanism for defining routes in code and plugging in alternative file system routing conventions, available via the routes option on the Vite plugin.

为了简化迁移,提供了一个适配器包,可以将 Remix 的 routes 选项转换为 React Router 的 RouteConfig 数组。

¥To make migration easier, an adapter package is available that converts Remix's routes option into React Router's RouteConfig array.

首先安装适配器:

¥To get started, first install the adapter:

npm install -D @remix-run/routes-option-adapter

此软件包与 React Router v7 的 @react-router/remix-routes-option-adapter 的 API 匹配,使 React Router v7 的迁移尽可能简单。

¥This package matches the API of React Router v7's @react-router/remix-routes-option-adapter, making the React Router v7 migration as easy as possible.

然后,更新你的 routes.ts 文件以使用适配器,将 routes 选项的值传递给 remixRoutesOptionAdapter 函数,该函数将返回一个已配置路由的数组。

¥Then, update your routes.ts file to use the adapter, passing the value of your routes option to the remixRoutesOptionAdapter function which will return an array of configured routes.

例如,如果你使用 routes 选项来使用其他文件系统路由实现,例如 remix-flat-routes

¥For example, if you were using the routes option to use an alternative file system routing implementation like remix-flat-routes:

import { type RouteConfig } from "@remix-run/route-config";
import { remixRoutesOptionAdapter } from "@remix-run/routes-option-adapter";
import { flatRoutes } from "remix-flat-routes";

export default remixRoutesOptionAdapter((defineRoutes) =>
  flatRoutes("routes", defineRoutes)
) satisfies RouteConfig;

或者,如果你使用 routes 选项定义基于配置的路由:

¥Or, if you were using the routes option to define config-based routes:

import { flatRoutes } from "@remix-run/fs-routes";
import { type RouteConfig } from "@remix-run/route-config";
import { remixRoutesOptionAdapter } from "@remix-run/routes-option-adapter";

export default remixRoutesOptionAdapter((defineRoutes) => {
  return defineRoutes((route) => {
    route("/", "home/route.tsx", { index: true });
    route("about", "about/route.tsx");
    route("", "concerts/layout.tsx", () => {
      route("trending", "concerts/trending.tsx");
      route(":city", "concerts/city.tsx");
    });
  });
}) satisfies RouteConfig;

如果你以这种方式定义基于配置的路由,你可能需要考虑迁移到新的路由配置 API,因为它更加精简,同时仍然与旧 API 非常相似。例如,上面的路由如下所示:

¥If you're defining config-based routes in this way, you might want to consider migrating to the new route config API since it's more streamlined while still being very similar to the old API. For example, the routes above would look like this:

import {
  type RouteConfig,
  route,
  layout,
  index,
} from "@remix-run/route-config";

export default [
  index("home/route.tsx"),
  route("about", "about/route.tsx"),
  layout("concerts/layout.tsx", [
    route("trending", "concerts/trending.tsx"),
    route(":city", "concerts/city.tsx"),
  ]),
] satisfies RouteConfig;

请注意,如果你需要混合搭配不同的路由配置方法,可以将它们合并为一个路由数组。RouteConfig 类型确保所有内容仍然有效。

¥Note that if you need to mix and match different route config approaches, they can be merged into a single array of routes. The RouteConfig type ensures that everything is still valid.

import { flatRoutes } from "@remix-run/fs-routes";
import type { RouteConfig } from "@remix-run/route-config";
import { route } from "@remix-run/route-config";
import { remixRoutesOptionAdapter } from "@remix-run/routes-option-adapter";

export default [
  ...(await flatRoutes({ rootDirectory: "fs-routes" })),

  ...(await remixRoutesOptionAdapter(/* ... */)),

  route("/hello", "routes/hello.tsx"),
] satisfies RouteConfig;

弃用

¥Deprecations

@remix-run/eslint-config

@remix-run/eslint-config 包已弃用,不会包含在 React Router v7 中。我们建议使用精简的 ESLint 配置,例如 Remix 模板 中包含的配置。

¥The @remix-run/eslint-config package is deprecated and will not be included in React Router v7. We recommend moving towards a streamlined ESLint config such as the ones included in the Remix templates.

json

此实用程序已弃用,并将在 React Router v7 中移除,取而代之的是 单次获取 裸对象返回。

¥This utility is deprecated and will be removed in React Router v7 in favor of Single Fetch naked object returns.

  • 如果你不依赖 json 来序列化数据(例如将 Date 对象字符串化),你可以安全地将其移除。

    ¥If you were not relying on json to serialize your data (such as stringifying Date objects), you can safely remove it.

  • 如果你通过 json 返回 headersstatus,则可以使用新的 数据实用程序 作为直接替代品来设置这些值。

    ¥If you were returning headers or status via json, you can use the new data util as a drop-in replacement to set those values.

  • 如果你想将数据序列化为 JSON,可以使用原生的 Response.json() 方法。

    ¥If you want to serialize your data to JSON, you can use the native Response.json() method.

查看 单次获取 文档了解更多信息。

¥View the Single Fetch docs for more information.

defer

此实用程序已弃用,并将在 React Router v7 中移除,取而代之的是 单次获取 裸对象返回。

¥This utility is deprecated and will be removed in React Router v7 in favor of Single Fetch naked object returns.

  • 如果你通过 defer 返回 headersstatus,则可以使用新的 数据实用程序 作为直接替代品来设置这些值。

    ¥If you were returning headers or status via defer, you can use the new data util as a drop-in replacement to set those values.

查看 单次获取 文档了解更多信息。

¥View the Single Fetch docs for more information.

SerializeFrom

此类型已弃用,并将在 React Router v7 中移除,因为 单次获取 不再将数据序列化为 JSON。

¥This type is deprecated and will be removed in React Router v7 since Single Fetch no longer serializes data to JSON.

如果你依赖 SerializeFrom 来解包 loader/action 数据,可以使用如下自定义类型:

¥If you are relying on SerializeFrom to unwrap your loader/action data, you can use a custom type like this:

type SerializeFrom<T> = ReturnType<typeof useLoaderData<T>>;

在大多数情况下,你应该能够直接删除 SerializeFrom 并使用 useLoaderData/useActionData 返回的类型,或 loader/action 函数中的数据类型。

¥In most cases, you should be able to just remove SerializeFrom and use the types returned from useLoaderData/useActionData, or the types of the data in loader/action functions.

多部分表单数据和文件上传工具

¥Multipart Form Data and File Upload utilities

以下实用程序已弃用,将在 React Router v7 中移除:

¥The following utilities are deprecated and will be removed in React Router v7:

  • unstable_parseMultipartFormData

  • unstable_composeUploadHandlers

  • unstable_createFileUploadHandler

  • unstable_createMemoryUploadHandler

我们建议使用 @mjackson/form-data-parser@mjackson/file-storage 来处理多部分表单数据和文件上传。

¥We recommend using @mjackson/form-data-parser and @mjackson/file-storage to handle multipart form data and file uploads.

你还可以查看 React Router "文件上传" 文档"使用 Remix 上传文件" 博客文章,获取使用这些库的指南。

¥You can also check out the React Router "File Uploads" doc or "File uploads with Remix" blog post for guides on using these libraries.

Remix v2.17 中文网 - 粤ICP备13048890号