React Router v7 发布!新版本特性与升级指南

React Router v7 发布!新特性与升级指南

React Router,作为 React 生态中最受欢迎的路由库,每一次版本更新都牵动着无数开发者的心。近日,React Router 团队悄然发布了 v7 的 pre-release 版本(目前仍处于开发阶段,API 可能会有变动,请谨慎用于生产环境),虽然官方尚未发布正式公告,但从其代码仓库和 npm 包中,我们已经可以一窥 v7 带来的重大变化。本文将深入探讨 React Router v7 的新特性,并提供一份详尽的升级指南,帮助开发者平滑过渡到新版本。

注意: 由于 v7 仍处于早期阶段,本文内容基于当前可获取的信息(alpha/beta 版本)。正式发布时可能会有差异。请密切关注官方文档和 release notes。

一、v7 核心理念:更小、更快、更现代

React Router v7 的设计目标非常明确:

  • 更小 (Smaller): 进一步减小打包体积,提升应用加载速度。
  • 更快 (Faster): 优化内部实现,减少不必要的渲染,提升路由切换性能。
  • 更现代 (More Modern): 拥抱 React 最新特性(如 Concurrent Mode 和 Server Components),移除过时的 API,简化使用方式。

为了实现这些目标,v7 做出了许多底层重构和 API 调整。其中最显著的变化是:

  1. 完全拥抱 Hooks: v7 将全面转向 Hooks API,useRoutes 成为构建路由配置的核心。
  2. 移除 <Route> 组件: 不再使用 <Route> 组件嵌套的方式定义路由,而是采用更简洁、更直观的 JavaScript 对象配置。
  3. 内置数据加载方案: 引入了 loaderaction 函数,与路由配置紧密集成,简化数据获取和表单提交。
  4. 改进的 Suspense 支持: 更好地与 React Suspense 集成,实现更优雅的代码分割和加载状态管理。
  5. 支持相对路径 ...: 在 to 属性中使用相对路径更加方便。
  6. 优化 TypeScript 支持: 提供更完善的类型定义,提升开发体验。

二、新特性详解

1. useRoutes Hook 和路由配置对象

在 v7 中,useRoutes Hook 取代了 <Routes><Route> 组件,成为构建路由配置的主要方式。路由配置不再是 JSX 嵌套,而是一个纯 JavaScript 对象数组。

v6 及之前版本 (JSX):

```jsx
import { BrowserRouter, Routes, Route, Link } from 'react-router-dom';

function App() {
return (


} />
} />
} />
} />


);
}
```

v7 (Hooks + 对象配置):

```jsx
import { BrowserRouter, useRoutes, Link } from 'react-router-dom';

function App() {
let routes = [
{
path: '/',
element: ,
children: [
{
path: 'about',
element:
}
]
},
{
path: '/users/:id',
element: ,
},
{
path: '*',
element: ,
},
];

let element = useRoutes(routes);

return (

{element}

);
}
```

优点:

  • 更简洁: 对象配置比 JSX 更简洁,更易于阅读和维护。
  • 更灵活: 可以方便地使用 JavaScript 逻辑动态生成路由配置。
  • 更易于测试: 纯 JavaScript 对象更容易进行单元测试。
  • 与数据加载集成: loaderaction 函数可以直接添加到路由配置对象中。

2. 内置数据加载:loaderaction

v7 引入了 loaderaction 函数,与路由配置紧密集成,用于处理数据获取和表单提交。

  • loader: 在路由组件渲染之前执行,用于获取数据。
  • action: 用于处理表单提交或其他副作用(如数据修改)。

```jsx
import { useLoaderData, Form, useActionData } from 'react-router-dom';

const routes = [
{
path: '/users/:id',
element: ,
loader: async ({ params }) => {
const res = await fetch(/api/users/${params.id});
return res.json();
},
action: async ({ request, params }) => {
const formData = await request.formData();
const updates = Object.fromEntries(formData);
await updateUser(params.id, updates);
return redirect(/users/${params.id}); // 重定向
},
},
];

function User() {
const user = useLoaderData(); // 获取 loader 返回的数据
const actionData = useActionData(); //提交action返回的数据

return (

User: {user.name}

{/ ... /}


{/ 表单字段 /}


{actionData &&

提交成功

}

);
}

```

优点:

  • 简化数据流: 将数据获取和路由逻辑整合在一起,减少了组件间的 props 传递。
  • 内置加载状态: loader 执行期间,React Router 会自动处理加载状态,无需手动管理。
  • 错误处理: loaderaction 中抛出的错误会被 React Router 捕获,并显示错误边界。
  • 表单处理: action 函数简化了表单提交的处理,无需手动监听 onSubmit 事件。
  • 自动重新验证: 当 action 被触发时,所有的 loader data 会被重新验证

3. 改进的 Suspense 支持

v7 更好地与 React Suspense 集成,可以更方便地实现代码分割和加载状态管理。

```jsx
import { Suspense } from 'react';
import { useRoutes } from 'react-router-dom';

const LazyAbout = React.lazy(() => import('./About'));

const routes = [
{
path: '/about',
element: (
Loading...\

}>


),
},
];

function App() {
const element = useRoutes(routes);
return

{element}

;
}

```

