安装:
npm install react-router-dom
react-router最主要的API是给我们提供的一些组件:BrowserRouter
或HashRouter
其中BrowserRouter
使用history
模式;
import React from "react"
import ReactDOM from "react-dom/client"
import { BrowserRouter } from "react-router-dom"
import App from "./App"
const root = ReactDOM.createRoot(document.querySelector("#root"))
root.render(
<BrowserRouter>
<App/>
</BrowserRouter>
)
HashRouter
使用hash
模式:
import React from "react"
import ReactDOM from "react-dom/client"
import { HashRouter } from "react-router-dom"
import App from "./App"
const root = ReactDOM.createRoot(document.querySelector("#root"))
root.render(
<HashRouter>
<App/>
</HashRouter>
)
在App中通过使用Routes和Route
组件指定路由的出口,path
属性指定对应路径,element
属性指定要切换的组件实例。
通过Link
组件实现路由的跳转,to
属性指定切换的路径
import React, { PureComponent } from 'react';
import Home from './pages/Home';
import Search from './pages/Search';
import Detail from './pages/Detail';
import {Routes, Route, Link} from 'react-router-dom';
export class App extends PureComponent {
render() {
return (
<div>
<div className="header">
Header<hr />
<Link to='/home'>点击切换到Home</Link><hr />
<Link to='/detail'>点击切换到detail</Link><hr />
<Link to='/search'>点击切换到search</Link><hr />
</div>
<div className="content">
中间的路由内容
<Routes>
<Route path='/home' element={<Home />} />
<Route path='/detail' element={<Detail />} />
<Route path='/search' element={<Search />} />
</Routes>
</div>
<div className="foorer">Footer</div>
</div>
)
}
}
export default App
Navigate用于路由的重定向,当这个组件出现时,就会执行跳转到对应的to路径中
比如我们举个例子,用户来到登录页:
如果没登陆就显示登录按钮;
如果点击登录按钮,就把状态设置为登录,然后就自动跳转到home页。
import React, { PureComponent } from 'react'
import { Navigate } from 'react-router-dom';
export class Login extends PureComponent {
constructor(props) {
super(props);
this.state = {
isLogin: false
}
}
login() {
this.setState({
isLogin: true
})
}
render() {
let { isLogin } = this.state;
return (
<div>
<h1>Login</h1>
{!isLogin ? <button onClick={(() => this.login())}>登录</button> : <Navigate to='/home'/>}
</div>
)
}
}
export default Login
还有就是路由的重定向问题,如果遇到地址只有一个/
,那么就导航至home页(或者其实直接写Home组件
也可以的)
<Routes>
<Route path='/' element={ to='/home' /> } />
<Route path='/home' element={ /> } />
<Route path='/detail' element={ /> } />
<Route path='/login' element={ /> } />
Routes>
此时我们可以设置path='*'
时(找不到路径时),自动路由跳转到某个组件,给用户提示
<Routes>
<Route path='/' element={ to='/home' /> } />
<Route path='/home' element={ /> } />
<Route path='/detail' element={ /> } />
<Route path='/login' element={ /> } />
<Route path='*' element={ /> } />
Routes>
在vue中我们需要去配置children这个配置项,但是在react中,不需要噢。
比如我要在Home里面搞个导航栏切换对应的内容:
然后来到App,在Home页的这个Route
标签中间写嵌套的路由,这里的第一行是一个重定向,如果进入Home页,默认导航至第一个组件Column1
:
当然啊Outlet
啊,Route
啊,Link
啊这些组件别忘了引入。
React-router给我们提供了一个hook
,名字叫useNavigate
,调用它可以返回一个函数,我们调用这个函数并把path
传给它就可以实现随时通过点击事件进行路由的跳转。
但是既然是hook,那么就只能在函数式组件中使用,如果要在类组件中使用呢?
在类组件中想要使用hook并不是不可以,我们可以通过一个高阶组件,给类组件注入这个hook,然后通过props就能调用并传参。
在做高阶组件时,返回的应该是一个函数式组件(如果是类组件不还是用不了吗),然后把useNavigate
的返回值(一个函数)注入进去
import { useNavigate } from "react-router-dom";
export default function enhanceUseNavigate(Component) {
//这里要通过函数式组件注入,因为类组件里不能用噢
return function(props) {
let navigate = useNavigate();
return <Component {...props} navigate={navigate}/>
}
}
然后另一边使用高阶组件,并且点击按钮时调用传path就行了,这样就可以实现跳转。
export class App extends PureComponent {
navigateTo(path) {
//在这里就可以调用注入的跳转函数,传入path进行路由跳转
this.props.navigate(path);
}
render() {
return (
<div>
<div className="header">
Header<hr />
<button onClick={() => this.navigateTo('/home')}>点击按钮跳转到Home</button>
<button onClick={() => this.navigateTo('/detail')}>点击按钮跳转到detail</button>
<button onClick={() => this.navigateTo('/login')}>点击按钮跳转到登录页</button>
</div>
<div className="content">
<h4>中间的路由内容:</h4>
<Routes>
<Route path='/' element={<Navigate to='/home' />} />
<Route path='/home' element={<Home />} />
<Route path='/detail' element={<Detail />} />
<Route path='/login' element={<Login />} />
<Route path='*' element={<NotFound />} />
</Routes>
</div>
<div className="foorer">Footer</div>
</div>
)
}
}
export default enhanceUseNavigate(App);
这东西本来就是为函数式组件而生的,所以在这里使用简直是妙蛙种子到了米奇妙妙屋,妙到家了。
function App() {
let navigate = useNavigate();
function navigateTo(path) {
navigate(path);
}
return (
<div>
<div className="header">
Header<hr />
<button onClick={() => this.navigateTo('/home')}>点击按钮跳转到Home</button>
<button onClick={() => this.navigateTo('/detail')}>点击按钮跳转到detail</button>
<button onClick={() => this.navigateTo('/login')}>点击按钮跳转到登录页</button>
</div>
<div className="content">
<h4>中间的路由内容:</h4>
<Routes>
<Route path='/' element={<Navigate to='/home' />} />
<Route path='/home' element={<Home />} />
<Route path='/detail' element={<Detail />} />
<Route path='/login' element={<Login />} />
<Route path='*' element={<NotFound />} />
</Routes>
</div>
<div className="foorer">Footer</div>
</div>
)
}
export default App;
类似于vue中的params参数
注意,写了占位,如果不传,那么路径就会置空
通过拼接:参数名
可以设置参数的key
,这样的话就可以把参数的value
通过Link
或编程式导航传给Detail
组件。
那么参数怎么接呢?
这里要提到另一个hook用来接收参数,useParams
,但是呢如果Detail组件是一个类组件,那么我们就要用高阶组件给它注入这个东西:
拿刚才那个高阶组件举例吧,还是同样的方法,把params注入给Detail的props。
import { useNavigate, useParams } from "react-router-dom";
export default function enhanceUseNavigate(Component) {
//这里要通过函数式组件注入,因为类组件里不能用噢
return function (props) {
let navigate = useNavigate();
let params = useParams();
let router = { navigate, params };
return <Component {...props} router={router} />
}
}
最后来到Detail组件,可以直接读取,参数传过来放到对象里,我们就能去读它的id,或者用它发请求什么的。
import React, { PureComponent } from 'react';
import enhanceUseNavigate from '../utils/enhanceUseNavigate';
export class Detail extends PureComponent {
render() {
let { params } = this.props.router;
console.log(params);//{id: '123'}
return (
<div>
<h1>Detail</h1>
<h2>收到路由跳转传来的参数:{params.id}</h2>
</div>
)
}
}
export default enhanceUseNavigate(Detail)
类似vue中的query参数
这里用到另一个hook:useSearchParams
当然在函数式组件中用会比较合适,但是这里我们在类中演示吧,还是用高阶组件,拿之前那个举例:
import { useNavigate, useParams, useSearchParams } from "react-router-dom";
export default function enhanceUseNavigate(Component) {
//这里要通过函数式组件注入,因为类组件里不能用噢
return function (props) {
// 1.导航
const naviagte = useNavigate()
// 2.动态路由的参数
const params = useParams()
// 3.查询字符串的参数
const [searchParams] = useSearchParams()
// console.log(searchParams.get('name'))
const query = Object.fromEntries(searchParams.entries())
console.log(query);
const router = { naviagte, params, query}
return <Component {...props} router={router} />
}
}
这里的写法没怎么见过,先记住吧。总之就是把传过来的url参数变成一个对象,里面存着键值对格式的参数。同样我们注入到Login
组件中,就可以接到参数了
import React, { PureComponent } from 'react'
import enhanceUseNavigate from '../utils/enhanceUseNavigate';;
export class Login extends PureComponent {
render() {
let { query } = this.props.router;
return (
<div>
<h1>Login</h1>
<h2>拿到query参数!{query.name}-{query.age}</h2>
</div>
)
}
}
export default enhanceUseNavigate(Login)
目前我们所有的路由都配置在组件中,用标签的形式写出来的,这样会比较乱,那么我们想像vue一样有自己的独立配置,react
给我们提供了这样的hook
比如我们要把下面这一堆路由关系配置成一个单独的文件
<div className="content">
<h4>中间的路由内容:h4>
<Routes>
<Route path='/' element={ to='/home' /> } />
<Route path='/home' element={ /> } >
<Route path='/home' element={ to='/home/column1' /> }>Route>
<Route path='/home/column1' element={ /> }>Route>
<Route path='/home/column2' element={ /> }>Route>
<Route path='/home/column3' element={ /> }>Route>
Route>
<Route path='/detail/:id' element={ /> } />
<Route path='/login' element={ /> } />
<Route path='*' element={ /> } />
Routes>
div>
大概就是这样的:
import Home from '../pages/Home';
import Login from '../pages/Login';
import Detail from '../pages/Detail';
import NotFound from '../pages/NotFound';
import Column1 from '../pages/Column1';
import Column2 from '../pages/Column2';
import Column3 from '../pages/Column3';
import { Navigate } from 'react-router-dom';
let routes = [
{
path:'/',
element: <Navigate to='/home'/> //重定向
},
{
path:'/home',
element: <Home/>,
children: [
{
path:'/home',
element: <Navigate to="/home/column1"/> //重定向
},
{
path:'/home/column1',
element: <Column1/>
},
{
path:'/home/column2',
element: <Column2/>
},
{
path:'/home/column3',
element: <Column3/>
}
]
},
{
path:'/detail/:id',
element: <Detail/>
},
{
path:'/login',
element: <Login/>
},
{
path: '*',
element: <NotFound/>
}
]
export default routes;
然后在App中需要导入routes,并且使用hook占位:
两步
1、换成这个
import { Suspense } from 'react';
......
root.render(
<HashRouter>
<Suspense fallback={<h3>路由懒加载加载中!!</h3>}>
<App />
</Suspense>
</HashRouter>
);
这里fallback里的东西是加载没出来的时候显示的东西