路由跳转
路由跳转的方式分为标签跳转和编程式跳转。
标签跳转
使用NavLink
组件或者Link
组件,定义路由组件跳转。
Link
组件
Link
组件中最重要的属性to
,用于设置跳转到的路径。
js
// main.jsx
import { createRoot } from 'react-dom/client' // 用于渲染页面的
import React from 'react' // 用于创建组件的
import { BrowserRouter as Router } from 'react-router-dom' // 引入路由
import App from './App.jsx'
createRoot(document.getElementById('root')).render(
<Router>
<App />
</Router>
)
js
// App.jsx
import React from "react"
import { Routes, Route, Link } from "react-router-dom"
import Home from "./Home.jsx"
import Shop from "./Shop.jsx"
export default function App() {
return (
<>
<div>
<Link to='/home'>首页</Link>
<Link to='/shop'>商品</Link>
</div>
<Routes>
<Route path="/home" element={<Home></Home>}></Route>
<Route path="/shop" element={<Shop></Shop>}></Route>
</Routes>
</>
)
}
NavLink
组件
NavLink
组件会在属性to
匹配成功的那个NavLink
组件上添加一个动态的active
类名,我们可以根据这个类名设置一些样式。
css
/* App.css */
.active {
color: red;
}
js
// App.jsx
import React from "react"
import "./App.css"
import { Routes, Route, NavLink } from "react-router-dom"
import Home from "./Home.jsx"
import Shop from "./Shop.jsx"
export default function App() {
return (
<>
<div>
<NavLink to='/home'>首页</NavLink>
<NavLink to='/shop'>商品</NavLink>
</div>
<Routes>
<Route path="/home" element={<Home></Home>}></Route>
<Route path="/shop" element={<Shop></Shop>}></Route>
</Routes>
</>
)
}
NavLink
组件和Link
组件相当于超连接,它和a
标签的区别是,NavLink
组件和Link
组件不会刷新整个网站,只是切换路由页面渲染不同组件。而a
标签会再次重新请求整个网站然后刷新网页,a
标签多用于和外部网站建立链接。
js
// App.jsx
import React from "react"
import { Routes, Route, Link } from "react-router-dom"
import Home from "./Home.jsx"
import Shop from "./Shop.jsx"
export default function App() {
return (
<>
<Link to="/home">首页</Link>
<a href="/shop">商品</a>
<Routes>
<Route path="/home" element={<Home></Home>}></Route>
<Route path="/shop" element={<Shop></Shop>}></Route>
</Routes>
</>
)
}
上面动图可以清晰看到点击
a
标签进行跳转时,浏览器的刷新图标运行了让页面重新刷新了。而Link
组件并没有让浏览器的刷新图标运行。
编程式跳转
使用useNavigate
hook进行编程式跳转,useNavigate
返回一个 navigate
函数,可以用来进行路由跳转和监听路由变化。
navigate
函数接受两个参数:
to
(字符串或数字):要跳转的目标路径或者历史记录栈中的位置。如果提供的是一个字符串,那么它会被当作一个新的路径;如果是数字,则表示在历史记录栈中移动多少步,例如-1
表示回退上一步。options
(对象):可选参数,包含以下属性:replace
(布尔值,默认为 false): 如果设置为 true,则新的位置会替换当前的历史记录条目而不是添加新的条目。state
(任意类型):可以用来传递状态信息到目标页面。这个状态可以通过useLocation
Hook 在目标页面获取。
字符串路径跳转
js
// App.jsx
import React from "react"
import { Routes, Route, useNavigate } from "react-router-dom"
import Home from "./Home.jsx"
import Shop from "./Shop.jsx"
export default function App() {
const navigate = useNavigate();
const goToPage = (val) => {
navigate(`${val}`)
}
return (
<>
<div>
<button onClick={() => {goToPage('home')}}>首页</button>
<button onClick={() => {goToPage('shop')}}>商品</button>
</div>
<Routes>
<Route path="/home" element={<Home></Home>}></Route>
<Route path="/shop" element={<Shop></Shop>}></Route>
</Routes>
</>
)
}
带状态的跳转
假设有两个页面:Home 和 Shop。从 Home 页面跳转到 Shop 页面时,需要传递一些商品信息。
js
// App.jsx
import React from "react"
import { Routes, Route, useNavigate } from "react-router-dom"
import Home from "./Home.jsx"
import Shop from "./Shop.jsx"
export default function App() {
const navigate = useNavigate();
const goToPage = (val) => {
navigate(`${val}`)
}
return (
<>
<div>
<button onClick={() => {goToPage('home')}}>首页</button>
<button onClick={() => {goToPage('shop')}}>商品</button>
</div>
<Routes>
<Route path="/home" element={<Home></Home>}></Route>
<Route path="/shop" element={<Shop></Shop>}></Route>
</Routes>
</>
)
}
在 Home 组件中,使用 useNavigate
进行带状态的路由跳转:
js
// Home.jsx
import React from "react";
import { useNavigate } from 'react-router-dom'
const Home = (props) => {
const navigate = useNavigate();
const handleNavigate = () => {
const shop = {
type: '专辑',
name: '启示录',
describe: '《启示录》是邓紫棋于2022年8月9日发行的音乐专辑,共收录14首歌曲,由邓紫棋担任制作人 。2022年,该专辑获得微博视界大会突破创新音乐连续剧奖 、亚洲流行音乐大奖年度最佳专辑奖。2023年,邓紫棋凭借该专辑获得浪潮音乐大赏最佳女歌手奖、年度专辑奖'
};
navigate('/shop', { state: { shop } });
};
return (
<>
<h1>首页</h1>
<p>这是首页的数据</p>
<button onClick={handleNavigate}>前往商品页查看详情</button>
</>
)
}
export default Home
Home 组件:
- 导入
useNavigate
并在组件内部调用它来获取navigate
函数。 - 在
handleNavigate
函数中,创建一个shop
对象并将其作为状态传递给navigate
函数。 - 当按钮被点击时,触发
handleNavigate
函数,进行路由跳转。
在 Shop 组件中,使用 useLocation
获取传递的状态信息:
js
// Son.jsx
import React from "react"
import { useLocation } from 'react-router-dom';
const Shop = (props) => {
const location = useLocation();
const { shop } = location.state || {};
return (
<>
<h1>商品</h1>
<p>这是商品页的数据</p>
{shop ? (
<div>
<p>商品种类: {shop.type}</p>
<p>商品名称: {shop.name}</p>
<p>商品信息: {shop.describe}</p>
</div>
) : (
<p>暂时没有商品数据</p>
)}
</>
)
}
export default Shop
Shop 组件:
- 导入
useLocation
并在组件内部调用它来获取当前路由的位置信息。 - 从
location.state
中提取传递的shop
对象。 - 如果
shop
存在,则显示商品的详细信息;否则显示提示信息。
回退至上一页
js
navigate(-1);
替换当前历史记录条目
js
navigate('/shop', { replace: true });
当在一个网站中使用导航时,浏览器会记录访问过的每个页面。这些记录组成了一个历史记录栈。每次点击链接或通过编程方式跳转到一个新的页面时,浏览器会在历史记录栈中添加一个新的条目。这样,当点击浏览器的"后退"按钮时,浏览器会回到上一个记录的位置。
替换当前历史记录条目:
默认情况下,navigate
函数会在历史记录栈中添加一个新的条目。这意味着如果从页面 A 跳转到页面 B,然后再从页面 B 跳转到页面 C,历史记录栈会是这样的:
- 页面 A
- 页面 B
- 页面 C
如果现在点击浏览器的"后退"按钮,就会从页面 C 回到页面 B。
但是,如果设置了 replace: true
,那么浏览器不会添加新的历史记录条目,而是会替换当前的历史记录条目。这意味着如果从页面 A 跳转到页面 B,然后从页面 B 跳转到页面 C 且从页面 B跳转到页面 C时设置了 replace: true
,历史记录栈会是这样的:
- 页面 A
- 页面 C
在这种情况下,如果点击浏览器的"后退"按钮,你会直接从页面 C 回到页面 A,而不会经过页面 B。因为页面 B在跳转到页面 C时,历史记录栈的页面B被替换成了页面C。
路由参数
有三种常见的方法可以实现路由之间的参数传递:
search
传参:查询字符串参数params
传参:动态路由参数state
传参:状态传递参数
查询字符串参数以及获取参数方法
查询字符串参数适用于需要在URL中传递额外的、可选的参数。它是将参数拼接在网址的querystring
中。
优点:刷新页面后参数不丢失
缺点:只能传字符串,传值过多URL会变得很长,获取参数需要自己解析字符串。
示例
假设在Home页面进行搜索然后在Shop页面显示搜索的商品信息,URL格式为 /search?query=启示录
。
js
// App.jsx
import React from "react"
import { Routes, Route, useNavigate } from "react-router-dom"
import Home from "./Home.jsx"
import Shop from "./Shop.jsx"
export default function App() {
const navigate = useNavigate();
const goToPage = (val) => {
navigate(`${val}`)
}
return (
<>
<div>
<button onClick={() => {goToPage('home')}}>首页</button>
<button onClick={() => {goToPage('shop')}}>商品</button>
</div>
<Routes>
<Route path="/home" element={<Home></Home>}></Route>
<Route path="/shop" element={<Shop></Shop>}></Route>
</Routes>
</>
)
}
在Home组件路由跳转进行传参:
js
// Home.jsx
import React, { useState } from "react";
import { useNavigate } from 'react-router-dom'
const Home = (props) => {
const navigate = useNavigate();
let [query, setQuery] = useState("");
const handleSubmit = (e) => {
// 阻止表单提交按钮的默认事件
e.preventDefault();
navigate(`/shop?query=${query}`);
// navigate({ pathname: "/shop", search: `query=${query}` });
};
return (
<>
<h1>首页</h1>
<p>这是首页的数据</p>
<form onSubmit={handleSubmit}>
<input
type="text"
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="请输入关键字"
/>
<button type="submit">搜索</button>
</form>
</>
)
}
export default Home
在Shop组件使用 useLocation
获取传递的参数:
js
// Shop.jsx
import React from "react"
import { useLocation } from 'react-router-dom';
const Shop = (props) => {
// 接受search参数
const location = useLocation();
console.log(location, "location");
// 需要手动解析search参数
const { search } = location;
const shop = {
type: '专辑',
name: '启示录',
describe: '《启示录》是邓紫棋于2022年8月9日发行的音乐专辑,共收录14首歌曲,由邓紫棋担任制作人 。2022年,该专辑获得微博视界大会突破创新音乐连续剧奖 、亚洲流行音乐大奖年度最佳专辑奖。2023年,邓紫棋凭借该专辑获得浪潮音乐大赏最佳女歌手奖、年度专辑奖'
};
return (
<>
<h1>商品</h1>
<p>这是商品页的数据</p>
{search ? (
<div>
<p>商品种类: {shop.type}</p>
<p>商品名称: {shop.name}</p>
<p>商品信息: {shop.describe}</p>
</div>
) : (
<p>暂时没有商品数据</p>
)}
</>
)
}
export default Shop
loaction
对象的返回值如下:
注意:当使用对象用
pathname
跳转传参时不能在pathname
后面直接拼接参数会报错,必须使用search
属性且属性值只能是字符串,当传参需要变量动态取值只能使用反引号的模板字符串,不能是对象。
js
navgite({pathname:'/shop?query=89'}); // 错误写法
navgite({pathname:'/shop',search:'gid=89'}); // 正确写法
navgite('/shop?gid=89'); // 正确写法
let gid=89;
navgite(`/shop?gid=${gid}`); // 正确写法
navigate({ pathname: "/shop", search: `gid=${gid}` }); // 正确写法
动态路由参数以及参数获取方法
动态路由参数适用于需要在URL中传递参数的情况,它是将参数拼接在网址的pathname
中。
优点:刷新页面,参数不丢失
缺点:只能传字符串,传值过多URL会变得很长;参数必须在路由上配置
js
// 1.设置可传参数
<Route path='/center/:name/:uid' element={<Suspense><Usercenter /></Suspense>}></Route>
// 2.传参
navigate({pathname:`/center/utc_20090729/6`});
或
<Link to={{pathname:`/center/utc_19890530/7`}}>跳转到个人中心</Link>
// 3.接收参数
import {useParams} from 'react-router-dom';
let person = useParams();
console.log(person.name,person.uid)
示例
假设在Home组件中点击按钮传入商品的id跳转到商品详情页面,URL格式为 /shop/:id
,其中 :id
是动态部分。
设置可传参数:
js
// App.jsx
import React from "react"
import { Routes, Route, useNavigate } from "react-router-dom"
import Home from "./Home.jsx"
import Shop from "./Shop.jsx"
export default function App() {
const navigate = useNavigate();
const goToPage = (val) => {
navigate(`${val}`)
}
return (
<>
<div>
<button onClick={() => {goToPage('home')}}>首页</button>
</div>
<Routes>
<Route path="/home" element={<Home></Home>}></Route>
{/*设置可传参数*/}
<Route path="/shop/:id" element={<Shop></Shop>}></Route>
</Routes>
</>
)
}
在Home组件中传参:
js
import React from "react";
import { useNavigate } from 'react-router-dom'
const Home = (props) => {
const navigate = useNavigate();
const shops = [
{ id: 1, name: '启示录' },
{ id: 2, name: '摩天动物园' },
{ id: 3, name: '新的心跳' }
];
const goToDetail = (val) => {
navigate({ pathname: `/shop/${val}` })
}
return (
<>
<h1>首页</h1>
<p>这是首页的数据</p>
{shops.map(item => {
return (
<div key={item.id} onClick={() => {goToDetail(item.id)}}>{item.name}</div>
)
})}
</>
)
}
export default Home
在Shop组件中使用 useParams
接受参数:
js
import React from "react"
import { useParams } from 'react-router-dom';
const Shop = (props) => {
// 接受params参数
const params = useParams();
console.log(params, "params");
// 需要手动解析search参数
const { id } = params;
let shop = {}
// 模拟请求数据
if (id == 1) {
shop = {
type: '专辑',
name: '启示录',
describe: '《启示录》是邓紫棋于2022年8月9日发行的音乐专辑,共收录14首歌曲,由邓紫棋担任制作人 。2022年,该专辑获得微博视界大会突破创新音乐连续剧奖 、亚洲流行音乐大奖年度最佳专辑奖。2023年,邓紫棋凭借该专辑获得浪潮音乐大赏最佳女歌手奖、年度专辑奖'
};
} else if (id == 2) {
shop = {
type: '专辑',
name: '摩天动物园',
describe: '《摩天动物园》是邓紫棋于2019年12月27日发行的音乐专辑,共收录13首歌曲,由邓紫棋担任制作人。2020年,该专辑获得第31届台湾金曲奖"评审团奖"、第43届十大中文金曲"最佳中文唱片奖" 、2020hito流行音乐奖"亚洲传媒推崇大奖" 、2019年度流行音乐全金榜"年度最佳专辑奖"'
};
} else if (id == 3) {
shop = {
type: '专辑',
name: '新的心跳',
describe: '新的心跳》是邓紫棋于2015年11月6日发行的音乐专辑,共收录10首歌曲。2016年,邓紫棋凭该专辑获得第6届全球流行音乐金榜"年度最佳女歌手奖" ;该专辑则获得第10届音乐盛典咪咕汇"年度最佳国语专辑奖"、IFPI香港唱片销量大奖"全年最高销量国语唱片奖",并提名第27届台湾金曲奖"最佳演唱录音专辑奖"。'
};
}
return (
<>
<h1>商品</h1>
<p>这是商品页的数据</p>
<div>
<p>商品种类: {shop.type}</p>
<p>商品名称: {shop.name}</p>
<p>商品信息: {shop.describe}</p>
</div>
</>
)
}
export default Shop
状态传参
state
状态传参适用于不需要在URL中显示参数的情况,可以通过 useNavigate
的 state
属性传递。
优点:可以传对象,参数不会拼接在网址中,可以用于传不用给用户看见的参数
缺点: <HashRouter>
刷新页面,参数丢失
官方建议使用<BrowserRouter>
,<BrowserRouter>
页面刷新参数也不会丢失。
示例
假设有一个登录页面,登录成功后跳转到首页并传递用户信息。
js
// App.jsx
import React from "react"
import { Routes, Route } from "react-router-dom"
import Login from "./Login.jsx"
import Home from "./Home.jsx"
export default function App() {
return (
<>
<Routes>
<Route path="/" element={<Login />} />
<Route path="/home" element={<Home></Home>}></Route>
</Routes>
</>
)
}
Login组件传参:
js
// Login.jsx
import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
function Login() {
const [username, setUsername] = useState('');
const navigate = useNavigate();
const handleSubmit = (e) => {
e.preventDefault();
const user = { username };
navigate('/home', { state: { user } });
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={username}
onChange={(e) => setUsername(e.target.value)}
placeholder="Username"
/>
<button type="submit">Login</button>
</form>
);
}
export default Login;
在Home组件使用useLocation
接收参数:
js
// Home.jsx
import React from 'react';
import { useLocation, useNavigate } from 'react-router-dom';
function Home() {
const location = useLocation();
const { user } = location.state || {};
console.log(location,"location")
const navigate = useNavigate();
const goToPage = (val) => {
navigate('/')
}
return (
<div>
<div>
<button onClick={() => { goToPage('home') }}>前往登录</button>
</div>
<h1>Home 页面</h1>
{user ? (
<p>Welcome, {user.username}!</p>
) : (
<p>请登录</p>
)}
</div>
);
}
export default Home;