loader 函数也天然支持 Suspense:

```jsx
const routes = [
{
path: '/users/:id',
element: ,
loader: async ({ params }) => {
const user = await fetchUser(params.id); // 假设 fetchUser 返回一个 Promise
return user;
},
},
];

function User() {
const user = useLoaderData(); // useLoaderData 会自动处理 Suspense

return (
Loading user...\

}>

User: {user.name}

{/ ... /}

);
}
```

4. 相对路径支持

在 v7 中,<Link>useNavigateto 属性可以更方便地使用相对路径。

```jsx
import { Link, useNavigate } from 'react-router-dom';

function Post() {
const navigate = useNavigate();
return(
<>
详情页 (相对当前路径)
返回上一级


)
}
```

5. fetcher

fetcher 对象允许你在路由上下文之外去调用 loaderaction

jsx
const fetcher = useFetcher();
useEffect(() => {
if (fetcher.state === "idle" && !fetcher.data) {
fetcher.load("/projects");
}
}, [fetcher]);

fetcher.load("/projects")执行时,会调用/projects路由下定义的loader函数

fetcher 也可以提交表单,就像 <Form> 一样,只需要将 fetcher.Form 当做 Form 使用即可.

6. 其他变化

  • 移除 <Switch>: v7 不再需要 <Switch> 组件,因为 useRoutes 总是会匹配最具体的路由。
  • 移除 useHistory: 使用 useNavigate 代替。
  • 移除 matchPathwithRouter: 这些工具函数不再需要。
  • 增强的 TypeScript 类型: 提供更完善的类型定义,提升开发体验.
  • useNavigation: 返回当前 navigation 状态 (idle, submitting, loading)

三、升级指南

从 v6 升级到 v7 需要进行一些代码修改。以下是一些主要的步骤:

  1. 安装 v7:

    bash
    npm install react-router-dom@next react-router@next

  2. 使用 useRoutes 替换 <Routes><Route>:

    将 JSX 路由配置转换为 JavaScript 对象配置。

  3. 添加 loaderaction 函数:

    将数据获取和表单提交逻辑移入 loaderaction 函数。

  4. 更新组件中的数据获取方式:

    使用 useLoaderData 钩子获取 loader 返回的数据。

  5. 更新导航相关的 Hook:

    • 使用 useNavigate 替换 useHistory
    • 移除 matchPathwithRouter
  6. 处理 Suspense:

    根据需要,使用 <Suspense> 组件包裹懒加载组件或使用 useLoaderData 的地方。

  7. 调整测试代码:
    因为路由配置现在是纯 JavaScript 对象,所以测试路由配置会更加容易。

  8. 处理相对路径:
    如果之前使用了复杂的相对路径计算,现在可以利用...简化

示例:

假设我们有一个简单的博客应用,v6 代码如下:

```jsx
// v6
import { BrowserRouter, Routes, Route, Link } from 'react-router-dom';

function Home() {
return

Home

;
}

function Posts() {
const [posts, setPosts] = useState([]);

useEffect(() => {
fetch('/api/posts')
.then((res) => res.json())
.then((data) => setPosts(data));
}, []);

return (

Posts

    {posts.map((post) => (

  • /posts/${post.id}}>{post.title}
  • ))}

);
}

function Post() {
const { id } = useParams();
const [post, setPost] = useState(null);

useEffect(() => {
fetch(/api/posts/${id})
.then((res) => res.json())
.then((data) => setPost(data));
}, [id]);

if (!post) {
return

Loading...

;
}

return (

{post.title}

{post.content}

);
}

function App() {
return (


} />
} />
} />


);
}
```

升级到 v7 后,代码如下:

```jsx
// v7
import { BrowserRouter, useRoutes, Link, useLoaderData } from 'react-router-dom';

function Home() {
return

Home

;
}

function Posts() {
const posts = useLoaderData();

return (

Posts

    {posts.map((post) => (

  • ${post.id}}>{post.title}
  • ))}

);
}

function Post() {
const post = useLoaderData();

return (

{post.title}

{post.content}

);
}

const routes = [
{
path: '/',
element: ,
},
{
path: '/posts',
element: ,
loader: async () => {
const res = await fetch('/api/posts');
return res.json();
},
children: [
{
path: ':id',
element: ,
loader: async ({params}) => {
const res = await fetch(/api/posts/${params.id});
return res.json();
}
}
]
},
];

function App() {
const element = useRoutes(routes);

return (

{element}

);
}

```

可以看到,v7 版本代码更简洁,数据获取和路由逻辑更紧密地结合在一起。

四、总结与展望

React Router v7 是一次重大更新,它带来了更小、更快、更现代的路由体验。通过拥抱 Hooks、引入内置数据加载方案、改进 Suspense 支持等特性,v7 极大地简化了 React 应用的路由管理。虽然升级过程可能需要一些代码修改,但长远来看,v7 将为开发者带来更高的开发效率和更好的应用性能。

由于 v7 仍在开发中,建议开发者在生产环境中使用 v6,并密切关注 v7 的正式发布。一旦 v7 稳定发布,本文提供的升级指南将帮助你快速迁移到新版本,享受 React Router v7 带来的种种优势。

THE END