React Router(react-router-dom V6 整理)
官方文档
一个神奇的链接: React Router 官方文档
安装
运行以下命令安装React Router:
npm install react-router-dom@6 --save
注意:react-router-dom
包含所有内容,导入组件时应该从react-router-dom
中导入,而不应该从 react-router
中导入,否则,会意外地在应用中导入不匹配的库版本;
基本用法
在Web应用程序中开启 React Router 功能
// index.js
import React from 'react';
import { createRoot } from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import App from './App';
const container = document.getElementById('root');
const root = createRoot(container);
root.render(
// 通过在应用入口添加 BrowserRouter 组件开启 React Router 功能
<BrowserRouter>
<React.StrictMode>
<App />
</React.StrictMode>
</BrowserRouter>
);
注意:web 应用程序中一般使用 BrowserRouter
组件, 还用另一种 HashRouter
组件方式;
这两种方式的区别:
- 底层原理不一样:
BrowserRouter
调用的是 H5 history API,低版本兼容性问题;
HashRouter
使用的是 URL 哈希值; - 地址栏表现形式不一样:
BrowserRouter
的路径:localhost:3000/index
HashRouter
的路径:localhost:3000/#/index - 刷新后对路由 state 参数的影响
BrowserRouter
没有任何影响,因为 state 保存在 history 对象中
HashRouter
刷新后会导致路由 state 参数的丢失
值得注意的是,官方强烈建议不要使用 HashRouter
;
配置路由
点击查看代码
// App.js
// 导入 Route, Routes 组件
import { Route, Routes } from 'react-router-dom';
function App() {
return (
<Routes>
{/* 页面默认导航到 Home 组件(页面上显示 Home Compontent) */}
<Route path='/' element={<Home />} />
{/* 在地址输入 http://localhost:3000/about 导航到 About 组件(页面上显示 About Compontent) */}
<Route path='/about' element={<About />} />
</Routes>
);
}
const Home = (props) => {
return <div>Home Compontent</div>;
}
const About = (props) => {
return <div>About Compontent</div>;
}
export default App;
在以前版本的 React Router 中,必须以某种方式对路由进行排序,以便在多个路由与不明确的 URL 匹配时获得要呈现的正确路由。V6更智能,会选择最具体的匹配;
添加 "不匹配" 路由
点击查看代码
// App.js
import { Route, Routes } from 'react-router-dom';
function App() {
return (
<Routes>
<Route path='/' element={<Home />} />
<Route path='/about' element={<About />} />
{/* 当没有其他路由与 URL 匹配时,匹配 path='*'的路由 */}
<Route path='*' element={<NotFount />} />
</Routes>
);
}
const Home = (props) => {
return <div>Home Compontent</div>;
}
const About = (props) => {
return <div>About Compontent</div>;
}
const NotFount = (props) => {
return <div>NotFount !!!</div>;
}
export default App;
当没有其他路由与 URL 匹配时,才会匹配 path='*'
路由。此路由将匹配任何 URL,但优先级最弱,因此路由器仅在没有其他路由匹配时才会选择它;
使用链接导航
点击查看代码
// App.js
// 导入 Link 组件
import { Route, Routes, Link } from 'react-router-dom';
function App() {
return (
<Routes>
{/* 页面默认导航到 Home 组件(渲染 Home 组件, 页面显示 About Compontent 链接) */}
<Route path='/' element={<Home />} />
<Route path='/about' element={<About />} />
</Routes>
);
}
const Home = (props) => {
return <div>
{/* 点击 About Link 链接跳转至 http://localhost:3000/about
画面显示 About 组件内容(Home Link链接)
*/}
<Link to='/about'>About Link</Link>
</div>;
}
const About = (props) => {
return <div>
{/* 点击 Home Link 链接跳转至 http://localhost:3000/
画面显示 Home 组件内容(About Link链接)
*/}
<Link to='/'>Home Link</Link>
</div>;
}
export default App;
使用嵌套路由
点击查看代码
// App.js
// 导入 Link, Outlet 组件
import { Route, Routes, Link, Outlet } from 'react-router-dom';
function App() {
return (
<Routes>
<Route path='/' element={<Home />} >
<Route path='about' element={<About />} />
<Route path='setting' element={<Setting />} />
{/* 默认子路由
如果导航栏地址为 http://localhost:3000,此时子路由渲染位置(Outlet)为空白,
增加以下配置,子路由渲染位置(Outlet)渲染 <List />
*/}
<Route index element={<List />} />
</Route>
</Routes>
);
}
const Home = (props) => {
return <>
<div>
<Link to='/about'>About Link</Link> | {" "}
<Link to='/setting'>Setting Link</Link>
</div>
<div style={{padding: '20px', margin: '10px', borderTop: '1px solid'}}>
{/* Outlet 为嵌套子路由的出口,比如:点击 About Link 链接,
浏览器地址变为 http://localhost:3000/about
在此渲染路由地址为 /about 的组件(在此显示: About Compontent)
*/}
<Outlet />
</div>
</>;
}
const About = (props) => {
return <div>
About Compontent
</div>;
}
const Setting = (props) => {
return <div>
Setting Compontent
</div>;
}
const List = (props) => {
return <div>
List Compontent
</div>;
}
export default App;
这是 React Router 最强大的功能之一,在实际开发中,大多数 UI 都是一系列嵌套布局,React Router 通过这种嵌套路由的方式实现了一些自动、持久的布局处理;
使用活动链接
点击查看代码
// App.js
// 导入 NavLink 组件
import { Route, Routes, NavLink, Outlet } from 'react-router-dom';
function App() {
return (
<Routes>
<Route path='/' element={<Home />} >
<Route path='about' element={<About />} />
<Route path='setting' element={<Setting />} />
<Route index element={<List />} />
</Route>
</Routes>
);
}
const Home = (props) => {
return <>
<div>
{/* <NavLink /> 接收一个style 或者 className 属性
属性值为一个回调函数,可以通过 isActive 的值判断
链接是否处于活动状态,从而实现给活动链接节点添加样式的效果
示例效果:点击哪个链接,目标链接字体变红
*/}
<NavLink
style={({ isActive }) => navColor(isActive)}
to='/about'
>
About Link
</NavLink> | {" "}
<NavLink
style={({isActive}) => navColor(isActive)}
to='/setting'
>
Setting Link
</NavLink>
</div>
<div style={{ padding: '20px', margin: '10px', borderTop: '1px solid' }}>
<Outlet />
</div>
</>;
}
const About = (props) => {
return <div>
About Compontent
</div>;
}
const Setting = (props) => {
return <div>
Setting Compontent
</div>;
}
const List = (props) => {
return <div>
List Compontent
</div>;
}
const navColor = (isActive) => {
return {color: isActive ? 'red' : ""}
}
export default App;
主要实现了给当前激活的链接设置一个式样,支持 style 和 className 这两种属性;
读取 URL 参数
点击查看代码
// 导入 useParams 组件
import { Route, Routes, NavLink, Outlet, useParams } from 'react-router-dom';
function App() {
return (
<Routes>
<Route path='/' element={<Home />} >
<Route path='list' element={<List />} >
<Route path=':id' element={<Item />} />
</Route>
</Route>
</Routes>
);
}
const Home = (props) => {
return <>
<div>
<NavLink
style={({ isActive }) => navColor(isActive)}
to='/list'
>
List Link
</NavLink>
</div>
<div style={{ padding: '20px', margin: '10px', borderTop: '1px solid' }}>
<Outlet />
</div>
</>;
}
const Item = (props) => {
// 从 URL 获取参数::id
const params = useParams();
return <h2>Item: {params.id}</h2>;
}
const List = (props) => {
const list = [
{
name: "赵云",
no: 100
},
{
name: "马超",
no: 101
}
]
return <div>
{list.map((item) => {
return (<NavLink
style={({isActive}) => navColor(isActive)}
to={`/list/${item.no}`}
key={item.no}
>
{item.name}
</NavLink>)
})}
<div className='content'>
{/* 指定子路由 /list/? 的渲染位置 */}
<Outlet />
</div>
</div>;
}
const navColor = (isActive) => {
return {
color: isActive ? 'red' : "",
marginRight: '10px'
}
}
export default App;
与 V5 的区别
<Routes>
替代<Switch>
写法上的比较:
v6 的优点:// v5 写法 // 引入 react-router import { Route, Switch } from 'react-router-dom'; function App() { return ( <Switch> {/* 路由配置 */} </Switch> ); } // v6 写法 import { Route, Routes } from 'react-router-dom'; function App() { return ( // Routes 替换 Switch <Routes> {/* 路由配置 */} </Routes> ); }
v6 提供了功能更强大的 Routes 组件来代替 Switch 组件,Routes 不再按顺序匹配路径,而是采用了一种自动匹配最佳路径的方法;<Route>
不再支持子组件,改为使用 element 属性;并且不再需要 exact 属性了
写法上的比较:
v6 的优点:// v5 写法 // 引入 react-router import { Route, Switch } from 'react-router-dom'; function App() { return ( <Switch> <Route exact path='/home'> <Home /> </Route> </Switch> ); } // v6 写法 import { Route, Routes } from 'react-router-dom'; function App() { return ( <Routes> <Route path='/home' element={<Home /> } /> </Routes> ); }
- v6 提供的 element 属性, 可以使开发者更加方便的注入想要的 props;
- v6
中的 path 属性是相对的; - 可以按照所需的任何顺序放置路由,路由器将自动检测当前URL的最佳路由;
- 移除了
<NavLink>
的 activeClassName 属性
v6写法:import { NavLink } from 'react-router-dom'; function App() { return ( <> {/* className 写法 */} <NavLink className={({isActive}) => { return isActive ? "highlight" : ""; }} to="home">Home</NavLink> {/* style 写法 */} <NavLink to="about" style={({isActive}) => { return { color: isActive ? "red" : "" } }} >About</NavLink> </> ); }
- 移除
<Redirect>
,改为使用<Navigate>
写法上的对比:// v6 写法 import { Navigate, Route, Routes } from 'react-router-dom'; function App() { return ( <Routes> <Route path='/' element={<Navigate replace to="/home" />} /> </Routes> ); }
<Link to>
支持相对位置// 配置路由 <Route path="app"> <Route path="home"> <Route path="list" /> </Route> </Route> // 当前 URL 是 /app/home <Link to="list"> => <a href="/app/home/list"> <Link to="../list"> => <a href="/app/list"> <Link to="../../list"> => <a href="/list"> <Link to="../../../list"> => <a href="/list">
- 新增
<Outlet>
关于<Outlet>
,参考本文的嵌套路由节点; 此组件是一个占位符,告诉 React Router 嵌套的内容应该放到哪里; 通过<Outlet>
可以将所有的路由(嵌套的子路由)配置合并在一起,可进行路由的统一管理,增加了代码可维护性; - 使用 useNavigate 实现编程式导航,从而代替 useHistory
主要写法变更:// v6 写法 import { useNavigate } from 'react-router-dom'; function App() { const navigate = useNavigate(); const handleClick = () => { navigate('/home'); // push // 重定向 // navigate('/home', {replace: true}); }; return ( <div> <button onClick={handleClick}>返回首页</button> </div> ); }
history.push("/") => navigate("/")
history.replace("/") => navigate("/",{ replace: true })
history.goBack() => navigate(-1)
history.goForward() => navigate(1)
history.go(2) => navigate(2)
- 一系列的 Hooks
hooks名 作用 说明 useParams
返回当前参数 根据路径读取参数 useNavigate
返回当前路由 代替原有V5中的 useHistory useOutlet
返回根据路由生成的element useLocation
返回当前的location 对象 useRoutes
同Routers组件一样,只不过是在js中使用 useSearchParams
用来匹配URL中?后面的搜索参数
总结
本文主要记录了一下 React Router V6 的一些基本用法以及对V5的比较,有了这些知识的支撑,足以应付大多数日常开发了;v6 版本基于全新的路由算法带来强大的功能和 hooks,并且重新实现了 useNavigate 来替代 useHistory ,整体上更加好理解;
先记录这么多,后续持续更新!!!