0.前端路由
前端路由可以实现让用户点击链接或输入 URL时,加载不同的视图或页面。并且仍然保持在同一个网站中,不需要重新加载整个页面。这提供了更流畅和响应迅速的用户体验。
因此可以将前端路由理解为一个工具,它可以在单一页面应用中导航到不同的视图,并且不会进行整体页面的刷新。
1.Router基本使用
1.安装React Router
npm install react-router-dom
2.引入并配置路由模式
在应用程序的入口文件(通常是 index.js )中,引入 React Router 组件。
可以使用 BrowserRouter
或 HashRouter
包裹应用程序,并在其中定义路由规则。
两者区别在于
- BrowserRouter:使用真实的 URL(例如 example.com/path)
- HashRouter:使用带有哈希部分的 URL(例如 example.com/#/path)
javascript
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import { HashRouter } from "react-router-dom"
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<HashRouter>
<App />
</HashRouter>
);
3.路由映射配置
- Routes是一个包裹所有Route的组件,它可以进行路由匹配。
- Route用于路径的匹配
path属性
:设置匹配路由
element属性
:设置匹配成功后渲染的组件
xml
<Routes>
<Route path="/" element={<Navigate to="/home" />} />
<Route path="/home" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
4.创建路由链接进行跳转
可以使用 Link 或 NavLink 组件来创建链接,这些链接将导航到不同的路由,文章后续还会提供其余跳转方法。
如下使用Link组件创建了跳转到首页以及关于页面的链接。
javascript
export function App(props) {
return (
<div className="app">
<div className="nav">
<Link to="/home">首页</Link>
<Link to="/about">关于</Link>
</div>
</div>
);
}
export default App;
配置成功之后点击不同的Link链接即可进行跳转(页面实际渲染内容更复杂一些,第1部分只为让读者对router使用有一个清晰的认识)
2.NavLink与Link
NavLink 和 Link 是 React Router 库中的两个组件,用于处理单页面应用程序(SPA)中的导航。
它们的主要区别在于 NavLink 在当前页面的 URL 与导航链接匹配时,会默认添加active
类名。
Link
Link 组件用于在单页面应用中进行导航。它不会添加任何额外的样式或类名到链接元素,仅仅提供了点击后进行导航的功能。在这个例子中,当用户点击链接时,应用程序会导航到 /path
路由。
javascript
import { Link } from 'react-router-dom';
function MyComponent() {
return (
<div>
<Link to="/path">点击我进行导航</Link>
</div>
);
}
NavLink
NavLink 是 React Router 库中的一个组件,它用于在单页面应用程序中实现导航链接,并在当前路由与导航链接匹配时添加活动类名,默认会给链接添加active
类名。
xml
<div className="navlink">
<span>NavLink组:</span>
<NavLink to="/home">首页</NavLink>
<NavLink to="/about">关于</NavLink>
</div>
可以看到当点击首页
时,自动为首页对应的a标签添加了active
所以可以通过为active类添加CSS样式来修饰当前项。
如果担心active
类名在其他地方已经用过,也可以直接为a链接
添加样式属性,或者自定义类名。
首先可以直接通过为style
传入函数的方式,来编写CSS样式。其中isActive是解构出的一个属性。如下案例的含义是如果被点击,就将字体颜色改为橙色。
ini
<NavLink
to="/home"
style={({ isActive }) => ({ color: isActive ? "orange" : "" })}
>
首页
</NavLink>
<NavLink
to="/about"
style={({ isActive }) => ({ color: isActive ? "orange" : "" })}
>
关于
</NavLink>
通过为className
传入函数的方式,来添加自定义类名。
下列案例可以实现点击某一项时,为其添加类名link-avtive
ini
<NavLink
to="/home"
className={({ isActive }) => (isActive ? "link-active" : "")}
>
首页
</NavLink>
<NavLink
to="/about"
className={({ isActive }) => (isActive ? "link-active" : "")}
>
关于
</NavLink>
3.编程式导航
编程式导航指的是通过编写代码来实现页面之间的导航,而不是依赖用户的直接交互
Navigate
Navigate 是一个用于执行编程式导航的组件。与 Link 或 NavLink 不同,Navigate 不是一个渲染链接的组件,而是一个用于在 React 组件内部执行导航的组件。
例如在编写Routes
时,如果用户访问根路径/
或者未匹配时,则将用户定向到Home组件的页面。
xml
{/* 配置映射关系, path - Component */}
<Routes>
{/* 重定向到 /home 路由 */}
<Route path="/" element={<Navigate to="/home" />} />
<Route path="/about" element={<About />} />
<Route path="/user" element={<User />} />
<Route path="*" element={<Navigate to="/home" />} />
</Routes>
useNavigate
在 React Router v6 中,useNavigate
是一个 React hook,用于获取导航函数,允许你在组件内部执行编程式导航。
并且这种用法的好处在于,可以为button
等元素也监听导航事件,而不仅仅只用Link和NavLink进行路由跳转。
scss
const navigate = useNavigate();
function navigateTo(path) {
navigate(path);
}
{/* 通过useNavigate实现js代码路由跳转 */}
<button onClick={(e) => navigateTo("/category")}>分类</button>
<span onClick={(e) => navigateTo("/order")}>订单</span>
高阶组件withRouter
在函数组件中,可以直接使用 useNavigate
hook,但在类组件中,hook 是无法直接使用的。这时,你可以通过高阶组件
将 useNavigate
引入到类组件中。
首先编写高阶组件,我们将 useNavigate
生成的导航函数作为 router
属性传递给需要使用hook函数的类组件。
javascript
// 高阶组件 withRouter: 用于在类组件中使用react-router的hook函数
import { useNavigate, useParams, useLocation, useSearchParams } from "react-router-dom"
function withRouter(WrapperComponent) {
return function(props) {
const navigate = useNavigate()
// 包装要传递给原始组件的参数
const router = { navigate }
return <WrapperComponent {...props} router={router} />
}
}
export default withRouter
将类组件应用高阶组件withRouter
后,就可以在类组件中通过 this.props.router.navigate
来执行编程式导航。
javascript
navigateTo(path) {
const { navigate } = this.props.router;
navigate(path);
}
<button onClick={(e) => this.navigateTo("/home/songmenu")}>
歌单
</button>
4.路由参数传递
动态路由
动态路由用于处理可变的URL路径,如用户个人资料页,其中URL的某些部分是动态的。如下面例子,需要根据不同的用户userId,加载不同的页面
arduino
https://zh.weihy.org/users/{userId}
这种情况可以在路由配置中,使用元素的path
属性来定义动态路由。动态部分通常用冒号(:)开头,后跟参数名称
:userId
是一个动态参数,它将匹配URL中的实际用户ID。但无论访问/users/123
还是/users/124
,都匹配到User组件。
ini
<Route path="/users/:userId" element={<User/>} />
接下来讨论如何获取路由参数,useParams
是 React Router 库中提供的一个 hook,用于在函数组件中获取路由参数。当路由包含动态路径参数(例如 /users/:id),你可以使用 useParams
来提取这些参数的值。以下是 useParams 的基本用法:
javascript
import React, { memo } from "react";
import { useParams } from "react-router-dom";
const Users = memo(() => {
const params = useParams();
console.log("params", params);
return (
<div>
<h2>id: {params.id}</h2>
</div>
);
});
export default Users;
当输入路径/users/133
时,会log以下信息,并进行页面渲染,得到具体的动态路由参数id
查询字符串
查询字符串(Query String)是一个包含在 URL 中的一组键值对(key-value pairs),用于向服务器传递参数。查询字符串通常出现在 URL 的问号 ?
之后,多个键值对之间使用 &
分隔。例如,在以下的 URL 中:
ini
https://example.com/userpage?name=John&age=30
查询字符串是 name=John&age=30
。在这个查询字符串中,name 是键,John 是对应的值;age 是另一个键,30 是它的值。
查询字符串常用于网页的 GET 请求,通过将参数附加到 URL 中,将这些参数传递给服务器。
通常使用React提供的hook useSearchParams
来处理查询字符串
useSearchParams
是 React Router 库提供的一个 hook,用于获取和修改查询字符串参数。
如果在浏览器输入url:http://localhost:3000/#/userinfo/?id=3&name='麦子'
那么就会获取到查询字符串中的具体参数并进行展示。
同时可以通过Object.fromEntries
来将searchParams
转换为一个普通的对象,其中包含查询参数:
javascript
import React, { memo } from "react";
import { useSearchParams } from "react-router-dom";
const UserInfo = memo(() => {
const [searchParams] = useSearchParams();
const id = searchParams.get("id");
const name = searchParams.get("name");
console.log("searchParams", searchParams);
const queryObj = Object.fromEntries(searchParams);
console.log("queryObj", queryObj);
return (
<div>
<h2>
用户信息 id:{id} | name:{name}
</h2>
</div>
);
});
export default UserInfo;
5.路由配置
useRoutes
允许以JavaScript 对象的形式配置路由,而不是通过声明式的 JSX。该JS对象具有与Routes
元素相同的属性。
1.创建路由配置对象
配置对象的写法与编写Route元素的方式类似,只是将对应的path
以及element
属性换了一种编写方式,改写成为js对象的方式。
JSX方式编写路由配置
并且注意,路由配置也支持嵌套路由的写法。
javascript
const routes = [
{
path: "/",
element: <Navigate to="/home"/>
},
{
path: "/home",
element: <Home/>,
children: [
{
path: "/home",
element: <Navigate to="/home/recommend" />
},
{
path: "/home/recommend",
element: <HomeRecommends/>
},
]
},
{
path: "/about",
element: <About/>
},
{
path: "/category",
element: <Category/>
},
]
export default routes
2.应用useRoutes
调用 useRoutes
函数并传入你的路由配置对象
javascript
import React from "react";
import { useRoutes } from "react-router-dom";
import routes from "./router";
export function App(props) {
return (
<div className="content">
{useRoutes(routes)}
</div>
);
}
export default App;
6.路由懒加载
懒加载是一种技术,它使得应用程序在需要时才加载特定的组件,而不是在初始加载时一次性加载所有内容。通过结合使用 React.lazy
和 Suspense
实现。
1.创建懒加载的组件
React.lazy
的基本思想是将组件的加载推迟到真正需要渲染该组件的时候。
React.lazy
返回一个新的动态引入(dynamic import)的组件,这个组件会在需要时加载。
javascript
// 演示懒加载
const About = React.lazy(() => import("../pages/About"))
const User = React.lazy(() => import("../pages/User"))
2.使用Suspense包裹App组件
Suspense
主要用于处理组件的懒加载和异步数据的加载fallback
属性是在等待组件或数据加载完成时显示的内容。
javascript
import { Suspense } from 'react';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<HashRouter>
<Suspense fallback={<h3>Loading...</h3>}>
<App />
</Suspense>
</HashRouter>
);
执行npm run build,可以发现打包后确实会有多个js文件