React Router v6

v6 版本相对比前面有较大修改

安装

1
yarn add react-router-dom

基本使用

常用方法

Routes

新版本 Routes 替代了 Switch,并且 Routes 和 Route 必须关联,
Route 内可以直接嵌套子路由.子路由的位置可以由Outlet组件占位.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import { Routes , Route , Outlet  } from 'react-router
import { BrowserRouter } from 'react-router-dom'
const index = () => {
return <div className="page" >
<div className="content" >
<BrowserRouter >
<Menus />
<Routes>
<Route element={<Home />}
path="/home"
></Route>
<Route element={<List/>}
path="/list"
></Route>
<Route element={<Layout/>}
path="/children"
>
<Route element={<Child1/>}
path="/children/child1"
></Route>
<Route element={<Child2/>}
path="/children/child2"
></Route>
</Route>
</Routes>
</BrowserRouter>
</div>
</div>
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// Layout渲染二级路由
function Container() {
// 这里Outlet的占位功能,声明了子路由的渲染位置
return (
<div>
{" "}
<Outlet />
</div>
);
}
/* 子路由菜单 */
function Menus1() {
return (
<div>
<Link to={"/children/child1"}> child1 </Link>
<Link to={"/children/child2"}> child2 </Link>
</div>
);
}

function Layout() {
return (
<div>
这里是 children 页面
<Menus1 />
<Container />
</div>
);
}

路由状态和页面跳转

1
<Link to="/invoices">Invoices</Link>

Link 避免了重新渲染。Link 只会触发相匹配路由页面的更新,不会刷新整个页面。
Link 所做的事:

  • 有 onClick 就执行 onClick
  • click 的时候阻止 a 标签默认事件
  • 根据跳转的 href,使用 history 跳转,只是链接变了,并不刷新整个页面

useLocation

可以使用 useLocation 获取到路由状态 location 的信息,这里保存了hash | key | pathname | search | state等状态.

useNavigate

1
2
3
4
5
6
7
8
function Home() {
const navigate = useNavigate();
return <>
<button onClick={() => navigate('/list', { state: 'alien'})}>
跳转列表页
</button>
<>
}

navigate第一个参数是路径,第二个是路由状态信息,可以传递state等信息.

useParams

动态路由

通过useParams获取 url 上的动态路由信息

1
<Route element={<List />} path="/list/:id" />

跳转动态路由:

1
<button onClick={() => navigate("/lsit/1")}>跳转列表页</button>

获取动态参数

1
2
3
4
5
function List() {
const params = useParams();
console.log(params, "params"); // {id: '1'}
return <div>React yes!</div>;
}

useSearchParams

用于 url 参数信息获取或设置.
const [searchParams, setSearchParams] = useSeatchParams()
方法中 searchParams 可以获取 params 中的 url 信息,第二个可以设置.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function Index() {
const [searchParams, setSearchParams] = useSeatchParams();
const name = search.get("name");
return (
<>
<button
onClick={() => {
setSearchParams({ name: "alien", age: 22 });
}}
>
设置params
</button>
</>
);
}

区别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
const Detail = (props) => {
// 方式1:通过search传递参数,比较复杂,不推荐,
//需要用解构赋值获得search对象,然后调用get函数获取对应的key的值
// const [search] = useSearchParams()
// const detail = { id: search.get('id'), title: search.get('title') }

// 方式2:通过params参数传递,非常简单直接使用useParams()
// const detail = useParams()

// 方式3:通过state传递参数
// const { state: detail } = useLocation();
// console.log("此处可以收到来自route的state参数:", detail);

//方式1+plus:自定义的钩子根据传入的key数组拿到search中的数据并包装成对象
const detail = useUrlQueryParam(["id", "title"]);
console.log("获取到对象:", detail);

return (
<ul>
<li>ID:{detail.id}</li>
<li>TITLE:{detail.title}</li>
<li>
CONTENT:{detail.title}+{detail.id}
</li>
</ul>
);
};

export default Detail;

//js下极简的封装
export const useUrlQueryParam = (keys) => {
const [search] = useSearchParams();
//遍历keys,从search中获取对应的值,返回一个新对象
const query = keys.reduce((acc, key) => {
acc[key] = search.get(key);
return acc;
}, {});
return query;
};

useRoutes

用于自由配置路由

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
const routeConfig = [
{
path: "/home",
element: <Home />,
},
{
path: "/list/:id",
element: <List />,
},
{
path: "/children",
element: <Layout />,
children: [
{ path: "/children/child1", element: <Child1 /> },
{ path: "/children/child2", element: <Child2 /> },
],
},
];

const Index = () => {
const element = useRoutes(routeConfig);
return (
<div className="page">
<div className="content">
<Menus />
{element}
</div>
</div>
);
};

const App = () => (
<BrowserRouter>
<Index />
</BrowserRouter>
);

疑难问题

axios 内部只是不能调用 hook,因为 hooks 的定义就是只能在函数组件顶层或其他 hook 内部调用。
解决办法:先在 axios 方法外调用 useNavigate 钩子得到 nav 方法,然后在 axios 回调里用 nav 方法进行路由跳转

Navigate 组件应该是放在业务组件里。比如路径/a 要跳转到路径/b,则是放在路径/a 对应的 A 组件里,不是放在 routes 里面

原理

BrowserRouter

  • 通过 createBrowserHistory 创建 history 对象,并通过 useRef 保存 history 对象。
  • 通过 useLayoutEffect 来监听 history 变化,当 history 发生变化(浏览器人为输入,获取 a 标签跳转,api 跳转等 )。派发更新,渲染整个 router 树。这是和老版本的区别,老版本里面,监听路由变化更新组件是在 Router 中进行的。
  • 还有一点注意的事,在老版本中,有一个 history 对象的概念,新版本中把它叫做 navigator 。

不使用

exact

取消了,所有路由默认都是严格匹配,如果想模糊匹配,可以在路由后加*

新增数据路由

主要是针对数据的新增一些 api。
比如 createBrowser 之类。
loader 方法,在路由加载之前可以用来请求数据。
errorElement:错误组件,路由通过 useRoutesError 返回错误信息,可以调用这个组件。只在使用数据路由时生效。