react-router是一个核心库,我们在做PC端时要用react-router-dom,而在移动端我们要用React-Router-Native
这个库是专门做PC端的路由
npm i react-router-dom
React-Router-Dom最新版本是6,如果下载指定版本加@符
npm i react-router-dom@5
前端路由存在的意义就是单页面应用,英文:Single Page Application,前端有自己的一套路由,页面只有一个html完成,在不同的路径下,加载不同的组件,从而完成单页面应用,在此也可以通过ajax请求的方式去后端获得数据
React-Router-Dom6在2021年11月已成为默认版本
import {Link, Route} from "react-router-dom"
<Link to="/about">About</Link>
<Link to='/home'>Home</Link>
<Switch>
<Route path="/about" component={About}/>
<Route path="/home" component={Home}/>
<Redirect to="/about"/>
</Switch>
link要和Route相互搭配,link书写链接,由Route来注册路由
在react中我们要用BrowserRouter包裹一下路由,通常在index.js里
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import {BrowserRouter} from "react-router-dom"
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<BrowserRouter>
<App/>
</BrowserRouter>
</React.StrictMode>
);
reportWebVitals();
和BrowserRouter一样,只不过HashRoute在url里是有hash值的
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import {HashRouter} from "react-router-dom"
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<HashRouter>
<App/>
</HashRouter>
</React.StrictMode>
);
reportWebVitals();
在React中相同路径下是可以加载多个组件的,假如你不想,你可以用Switch包裹一下Route,这样即使相同路径下,也只是加载最上面的组件
<Switch>
<Route path="/home/news" component={News}/>
<Route path="/home/massage" component={Message}/>
<Route path="/home/massage" component={Detail}/>
<Redirect to="/home/news"/>
</Switch>
上面相同路径但只加载Message组件
在react中我们想在一开始默认加载哪个组件或者路径不适配的时候我们可以用Redirect标签,可以给我们重定向加载任何一个组件,但Redirect一般出现在Route的最下面
<Switch>
<Route path="/home/news" component={News}/>
<Route path="/home/massage" component={Message}/>
<Redirect to="/home/news"/>
</Switch>
假设现在我们需要给予选中路由一个高亮我们可以使用NavLink标签,我们可以把他单独封装成一个公用的路由组件
import React, {Component, Fragment} from "react"
import {NavLink} from 'react-router-dom'
import "./MyNavLink.css"
export default class MyNavLink extends Component {
render() {
return (
<Fragment>
<div className="btn-div">
<NavLink activeClassName="active" className="btn-a" {...this.props}/>
</div>
</Fragment>
)
}
}
注意高亮默认在React-Router中式action属性
如果我们想在react项目中,写入多级路由的话,我们需要在Link,Route以及Redirect中加入父级路由
import {Fragment} from "react"
import {Route, Switch, Redirect} from "react-router-dom"
import Title from "./component/Title";
import About from "./pages/About";
import Home from "./pages/Home";
import MyNavLink from "./MyNavLink";
import './App.css';
function App() {
return (
<Fragment>
<Title/>
<MyNavLink to="/about">About</MyNavLink>
<MyNavLink to='/home'>Home</MyNavLink>
<Switch>
<Route path="/about" component={About}/>
<Route path="/home" component={Home}/>
<Redirect to="/about"/>
</Switch>
</Fragment>
);
}
export default App;
import React, {Component, Fragment} from "react"
import {Switch, Route, Redirect} from 'react-router-dom'
import MyNavLink from "../../MyNavLink";
import Message from "./Message";
import News from "./News";
export default class Home extends Component {
render() {
return (
<Fragment>
<h1>我是Home</h1><br/>
<MyNavLink to="/home/news">News</MyNavLink>
<MyNavLink to="/home/massage">Massage</MyNavLink>
<Switch>
<Route path="/home/news" component={News}/>
<Route path="/home/massage" component={Message}/>
<Redirect to="/home/news"/>
</Switch>
</Fragment>
)
}
}
<Fragment>
<Title/>
<MyNavLink to="/about">About</MyNavLink>
<MyNavLink to='/home/a/b'>Home</MyNavLink>
<Switch>
<Route path="/about" component={About}/>
<Route path="/home" component={Home}/>
<Redirect to="/about"/>
</Switch>
</Fragment>
像这样的路由我们多传,虽然路径不匹配但在模糊匹配下可以让react渲染Home组件,但是不可以头路径不正确
例如:
<Fragment>
<Title/>
<MyNavLink to="/about">About</MyNavLink>
<MyNavLink to='/a/home'>Home</MyNavLink>
<Switch>
<Route path="/about" component={About}/>
<Route path="/home" component={Home}/>
<Redirect to="/about"/>
</Switch>
</Fragment>
这样前面的路径不对是匹配不到的
假如现在我们不希望进行模糊匹配,严格按照路径的话我们就可以开启严格匹配模式
<Fragment>
<Title/>
<MyNavLink to="/about">About</MyNavLink>
<MyNavLink to='/a/home'>Home</MyNavLink>
<Switch>
<Route exact={true} path="/about" component={About}/>
<Route exact path="/home" component={Home}/>
<Redirect to="/about"/>
</Switch>
</Fragment>
我们不可以过多的使用严格模式就像ref一样,否则会造成一些问题,并且在多级路由的父路由不可以开启严格模式
this.state.massageArr.map(item => {
return <li key={item.id}>
<Link to={`/home/massage/detail/${item.id}/${item.title}`}>
{item.title}
</Link>
</li>
})
我们通过数组的映射以及母版字符串的方式传递了两个Params参数
<Route path="/home/massage/detail/:id/:title" component={Detail}/>
这样接收react-router帮我们生成了对象
export default class Detail extends Component {
render() {
const {id, title} = this.props.match.params
const findResult = data.find((_item) => {
return _item.id === id
})
return (
<Fragment>
<ul>
<li>id:{id}</li>
<li>title:{title}</li>
<li>content:{findResult.content}</li>
</ul>
</Fragment>
)
}
}
我们可以通过props里面的match里面封装的params里面将传递的值解构出来使用
this.state.massageArr.map(item => {
return <li key={item.id}>
<Link to={`/home/massage/detail?id=${item.id}&title=${item.title}`}>
{item.title}
</Link>
</li>
})
同样我们也传递了两个Search参数,但注意Search参数不需要接收
key=value&key=value
像这样的编码格式被称为urlencoded编码格式
我们可以转化
import qs from "querystring"
qs.stringify() 将对象转化为urlencoded
qs.parse() 将urlencoded转化为js中的对象
import React, {Component, Fragment} from "react"
import qs from "querystring"
const data = [
{id: "01", content: "你好,宁夏"},
{id: "02", content: "你好,吉林"},
{id: "03", content: "你好,陕西"},
]
export default class Detail extends Component {
render() {
const {search} = this.props.location
const {id, title} = qs.parse(search.slice(1))
const findResult = data.find((_item) => {
return _item.id === id
})
return (
<Fragment>
<ul>
<li>id:{id}</li>
<li>title:{title}</li>
<li>content:{findResult.content}</li>
</ul>
</Fragment>
)
}
}
如果没有这个第三方库我们可以安装
npm i querystring
this.state.massageArr.map(item => {
return <li key={item.id}>
<Link to={{pathname: "/home/massage/detail", state: {id: item.id, title: item.title}}}>
{item.title}
</Link>
</li>
})
传递state对象,要把地址和state封装成一个对象
import React, {Component, Fragment} from "react"
const data = [
{id: "01", content: "你好,宁夏"},
{id: "02", content: "你好,吉林"},
{id: "03", content: "你好,陕西"},
]
export default class Detail extends Component {
render() {
const {id, title} = this.props.location.state
const findResult = data.find((_item) => {
return _item.id === id
})
return (
<Fragment>
<ul>
<li>id:{id}</li>
<li>title:{title}</li>
<li>content:{findResult.content}</li>
</ul>
</Fragment>
)
}
}
push是压栈式的,浏览器可以回退,有保存记录。而replace是覆盖浏览器的栈顶不能回退
replaceShow = (id, title) => {
this.props.history.replace(`/home/massage/detail/${id}/${title}`)
}
注意:只有在路由组件内才可以拿到这两个方法
pushShow = (id, title) => {
this.props.history.push(`/home/massage/detail/${id}/${title}`)
}
如果我们想在非路由组件里调用路由组件的方法我们就可以用withRouter
import React, {Component, Fragment} from "react"
import {withRouter} from "react-router-dom"
class Title extends Component {
back = () => {
this.props.history.goBack()
}
forWord = () => {
this.props.history.goForward()
}
render() {
return (
<Fragment>
<h2 style={{marginLeft: "50px"}}>React Router Demo</h2>
<button onClick={this.forWord}>前进</button>
<button onClick={this.back}>后退</button>
<hr/>
</Fragment>
)
}
}
export default withRouter(Title)
注意:goBack为历史记录的后退,而goForward为前进,两个函数后可以在路由组件找到
安装
npm i react-router-dom@6
内置组件以及写法有些改变,函数式组件被官方明确推荐,又新增10个hooks
import {NavLink, Routes, Route} from "react-router-dom"
<Routes>
<Route path="/about" element={<About/>}/>
<Route path="/home" element={<Home/>}/>
</Routes>
import {NavLink, Routes, Route} from "react-router-dom"
<Routes>
<Route path="/about" element={<About/>}/>
<Route path="/home" element={<Home/>}/>
<Route path="/" element={<Navigate to="/about"/>}/>
</Routes>
<NavLink to="/about" className={({isActive}) => isActive ? "list-group-item active":"list-group-item"}>About</NavLink>
在react-router-dom6中可以用函数的方式返回出样式
import {NavLink, useRoutes} from "react-router-dom"
const element = useRoutes(route)
我们可以通过路由表的方式生成路由
import About from "../pages/About";
import Home from "../pages/Home";
import {Navigate} from "react-router-dom";
export const route = [
{
path: "/about",
element: <About/>
},
{
path: "/home",
element: <Home/>
},
{
path: "/",
element: <Navigate to="/about"/>
}
]
多级路由也可以写在路由表里,但是需要Outlet配合,类似Vue的roter-view
多级路由的路由表
import About from "../pages/About";
import Home from "../pages/Home";
import Message from "../pages/Message";
import News from "../pages/News";
import {Navigate} from "react-router-dom";
export const route = [
{
path: "/about",
element: <About/>
},
{
path: "/home",
element: <Home/>,
children: [
{
path: "news",
element: <News/>
},
{
path: "message",
element: <Message/>
}
]
},
{
path: "/",
element: <Navigate to="/about"/>
}
]
子路由可以用children来写,类型也是数组,注意路径不要加 “/”
Outlet是子路由的出口
import React, {Fragment, useState} from "react"
import {Navigate,Outlet,NavLink} from "react-router-dom"
export default function Home() {
const [sum, setSum] = useState(1)
return (
<Fragment>
<h3>Home</h3>
{sum === 2 ? <Navigate to="/about"/> : <h4>当前sum为{sum}</h4>}
<button onClick={() => setSum(sum + 1)}>点我加一</button>
<NavLink to="message">message</NavLink>
<NavLink to='news'>news</NavLink>
<Outlet/>
</Fragment>
)
}
import React, {Fragment, useState} from "react"
import {Link, Outlet} from "react-router-dom"
export default function Message() {
const [message] = useState([
{id: "001", title: "消息1", content: "锄禾日当午"},
{id: "002", title: "消息2", content: "汗滴禾下土"},
{id: "003", title: "消息3", content: "谁知盘中餐"},
{id: "004", title: "消息4", content: "粒粒皆辛苦"},
])
return (
<Fragment>
<ul>
{
message.map((_item) => {
return <li><Link
to={`detail/${_item.id}/${_item.title}/${_item.content}`}>
{_item.title}
</Link></li>
})
}
</ul>
<hr/>
<Outlet/>
</Fragment>
)
}
和react-router-dom5版本一样在路径后面加
路由表需要接收Params参数
import About from "../pages/About";
import Home from "../pages/Home";
import Message from "../pages/Message";
import News from "../pages/News";
import Detail from "../pages/Detail";
import {Navigate} from "react-router-dom";
export const route = [
{
path: "/about",
element: <About/>
},
{
path: "/home",
element: <Home/>,
children: [
{
path: "news",
element: <News/>
},
{
path: "message",
element: <Message/>,
children: [
{
path: "detail/:id/:title/:content",
element: <Detail/>
}
]
}
]
},
{
path: "/",
element: <Navigate to="/about"/>
}
]
import React, {Fragment} from "react"
import {useParams} from "react-router-dom"
export default function Detail() {
const {id, title, content} = useParams()
return (
<Fragment>
<ol>
<li>{id}</li>
<li>{title}</li>
<li>{content}</li>
</ol>
</Fragment>
)
}
import React, {Fragment} from "react"
import {useMatch} from "react-router-dom"
export default function Detail() {
const {id, title, content} = useMatch("/home/message/detail/:id/:title/:content").params
return (
<Fragment>
<ol>
<li>{id}</li>
<li>{title}</li>
<li>{content}</li>
</ol>
</Fragment>
)
}
注意useMatch这个hook要求把整个路径全部当为参数传递进去
import React, {Fragment, useState} from "react"
import {Link, Outlet} from "react-router-dom"
export default function Message() {
const [message] = useState([
{id: "001", title: "消息1", content: "锄禾日当午"},
{id: "002", title: "消息2", content: "汗滴禾下土"},
{id: "003", title: "消息3", content: "谁知盘中餐"},
{id: "004", title: "消息4", content: "粒粒皆辛苦"},
])
return (
<Fragment>
<ul>
{
message.map((_item) => {
return <li key={_item.id}><Link
to={`detail?id=${_item.id}&title=${_item.title}&content=${_item.content}`}>
{_item.title}
</Link></li>
})
}
</ul>
<hr/>
<Outlet/>
</Fragment>
)
注意Search不需要接收
import React, {Fragment} from "react"
import {useSearchParams} from "react-router-dom"
export default function Detail() {
const [search, setSearch] = useSearchParams()
const id = search.get("id")
const title = search.get("title")
const content = search.get("content")
return (
<Fragment>
<ol>
<li>{id}</li>
<li>{title}</li>
<li>{content}</li>
</ol>
<button onClick={() => {
setSearch('id=008&title=哈哈&content=嘻嘻')
}}>点我更新Search
</button>
</Fragment>
)
}
这个hook类似useState,生成两个,我们需要调用get方法以及合适的字段,就能拿到相应的状态
import React, {Fragment, useState} from "react"
import {Link, Outlet} from "react-router-dom"
export default function Message() {
const [message] = useState([
{id: "001", title: "消息1", content: "锄禾日当午"},
{id: "002", title: "消息2", content: "汗滴禾下土"},
{id: "003", title: "消息3", content: "谁知盘中餐"},
{id: "004", title: "消息4", content: "粒粒皆辛苦"},
])
return (
<Fragment>
<ul>
{
message.map((_item) => {
return <li key={_item.id}><Link
to="detail"
state={{
id: _item.id,
title: _item.title,
content: _item.content
}}
>{_item.title}
</Link></li>
})
}
</ul>
<hr/>
<Outlet/>
</Fragment>
)
}
import React, {Fragment} from "react"
import {useLocation} from "react-router-dom"
export default function Detail() {
const {state: {id, title, content}} = useLocation()
return (
<Fragment>
<ol>
<li>{id}</li>
<li>{title}</li>
<li>{content}</li>
</ol>
</Fragment>
)
}
我们需要连续解构赋值,在useLocation中将状态state解构,再将想要的状态从stae中结构
import React, {Fragment, useState} from "react"
import {Link, Outlet, useNavigate} from "react-router-dom"
export default function Message() {
const navigate = useNavigate()
function showDetail(item) {
navigate("detail", {
replace: false,
state: {
id: item.id,
title: item.title,
content: item.content
}
})
}
const [message] = useState([
{id: "001", title: "消息1", content: "锄禾日当午"},
{id: "002", title: "消息2", content: "汗滴禾下土"},
{id: "003", title: "消息3", content: "谁知盘中餐"},
{id: "004", title: "消息4", content: "粒粒皆辛苦"},
])
return (
<Fragment>
<ul>
{
message.map((_item) => {
return <li key={_item.id}><Link
to="detail"
state={{
id: _item.id,
title: _item.title,
content: _item.content
}}
>{_item.title}
</Link>
<button onClick={() => {
showDetail(_item)
}}>查看详情
</button>
</li>
})
}
</ul>
<hr/>
<Outlet/>
</Fragment>
)
}
我们要用useNavigate构造一个实例就是navigate是一个函数,我们可以传递两个参数,第一个是路径,第二个是一个对象
import React, {Fragment} from "react";
import {useNavigate} from "react-router-dom"
export default function Title() {
const navigate = useNavigate()
function back() {
navigate(-1)
}
function forWord() {
navigate(1)
}
return (
<Fragment>
<h2 style={{marginLeft: "50px"}}>React Router Demo</h2>
<button onClick={forWord}>前进-》</button>
<button onClick={back}>《-后退</button>
<hr/>
</Fragment>
)
}
我们可以调用navigate这个函数,参是是-1是后退,1是前进
如果我们要传递state可以放到navigate函数的第二个参数里,别的参数形式,我们要直接拼接到url里
该hook是判断组件是否在路由的上下文中,在返回true,反之false
import React, {Fragment} from "react"
import {useLocation, useInRouterContext} from "react-router-dom"
export default function Detail() {
const {state: {id, title, content}} = useLocation()
console.log(useInRouterContext())
return (
<Fragment>
<ol>
<li>{id}</li>
<li>{title}</li>
<li>{content}</li>
</ol>
</Fragment>
)
}
该hook返回的是导航的类型为3种,POP为页面刷新,还有PUSH,REPLACE
import React, {Fragment} from "react"
import {useLocation, useNavigationType} from "react-router-dom"
export default function Detail() {
const {state: {id, title, content}} = useLocation()
console.log(useNavigationType())
return (
<Fragment>
<ol>
<li>{id}</li>
<li>{title}</li>
<li>{content}</li>
</ol>
</Fragment>
)
}
该hook是用来展现当前组件中嵌套的路由组件
import React, {Fragment, useState} from "react"
import {Navigate,Outlet,NavLink,useOutlet} from "react-router-dom"
export default function Home() {
const [sum, setSum] = useState(1)
console.log(useOutlet())
return (
<Fragment>
<h3>Home</h3>
{sum === 2 ? <Navigate to="/about"/> : <h4>当前sum为{sum}</h4>}
<button onClick={() => setSum(sum + 1)}>点我加一</button>
<NavLink to="message">message</NavLink>
<NavLink to='news'>news</NavLink>
<Outlet/>
</Fragment>
)
}
注意,若组件子路由组件没挂载就返回NULL,反之返回路由对象
该hook是帮我们解析路径的,可以分析出路径,以及各种参数
import React, {Fragment} from "react"
import {useLocation, useResolvedPath} from "react-router-dom"
export default function Detail() {
const {state: {id, title, content}} = useLocation()
console.log(useResolvedPath())
return (
<Fragment>
<ol>
<li>{id}</li>
<li>{title}</li>
<li>{content}</li>
</ol>
</Fragment>
)
}