跳转到内容

Future Flags

未来标志

以下未来标志已稳定并可供采用。要了解有关未来标志的更多信息,请参阅 开发策略

更新至最新 v2.x

首先更新到 v2.x 的最新次要版本以获取最新的未来标志。

👉 更新至最新 v2

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

Vite 插件

背景

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

虽然这不是未来的标志,但新功能和一些功能标志仅在 Vite 插件中可用,并且 Classic Compiler 将在下一版本的 Remix 中删除。

👉 安装 Vite

Terminal window
npm install -D vite

更新您的代码

👉 在 Remix 应用程序的根目录中将 remix.config.js 替换为 vite.config.ts

vite.config.ts
import { vitePlugin as remix } from "@remix-run/dev";
import { defineConfig } from "vite";
export default defineConfig({
plugins: [remix()],
});

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

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

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

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

👉 更新 tsconfig.json

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

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

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

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

remix.env.d.ts
/// <reference types="@remix-run/dev" />
/// <reference types="@remix-run/node" />

如果 remix.env.d.ts 现在为空,请将其删除

Terminal window
rm remix.env.d.ts

配置路径别名

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

👉 安装 vite-tsconfig-paths

Terminal window
npm install -D vite-tsconfig-paths

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

vite.config.ts
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

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

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

👉 卸载 @remix-run/css-bundle

Terminal window
npm uninstall @remix-run/css-bundle

👉 删除对 cssBundleHref 的引用

app/root.tsx
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 导入

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

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

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

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

从 Remix 应用服务器迁移

👉 更新你的 devbuildstart 脚本

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

👉 在你的 Vite 配置中安装全局 Node polyfill

vite.config.ts
import { vitePlugin as remix } from "@remix-run/dev";
import { installGlobals } from "@remix-run/node";
import { defineConfig } from "vite";
installGlobals();
export default defineConfig({
plugins: [remix()],
});

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

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

迁移自定义服务器

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

迁移 MDX 路线

如果您正在使用 MDX,则应使用官方的 MDX Rollup 插件。请参阅 完整迁移指南 了解分步指南。

v3_fetcherPersist 复制代码

背景

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

👉 启用标志

vite.config.ts
remix({
future: {
v3_fetcherPersist: true,
},
});

更新您的代码

它不太可能影响您的应用。您可能需要检查 “useFetchers” 的使用情况,因为它们的持续时间可能会比以前更长。根据您正在做的事情,您可能会比以前渲染更长的内容。

v3_relativeSplatPath

背景

更改多段 splats 路径(如 dashboard/*,而不是 *)的相对路径匹配和链接。查看 CHANGELOG 了解更多信息。

👉 启用标志

vite.config.ts
remix({
future: {
v3_relativeSplatPath: true,
},
});

更新您的代码

如果您的任何路线带有路径 + splat,如 “dashboard.$.tsx” 或 “route (“dashboard/*”)”,并且其下方有相对链接,如 “” 或 “”,则您需要更新您的代码。

👉 将路线一分为二

对于任何 splat 路线,将其拆分为布局路线和带有 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");
});
});
},

👉 更新相关链接

更新该路线树中具有相对链接的任何 <Link> 元素,以包含额外的 .. 相对段,从而继续链接到同一位置:

// 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

背景

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

👉 启用标志

vite.config.ts
remix({
future: {
v3_throwAbortReason: true,
},
});

更新您的代码

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

v3_singleFetch

背景

使用此标志,Remix 会在您的应用内进行 SPA 导航时采用 “单一获取” 方法处理数据请求。更多详细信息请参阅 docs,但我们选择采用此方法的主要原因是简单性。使用单一获取,数据请求现在的行为与文档请求一样,开发人员不再需要考虑如何管理标头、缓存等两者之间的细微差别。对于更高级的用例,开发人员仍然可以选择细粒度的重新验证。

👉 启用标志(和类型)

vite.config.ts
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(),
],
});

更新您的代码

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

👉 删除 json ()/defer () 以支持原始对象

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

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 执行操作:

import { json } from "@remix-run/node";
import { data } from "@remix-run/node";
export async function loader({}: LoaderFunctionArgs) {
let tasks = await fetchTasks();
return json(tasks, {
return data(tasks, {
headers: {
"Cache-Control": "public, max-age=604800"
}
});
}

👉 调整服务器上止延迟

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

entry.server.tsx
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_lazyRouteDiscovery

背景

使用此标志,Remix 不再在初始加载时将完整路由清单发送到客户端。相反,Remix 仅在清单中发送服务器呈现的路由,然后在用户浏览应用程序时获取其余路由。更多详细信息请参阅 docsblog post

👉 启用标志

vite.config.ts
remix({
future: {
v3_lazyRouteDiscovery: true,
},
});

更新您的代码

您不需要对应用程序代码进行任何更改即可使用此功能。

如果您希望禁用某些链接上的紧急路由发现,您可能会发现新的 <Link discover> API 有一些用处。

unstable_optimizeDeps

在开发过程中选择自动启用 依赖项优化。此标志将保持 “不稳定” 状态,直到 React Router v7 推出,因此在升级到 React Router v7 之前,您无需在 Remix v2 应用中采用此功能。