npm install react-router-dom
// 目前版本: v6.3
官方案例:
import { render } from "react-dom";
import {
BrowserRouter,
Routes,
Route
} from "react-router-dom";
import App from "./App";
import Expenses from "./routes/expenses";
import Invoices from "./routes/invoices";
const rootElement = document.getElementById("root");
render(
<BrowserRouter>
<Routes>
<Route path="/" element={<App />} />
<Route path="/expenses" element={<Expenses />} />
<Route path="/invoices" element={<Invoices />} />
</Routes>
</BrowserRouter>,
rootElement
);
BrowserRouter为history模式
HashRouter为hash模式
注意:BrowserRouter组件最好放在最顶层所有组件之外,这样能确保内部组件使用Link做路由跳转时不出错
如下:
function App() {
return (
<Router>
<Routes>
<Route path="/product/:id" element={<ProductDetails/>}></Route>
<Route path="/home" element={<StudentList />}></Route>
</Routes>
</Router>
);
}
<Route path="/expenses" element={<Home/>} />
path 为路由名 , element 为对应的组件
注:element 的值 必须 写成标签的形式
示例:
&lt;BrowserRouter&gt;
&lt;Route path=&quot;/&quot; component={ Home } /&gt;
&lt;Route path=&quot;/product&quot; component={ Product } /&gt;
&lt;/BrowserRouter&gt;
如上代码:
在当用户输入/product时,将会匹配到两个路由,/ 及 /product ;则会显示两个组件 ;
原因是老版本路由在匹配时,是进行模糊匹配
解决方案:
步骤1:使用Switch让路由只能匹配一个; 注意顺序问题,路由先从上向下进行匹配
&lt;BrowserRouter&gt;
&lt;Switch&gt;
&lt;Route path=&quot;/&quot; element={&lt;Home /&gt;} /&gt;
&lt;Route path=&quot;/product&quot; element={&lt;Product /&gt;} /&gt;
&lt;/Switch&gt;
&lt;/BrowserRouter&gt; 步骤2:使用exact关键字,让路由进行精准匹配
加上以上关键字,路由将会精准匹配,只会匹配,path为”/" 的路由
v6 中 Switch 名称变为 Routes , 且Route 标签必须包含在Routes标签里,会不然报错
也就是说,路由只能匹配到一个,不会在出现多个路由匹配的情况
v6 内部算法改变,不再需要加exact实现精确匹配路由,默认就是匹配完整路径。
如果需要旧的行为(模糊匹配),路径后加/*
测试: /prodcuts 显示
/products/4 显示
/products/haha 显示
/products/haha/hehe 显示
结论:看第6点:React Router 能够自动找出最优匹配路径 ,顺序不重要
若:path属性取值为*时,可以匹配任何(非空)路径,同时该匹配拥有最低的优先级。可以用于设置404页面。
&lt;Routes&gt;
&lt;Route path=&#39;/foo&#39; element={Foo}&gt;
&lt;Route path=&#39;bar&#39; element={Bar}&gt;&lt;/Route&gt;
&lt;Route path=&#39;*&#39; element={NotFound}&gt;&lt;/Route&gt;
&lt;/Route&gt;
&lt;/Routes&gt; Link,NavLink 类似与a标准,区别NavLink可以设置高亮样式
<Link to="/home">首页</Link>
NavLink的使用,及激活状态的样式设置
V5老版本,activeClassName设置,或activeStyle
&lt;NavLink to=&quot;/&quot; activeClassName=&#39;active-menu&#39;&gt;
&lt;i className=&quot;fa fa-dashboard&quot;&gt;&lt;/i&gt;
&lt;span&gt;首页&lt;/span&gt;
&lt;/NavLink&gt; V6新版本,activeClassName 与 activeStyle属性被移除
可以直接在的className和style中使用一个函数来设置激活状态的样式。
方法:通过箭头函数接收到isActive参数值,通过isActive的值来设置
通过className
&lt;NavLink
to=&quot;/faq&quot;
className={({ isActive }) =&gt;
isActive ? &#39;active&#39; : &#39;&#39;
}
&gt;
首页
&lt;/NavLink&gt;
通过style
&lt;NavLink to=&quot;/product/1&quot; style={({ isActive }) =&gt; {
return {
fontWeight: &quot;bold&quot;,
color: isActive ? &quot;red&quot; : &quot;&quot;
};
}}
fontWeight: "bold" 不管是否激活,都会有; 因为没有判断
<Route path="/" element ={<Navigate replace to="/home" />} />
<Navigate replace to="" />是对旧的 Redirect 的完整取代。
replace 属性也可以省略,不过行为由 replace 改为 push
replace vs push
this.props.history.push('router地址')
push: a-b-c,可以回到上一级
push跳转会形成history,可返回到上一层。
this.props.history.replace('router地址')
replace: a-b-c 回不到上一级 适用于登录后,不需要重新回到登页面
replace跳转不会形成history,不可返回到上一层。
结论: push有历史记录,replace没有历史记录
嵌套路由必须放在 中,且使用相对路径,不再像 v5 那样必须提供完整路径,因此路径变短。
&lt;Route path=&#39;/about&#39; element={&lt;About /&gt;}&gt;
&lt;Route index element={&lt;Address /&gt;} /&gt;
&lt;Route path=&#39;address&#39; element={&lt;Address /&gt;}&gt;&lt;/Route&gt;
&lt;Route path=&#39;information&#39; element={&lt;Information /&gt;}&gt;&lt;/Route&gt;
&lt;Route path=&#39;joinus&#39; element={&lt;Join /&gt;}&gt;&lt;/Route&gt;
&lt;/Route&gt; 上面的访问路径为 /about/address , /about/information, /about/joinus
此组件是一个占位符,告诉 React Router 嵌套的内容应该放到哪里。
export default class about extends Component {
render() {
return (
&lt;div className=&#39;container&#39;&gt;
&lt;h2&gt;关于&lt;/h2&gt;
&lt;div&gt;
&lt;div className=&quot;left&quot;&gt;
&lt;ul&gt;
&lt;li&gt;&lt;Link to=&#39;/about/address&#39;&gt;公司地址&lt;/Link&gt;&lt;/li&gt;
&lt;li&gt;&lt;Link to=&#39;/about/join&#39;&gt;加入我们&lt;/Link&gt;&lt;/li&gt;
&lt;li&gt;&lt;Link to=&#39;/about/story&#39;&gt;背景故事&lt;/Link&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div className=&quot;left&quot;&gt;
&lt;Outlet/&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
)
}
}
<Route path='/about' element={<About />}> <Route index element={<Address />} /> <Route path='address' element={<Address />}></Route> <Route path='information' element={<Information />}></Route> <Route path='joinus' element={<Join />}></Route> </Route>
或者
设置path为空,来指定默认路由
let router =[{
path: &quot;/home&quot;,
element :&lt;Home/ &gt;,
children: [
{
path:&quot;&quot;,
element:&lt;News/&gt;
},
{
path: &quot; news &quot; ,
element:&lt;News/&gt;
}
]
}]
声明式路由中,不能写index, 可以让path: "" , 来实现显示默认组件;
useRoutes函数,会返回已经渲染好的路由元素
const GetRoutes=()=&gt;{
return useRoutes([
{
path:&#39;/&#39;,
element:&lt;Home/&gt;
},
{
path:&#39;/home&#39;,
element:&lt;Home/&gt;
},
{
path:&#39;/product&#39;,
element:&lt;Product/&gt;
},
{
path:&#39;/about&#39;,
element:&lt;About/&gt;,
children:[
{
path:&quot;&quot;,
element:&lt;Story/&gt;
},
{
path:&#39;address&#39;,
element:&lt;Address/&gt;
},
{
path:&#39;join&#39;,
element:&lt;Join/&gt;
},
{
path:&#39;story&#39;,
element:&lt;Story/&gt;
}
]
},
{
path:&#39;/concat&#39;,
element:&lt;Concat/&gt;
},
{
path:&#39;/brand&#39;,
element:&lt;Brand/&gt;
}
])
}
function App() {
return (
&lt;div&gt;
&lt;Router&gt;
&lt;Nav&gt;&lt;/Nav&gt;
&lt;GetRoutes/&gt;
{/* &lt;Routes&gt;
&lt;Route path=&#39;/&#39; element={&lt;Home/&gt;}&gt;&lt;/Route&gt;
&lt;Route path=&#39;/home&#39; element={&lt;Home/&gt;}&gt;&lt;/Route&gt;
&lt;Route path=&#39;/product&#39; element={&lt;Product/&gt;}&gt;&lt;/Route&gt;
&lt;Route path=&#39;/about&#39; element={&lt;About/&gt;}&gt;
&lt;Route index element={&lt;Story/&gt;}&gt;&lt;/Route&gt;
&lt;Route path=&#39;address&#39; element={&lt;Address/&gt;}&gt;&lt;/Route&gt;
&lt;Route path=&#39;join&#39; element={&lt;Join/&gt;}&gt;&lt;/Route&gt;
&lt;Route path=&#39;story&#39; element={&lt;Story/&gt;}&gt;&lt;/Route&gt;
&lt;/Route&gt;
&lt;Route path=&#39;/concat&#39; element={&lt;Concat/&gt;}&gt;&lt;/Route&gt;
&lt;Route path=&#39;/brand&#39; element={&lt;Brand/&gt;}&gt;&lt;/Route&gt;
&lt;/Routes&gt;*/}
&lt;/Router&gt;
&lt;/div&gt;
);
}
export default App;
import {useNavigate} from &quot;react-router-dom&quot;;
const navigate = useNavigate();
//push
navigate(&quot;/welcome&quot;); 如果要重定向:
navigate("/welcome",{replace:true});
除此之外,还可以使用navigate(-1)后退到前一页,使用navigate(-2)后退到前一页的前一页,navigate(1)前向导航,
注:V5版本中的编程式路由导航 this.props.history.replace() 与 this.props.history.push();
在V6中useNavigate 替代
详细版本:
// v6版本编程导航使用 useNavigate (以下为引入代码)
import { useNavigate } from &quot;react-router-dom&quot;;
export default function A() {
const navigate = useNavigate();
//...
}
1.push跳转+携带params参数
navigate(`/b/child1/${id}/${title}`);
2.push跳转+携带search参数
navigate(`/b/child2?id=${id}&title=${title}`);
3.push跳转+携带state参数
navigate("/b/child2", { state: { id, title }});
4.replace跳转+携带params参数
navigate(`/b/child1/${id}/${title}`,{replace: true});
5.replace跳转+携带search参数
navigate(`/b/child2?id=${id}&title=${title}`,{replace: true});
6.replace跳转+携带state参数
navigate("/b/child2", { state: { id, title },replace: true});
在Route组件中的path属性中定义路径参数
在组件内通过useParams hook访问路径参数
&lt;BrowserRouter&gt;
&lt;Routes&gt;
&lt;Route path=&#39;/foo/:id&#39; element={Foo} /&gt;
&lt;/Routes&gt;
&lt;/BrowserRouter&gt;
import { useParams } from &#39;react-router-dom&#39;;
export default function Foo(){
const params = useParams();
return (
&lt;div&gt;
&lt;h1&gt;{params.id}&lt;/h1&gt;
&lt;/div&gt;
)
}
在以前版本中,组件的props会包含一个match对象,在其中可以取到路径参数。但在最新的6.x版本中,无法从props获取参数。
并且,针对类组件的withRouter高阶组件已被移除。
因此对于类组件来说,使用参数有两种兼容方法:
1. 将类组件改写为函数组件传递
2. 写一个HOC来包裹类组件,用useParams获取参数后通过props传入原本的类组件
查询参数不需要在路由中定义
使用useSearchParams hook来访问查询参数。其用法和useState类似,会返回当前对象和更改它的方法
更改searchParams时,必须传入所有的查询参数,否则会覆盖已有参数
import { useSearchParams } from &#39;react-router-dom&#39;;
// 当前路径为 /foo?id=12
function Foo(){
const [searchParams, setSearchParams] = useSearchParams();
console.log(searchParams.get(&#39;id&#39;)) // 12
setSearchParams({
name: &#39;foo&#39;
}) // /foo?name=foo
return (
&lt;div&gt;foo&lt;/div&gt;
)
} 但在最新的6.x版本中,无法从props获取参数。在类组件中获取seach参数的值,解决方法与上面一样.
1.传递参数
&lt;NavLink to={`detail`} state={{
id:item.id,
name:item.name,
content: item.content }}&gt;
{item.name}
&lt;/NavLink&gt; 或
2.接收参数
import React from &#39;react&#39;
import { useLocation } from &#39;react-router-dom&#39;
export default function Detail() {
// 这是连续结构赋值 把useLocation里面呢的state解构,在解构state里面的属性
const {state:{id,name,content}} = useLocation()
return (
&lt;div&gt;
&lt;ul&gt;
&lt;li&gt;id:{id}&lt;/li&gt;
&lt;li&gt;content:{content}&lt;/li&gt;
&lt;li&gt;name:{name}&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
)
} 注: prop属性中的location已经没有了,所以在类组件不能获取到相应的数据了,
解决方案就是1. 写成函数 2. 使用高阶组件HOC (13,14,15,16 都是这样)
17. 多组路由
通常,一个应用中只有一个Routes组件。
但根据实际需要也可以定义多个路由出口(如侧边栏和主页面都要随URL而变化)
&lt;Router&gt;
&lt;SideBar&gt;
&lt;Routes&gt;
&lt;Route&gt;&lt;/Route&gt;
&lt;/Routes&gt;
&lt;/SideBar&gt;
&lt;Main&gt;
&lt;Routes&gt;
&lt;Route&gt;&lt;/Route&gt;
&lt;/Routes&gt;
&lt;/Main&gt;
&lt;/Router&gt;
安装: npm i @loadable/component
import loadable from '@loadable/component'
const ComponentNode = loadable(()=>{ return import("./"+item.componentPath) })
<Route path={item.path} element={<ComponentNode />}>
菜单数据:
var menuInfo = [
{
menuId: 2,
menuName: &quot;用户管理理&quot;,
menuUrl: &quot;/index/user&quot;,
pathRoute:&#39;user&#39;,
pathname: &quot;userlist&quot;,
componentPath: &quot;user/UserManger&quot;,
menuImgClass: &#39;TeamOutlined&#39;,
pId:0,
menuState: &quot;0&quot;,
isContainChildren:false,
menuChilds: [{
menuId: 10,
menuName: &quot;添加用户&quot;,
menuUrl: &quot;/index/user/adduser&quot;,
pathRoute:&#39;adduser&#39;,
pathname: &quot;adduser&quot;,
componentPath: &quot;user/AddUser&quot;,
menuImgClass: &#39;VideoCameraAddOutlined&#39;,
pId:2,
menuState: &quot;0&quot;,
isContainChildren:false,
menuChilds: []
},{
menuId: 11,
menuName: &quot;修改用户&quot;,
menuUrl: &quot;/index/user/modifyUser&quot;,
pathRoute:&#39;modifyUser&#39;,
pathname: &quot;modifyUser&quot;,
componentPath: &quot;user/ModifyUser&quot;,
menuImgClass: &#39;VideoCameraAddOutlined&#39;,
pId:2,
menuState: &quot;0&quot;,
isContainChildren:false,
menuChilds: []
}]
},
{
menuId: 3,
menuName: &quot;角色管理理理&quot;,
menuUrl: &quot;/index/role&quot;,
pathRoute:&#39;role&#39;,
pathname: &quot;role&quot;,
componentPath: &quot;user/RoleManger&quot;,
menuImgClass: &#39;WhatsAppOutlined&#39;,
pId:0,
menuState: &quot;0&quot;,
isContainChildren:true,
menuChilds: [
{
menuId: 7,
menuName: &quot;添加角色&quot;,
menuUrl: &quot;/index/role/addrole&quot;,
pathRoute:&#39;addrole&#39;,
pathname: &quot;addrole&quot;,
componentPath: &quot;user/AddRole&quot;,
menuImgClass: &#39;VideoCameraAddOutlined&#39;,
pId:3,
menuState: &quot;0&quot;,
isContainChildren:false,
menuChilds: []
},
{
menuId: 8,
menuName: &quot;角色详情&quot;,
menuUrl: &quot;/index/role/roleInfo&quot;,
pathRoute:&#39;roleInfo&#39;,
pathname: &quot;roleInfo&quot;,
componentPath: &quot;user/RoleInfo&quot;,
menuImgClass: &#39;TagOutlined&#39;,
isContainChildren:false,
pId:3,
menuState: &quot;0&quot;,
menuChilds: []
},
{
menuId: 9,
menuName: &quot;角色列表&quot;,
menuUrl: &quot;/index/role/rolelist&quot;,
pathRoute:&#39;rolelist&#39;,
pathname: &quot;rolelist&quot;,
componentPath: &quot;user/RoleList&quot;,
menuImgClass: &#39;StarOutlined&#39;,
pId:3,
menuState: &quot;0&quot;,
isContainChildren:false,
menuChilds: []
}
]
}
];
动态路由生成组件:
// 用于创建路由(可以根据数据,生成动态的路由)
import {useRoutes} from &#39;react-router-dom&#39;
import Login from &#39;../pages/Login&#39;
import Home from &#39;../pages/Home&#39;
// react 动态加载组件 @loadable/component
import loadable from &#39;@loadable/component&#39;
import {observer,inject} from &#39;mobx-react&#39;
const PrivateRoute = (props)=&gt;{
function bindRouter(list){
let arr = [];
list.map((item)=&gt;{
const ComponentNode = loadable(()=&gt;{
return import(&quot;./&quot;+item.componentPath)
})
if(item.menuChilds &amp;&amp; item.menuChilds.length&gt;0){
if(item.isContainChildren){
arr.push({
path:item.pathRoute,
element:&lt;ComponentNode/&gt;,
children:[...bindRouter(item.menuChilds)]
})
}else{
arr.push({
path:item.pathRoute,
//element:&lt;ComponentNode/&gt;
children:[...bindRouter(item.menuChilds)]
})
}
}else{
arr.push({
path:item.pathRoute,
element:&lt;ComponentNode/&gt;
})
}
})
return arr;
}
const menuInfo = props.user.userInfo.menuInfo ? props.user.userInfo.menuInfo:[];
return useRoutes([
{
path:&quot;/&quot;,
element:&lt;Login/&gt;
},
{
path:&quot;/index&quot;,
element:&lt;Home /&gt;,
children:[...bindRouter(menuInfo)]
}
])
}
export default inject(&#39;user&#39;)(observer(PrivateRoute)); 更多内容,请关注公主号:bug收集