安装
pnpm add react-router-dom@6
或
yarn add react-router-dom@6
引入
//index.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import {
BrowserRouter as Router
} from 'react-router-dom'
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<React.StrictMode>
<Router>
<App />
</Router>
</React.StrictMode>
);
reportWebVitals();
//App.tsx
import React, {Suspense, lazy, ComponentType} from 'react';
import {
Routes,
Route,
} from 'react-router-dom'
//着急加载的模块直接加载
import Header from "./components/Header";
import Footer from "./components/Footer";
import Loading from "./components/Loading";
//不着急加载的模块使用懒加载
const Home = lazy((): Promise<{ default: ComponentType<any> }> => {
return import('./pages/Home')
})
const About = lazy((): Promise<{ default: ComponentType<any> }> => {
return import('./pages/About')
})
const History = lazy((): Promise<{ default: ComponentType<any> }> => {
return import('./pages/History')
})
const App: React.FC<{}> = () => {
return (
<React.Fragment>
<Header/>
<Suspense fallback={<Loading/>}> //加载时会使用Loading组件加载后会被替换成真正的路由组件,用于做页面的loading状态
<main id='main'>
<Routes>
<Route path="/" element={<Home/>}/>
<Route path="/history" element={<History/>}/>
<Route path="/about" element={<About/>}/>
</Routes>
</main>
</Suspense>
<Footer/>
</React.Fragment>
);
}
export default App;
页面跳转
- NavLink和Link的区别在于NavLink在当前路由时会加上一个active的类
- NavLink组件的style或className可以接收一个函数,函数接收一个isActive参数,可根据该参数调整样式
1.标签模式的跳转(NavLink,Link)
//Header.tsx
import { NavLink } from 'react-router-dom';
function Foo(){
return (
<NavLink
style={ (isActive) => ({color: isActive ? 'red' : '#fff'}) }
>Click here</NavLink>
)
}
2.编程模式的跳转(useNavigate )
import React from "react";
import { useNavigate } from "react-router-dom";
interface Props {
}
const Home: React.FC<Props> = () => {
const navigate = useNavigate();
const redirect=()=>{
navigate("/about")
}
return (
<React.Fragment>
<button onClick={redirect}>跳转</button>
</React.Fragment>
)
}
获取路由参数 (useParams)
<Routes>
<Route path="/test/:id" element={<Test/>}/>
</Routes>
import React from "react";
import {useParams} from 'react-router-dom'
interface Props{
}
const Test:React.FC<Props> =()=>{
const params = useParams();
return(
<div>{params.id}</div>
)
}
export default Test
获取查询参数
eg: /login?name=lgp
import React from "react";
import {useParams,NavLink, Outlet,useSearchParams,} from 'react-router-dom'
interface Props {
}
const Test: React.FC<Props> = () => {
const params=useParams()
const [searchParams, setSearchParams] = useSearchParams()
if(searchParams.get("name")) console.log(searchParams.get("name")) //获取查询参数
return (
<>
<nav>
<NavLink to="/test/login?name=lgp" style={{marginRight:'30px'}}>login</NavLink>
<NavLink to="/test/register" style={{marginRight:'30px'}}>register</NavLink>
<NavLink to="/test/xxx">params</NavLink>
</nav>
<div>
<Outlet/>
</div>
<div>
{params.id?params.id:"yyy"}
</div>
</>
)
}
export default Test
嵌套路由
//App.tsx
<Route path="/test" element={<Test/>}>
<Route index element={<Register/>}/> //索引路由 即在/test时,子路由将呈现Register组件
<Route path="login" element={<Login/>}/>
<Route path="register" element={<Register/>}/>
<Route path=":id" element={<Loading/>}/>
</Route>
这将匹配下面三个路由
- “/test/login”
- “/test/register”
- “/test/:id”
父组件需要用( <Outlet />
)标签呈现子组件
import React from "react";
import {useParams,NavLink, Outlet} from 'react-router-dom'
interface Props {
}
const Test: React.FC<Props> = () => {
const params=useParams()
return (
<>
<nav>
<NavLink to="/test/login" style={{marginRight:'30px'}}>login</NavLink>
<NavLink to="/test/register" style={{marginRight:'30px'}}>register</NavLink>
<NavLink to="/test/xxx">params</NavLink>
</nav>
<div>
<Outlet/> //将被子路由组件替换
</div>
<div>
{params.id?params.id:"yyy"} //获取路由路径参数
</div>
</>
)
}
export default Test
索引路由
您可以在路由层次结构的任何级别拥有索引路由,该路由将在父级匹配时呈现,但其他子级都不匹配。
<Route path="/test" element={<Test/>}>
<Route index element={<Register/>}/> //索引路由 即在/test时,子路由将呈现Register组件
<Route path="login" element={<Login/>}/>
<Route path="register" element={<Register/>}/>
<Route path=":id" element={<Loading/>}/>
</Route>
404路由
当没有其他路由与 URL 匹配时,可以使用 呈现“未找到”路由。
<Route path="*" element={<NotFound />} />
<Routes>
<Route path="/" element={<Home />} />
<Route path="dashboard" element={<Dashboard />} />
<Route path="*" element={<NotFound />} /> //放在最后做兜底
</Routes>
布局路由
<Routes>
<Route path="/" element={<App />}>
<Route index element={<Home />} />
<Route element={<PageLayout />}> //布局路由不参与匹配
<Route path="/privacy" element={<Privacy />} />
<Route path="/tos" element={<Tos />} />
</Route>
<Route path="contact-us" element={<Contact />} />
</Routes>
渲染结果
/privacy
<App>
<PageLayout>
<Privacy />
</PageLayout>
</App>
/tos
<App>
<PageLayout>
<Tos />
</PageLayout>
</App>
也可以被其他方式代替(不建议)
<Route
path="/privacy"
element={
<PageLayout>
<Privacy />
</PageLayout>
}
/>
<Route
path="/tos"
element={
<PageLayout>
<Tos />
</PageLayout>
}
/>
路由器模式
BrowserRouter
是在 Web 浏览器中运行 React Router 的推荐接口,使用干净的 URL 将当前位置存储在浏览器的地址栏中,并使用浏览器的内置历史记录堆栈进行导航。
import * as React from "react";
import * as ReactDOM from "react-dom";
import App from './App'
import { BrowserRouter } from "react-router-dom";
ReactDOM.render(
<BrowserRouter>
<App/>
</BrowserRouter>,
root
);
HashRouter
用于 Web 浏览器,当 URL 由于某种原因不应(或不能)发送到服务器时。在某些共享宿主方案中,您可能会完全控制服务器,这可能会发生这种情况。在这些情况下,可以将当前位置存储在当前 URL 的部分,因此永远不会将其发送到服务器。
import { HashRouter } from "react-router-dom";
//同上
HistoryRouter
将历史库的实例作为 prop。这允许您在非 React 上下文中使用该实例或将其用作全局变量。
import * as React from "react";
import * as ReactDOM from "react-dom";
import { unstable_HistoryRouter as HistoryRouter } from "react-router-dom";
import { createBrowserHistory } from "history";
const history = createBrowserHistory({ window });
ReactDOM.render(
<HistoryRouter history={history}>
{/* The rest of your app goes here */}
</HistoryRouter>,
root
);
MemoryRouter
不建议使用
NativeRouter
是在 React Native 应用程序中运行 React Router 的推荐接口
import * as React from "react";
import { NativeRouter } from "react-router-native";
function App() {
return (
<NativeRouter>
{/* The rest of your app goes here */}
</NativeRouter>
);
}
`编程式路由导航用useNavigate代替useHistory
go goBack和
goForward`
import { useNavigate } from 'react-router-dom'
const app=()=>{
const navigate = useNavigate()
function handleClick() {
history.push('/home');
//navigate('/home', {replace: true});
};
return (
<button onClick={handleClick}>Submit</button>
<button onClick={() => navigate(-2)}>
Go 2 pages back
</button>
<button onClick={() => navigate(-1)}>Go back</button>
<button onClick={() => navigate(1)}>
Go forward
</button>
<button onClick={() => navigate(2)}>
Go 2 pages forward
</button>
)
}
重定向
const location = useLocation();
if (!isLogin) {
return <Navigate to="/login" state={{from: location}} replace/>;
}
给路由添加进度条
pnpm add nprogress
pnpm add @types/nprogress -D
路由我们使用懒加载的方式加载
//App.tsx
import React, {Suspense, lazy, ComponentType} from 'react';
import Loading from "./components/Loading";
import {
Routes,
Route,
} from 'react-router-dom'
const Home = lazy((): Promise<{ default: ComponentType<any> }> => {
return import('./pages/Home')
})
const About = lazy((): Promise<{ default: ComponentType<any> }> => {
return import('./pages/About')
})
const History = lazy((): Promise<{ default: ComponentType<any> }> => {
return import('./pages/History')
})
const NotFound = lazy((): Promise<{ default: ComponentType<any> }> => {
return import('./pages/NotFound')
})
const App: React.FC<{}> = () => {
return (
<React.Fragment>
<Header/>
<Suspense fallback={<Loading/>}> //这里我们使用loading组件
<main id='main'>
<Routes>
<Route path="/" element={<Home/>}/>
<Route path="/history" element={<History/>}/>
<Route path="/about" element={<About/>}/>
<Route path="*" element={<NotFound/>}/>
</Routes>
</main>
</Suspense>
<Footer/>
</React.Fragment>
);
}
export default App;
loading组件
import React, {useEffect} from "react";
import nprogress from "nprogress";
import "nprogress/nprogress.css";
//配置
nprogress.configure({
easing: 'ease', // 动画方式
speed: 500, // 递增进度条的速度
showSpinner: false, // 是否显示加载ico
trickleSpeed: 200, // 自动递增间隔
minimum: 0.3, // 初始化时的最小百分比
});
const Loading:React.FC<{}>=()=>{
//组件挂在时执行nprogress.start()
useEffect(() => {
nprogress.start()
}, [])
//组件消亡时执行 nprogress.done()
useEffect(() => {
return () => {
nprogress.done()
}
})
return (
<React.Fragment />
)
}
export default Loading
自定义导航条的背景颜色,只需要修改下面的元素的背景颜色就行
注意:修改的css
文件引入要在nprogress.css
文件后面否则不能覆盖样式失效
#nprogress .bar{
background-color: #df4402;
}
useSubmit
如果您希望某人在一段时间不活动后自动从您的网站注销,这也很有用。在本例中,我们将不活动定义为用户在 5 分钟后没有导航到任何其他页面。
import { useSubmit, useLocation } from "react-router-dom";
import { useEffect } from "react";
function AdminPage() {
useSessionTimeout();
return <div>{/* ... */}</div>;
}
function useSessionTimeout() {
const submit = useSubmit();
const location = useLocation();
useEffect(() => {
const timer = setTimeout(() => {
submit(null, { method: "post", action: "/logout" });
}, 5 * 60_000);
return () => clearTimeout(timer);
}, [submit, location]);
}