React Router 进阶技巧
React Router v6 升级提示
本文内容已于 2024 年更新,全面适配 React Router v6。如果你仍在使用 v4/v5 版本,请注意:withRouter、<Switch>、exact 等核心 API 在 v6 中已被完全废弃。全面拥抱 Hooks 和 <Routes> 是 v6 的核心哲学。
在 TypeScript 中使用编程式导航
在 v6 之前,我们通常需要使用 withRouter 高阶组件将 history 注入到类组件的 Props 中。但在 v6 中,withRouter 已经被移除,官方推荐使用纯函数组件和 useNavigate Hook。
下面是一个在 TypeScript 中使用 useNavigate 的现代例子:
import { useNavigate } from 'react-router-dom';
interface NavigationProps {
name: string;
}
const Navigation: React.FC<NavigationProps> = ({ name }) => {
const navigate = useNavigate();
const toggleRoute = (path: string) => {
// 编程式导航,取代了原本的 history.push(path)
navigate(path);
};
return (
<div>
{/* 导航栏视图 */}
</div>
);
};
export default Navigation;匹配机制:v6 移除了 exact 和 strict
在 React Router v6 中,路由匹配逻辑被彻底重写:
- 默认精准匹配:不再需要写
exact。所有的<Route path="/a">都会精准匹配/a。 - 嵌套与模糊匹配:如果你希望匹配该路径下的所有子路由,必须显式在末尾添加通配符,即
path="/a/*"。 - 最佳匹配机制:路由的顺序不再重要(不需要像 v5 那样小心翼翼地把具体路径放在前面)。v6 拥有智能的打分机制,会自动选择最匹配的路由。
路由配置化:useRoutes 取代 react-router-config
在 v5 中,我们习惯使用 react-router-config 或手写遍历 <Switch> 的形式来实现路由配置树。
在 v6 中,官方直接内置了路由配置化的 Hook:useRoutes,它让嵌套路由的声明变得像 JSON 一样简单。
import { useRoutes, RouteObject } from "react-router-dom";
const routes: RouteObject[] = [
{
path: "/",
element: <HomePage /> // 注意 v6 使用 element 而不是 component,且传入 JSX
},
{
path: "/user",
element: <UserPage />,
children: [
{ path: "profile", element: <Profile /> } // 嵌套子路由
]
}
];
function App() {
const element = useRoutes(routes);
return element;
}路由变化响应
在 VueJS 技术栈中,vue-router 提供路由响应的钩子函数,例如:beforeEach、afterEach 等等。
但在 React 中,react-router 并不提供这种全局的钩子函数。如果有顶部导航栏,不同页面切换时,高亮不同的标签,我们该如何监听路由的变化呢?
在早期的类组件时代,开发者经常依赖 componentWillReceiveProps(已被废弃)或者 getDerivedStateFromProps 配合 withRouter 来监听。
在现代 React(Hooks 时代)和 React Router v6 中,这件事变得极其优雅:直接使用 useLocation 配合 useEffect 即可。
import { useEffect, useState } from "react";
import { useLocation } from "react-router-dom";
const Navigation: React.FC = () => {
const location = useLocation();
const [selectedPath, setSelectedPath] = useState<string>("");
useEffect(() => {
// 每次 location.pathname 发生变化时,这个 useEffect 都会触发
console.log("路由跳转到了:", location.pathname);
// 我们可以在这里更新导航栏的高亮状态,或者发送 PV 埋点数据
setSelectedPath(location.pathname);
}, [location.pathname]); // 依赖项为当前的路径
return <div>导航栏选中信息:{selectedPath}</div>;
};
export default Navigation;优势:
- 抛弃了臃肿的类组件和生命周期。
- 数据流清晰:依赖
location.pathname,当其改变时自动触发副作用。 - 如果只想渲染视图而不涉及副作用请求,甚至连
useEffect都不需要,直接在 Render 函数中取用location.pathname即可。
