前言
在浏览器中页面的跳转是根据地址栏 url 的变化的,从一个页面跳转到另一个页面,这个是属于多页面应用(MPA)。除了多页面,还有另一种情况,单页面应用(SPA)。
单页面应用只有一个完整的页面,url 的变化不会刷新页面,而是页面内容的局部变化。
路由
在 React 项目中路由也是较为重要的部分,我们可以用它来管理 URL,实现页面组件切换。在 router v6相比之前的 v3、v4、v5版本,v6吸收了之前版本的经验并进行了改进,相对于之前一些方法、属性也不尽相同,也相对复杂了一点。
使用
我们可以去安装 react-router-dom,它专门在 react 项目中实现一个 SPA 应用。
npm i react-router-dom
安装完成后查看 package.json 文件可以看到
上图可以看到安装的是 router v6 版本, v6 版本跟 v5 版本有些差异,移除了一些组件和修改一些属性,接下来使用和介绍的都是 v6 版本的
例子
index.js
React 的路由的实现有两种:一种是 browser history ------ <BrowserRouter>
,一种是 hash history ------<HashRouter>
。
从 react-router-dom 导入 BrowserRouter
组件,用来包裹 App 组件。
js
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import { BrowserRouter } from 'react-router-dom';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>
);
App.js
- NavLink ,该组件可以根据
to
属性跳转对应路由组件 - Routes ,和
Route
配合使用,用来包裹Route
组件 - Route ,根据
element
属性传入的组件,给组件匹配于path
值,当其路径与当前 path 值匹配时,则呈现该传入的组件 - Navigate,只要被渲染,就会修改路径,切换视图
js
// App.js
import { NavLink, Routes, Route, Navigate } from 'react-router-dom';
import Home from "./pages/home/home"
import My from "./pages/my/my"
function App() {
return (
<div>
<ul>
<li>
<NavLink to="/home">Home</NavLink>
</li>
<li>
<NavLink to="/my">my</NavLink>
</li>
</ul>
<Routes>
<Route path="/home" element={<Home />}></Route>
<Route path="/my" element={<My />}></Route>
<Route path="/" element={<Navigate to="/home" />}></Route>
</Routes>
</div>
);
}
export default App
home.js 创建 home 路由组件
js
function Home(props) {
return (
<div>
<p>Hello, home!</p>
</div>
);
}
export default Home
my.js 创建 my 路由组件
js
function My() {
return (
<div>
<p>Hello, my!</p>
</div>
);
}
export default My
这样我们就可以实现一个简单的路由切换了
路由表
其中这一段代码可以抽离出来,制定路由表
js
<Routes>
<Route path="/home" element={<Home />}></Route>
<Route path="/my" element={<My />}></Route>
<Route path="/" element={<Navigate to="/home" />}></Route>
</Routes>
创建 routers 文件夹 ,新建 index.js 文件
js
import { Navigate } from "react-router-dom"
import Home from '../pages/home/home'
import My from '../pages/my/my'
export const router = [
{
path: '/home',
element: <Home />
},
{
path: '/my',
element: <My />
},
{
path: '/',
element: <Navigate to="/home" />
},
]
使用路由表,在其中需要调用 useRoutes 并传入已经配置好的路由表,生成相当于 routes 以及 route 的组件结构,可以直接利用 {} 去进行包裹使用
js
// App.js
import { NavLink, useNavigate, useRoutes } from 'react-router-dom';
import { routers } from './routers'
// 根据路由表生成对应的路由规则
const element = useRoutes(routers)
// 直接在 dom 结构中使用
{element}
路由跳转
Link 组件
js
import { Link } from 'react-router-dom';
<Link to="/my">Link</Link>
NavLink 组件
js
import { NavLink } from "react-router-dom";
<NavLink to="/my">my</NavLink>
useNavigate 钩子函数
调用 useNavigate 函数去获得 navigate,传入路由 path 就可以切换到对应路由
js
import { // BrowserRouter, useNavigate, } from "react-router-dom";
const navigate = useNavigate();
navigate("/my");
参数传递
当我们进行路由跳转的时候需要携带一些参数,然后去到另一个路由利用参数进行一些操作,像一些跳转到详情页,传递的就是文章的id值,利用这id值查询出相应的数据等等情景。传参的方法有 params、search这几种。
params
在url后面拼接参数,进行动态传参,显示在地址栏上,刷新页面后参数不丢失
js
// 路由配置
{
path: '/my/:id',
element: <My />
},
// 路由跳转
<li>
<NavLink to="/my/18">my</NavLink>
</li>
// 路由组件接收,使用 useParams
import { useParams } from 'react-router-dom';
function My() {
let params = useParams()
console.log(params)
return (
<div>
<p>Hello, my!</p>
</div>
);
}
export default My
由 useParams 获取到路由跳转传递的参数
?
可选参数,代表该参数可传可不传,不传的话也不会报错。
通常来说一旦url寻找不到对应路由地址会发生错误,从而渲染不出该组件。
js
// 路由配置
{
path: '/my/:id?',
element: <My />
},
*
通配符
可以在后面匹配任何字符
js
// 路由配置
{
path: '/my/*',
element: <My />
},
search
在url后面用 ?
拼接参数,参数形式使用 key=value 方式,多个参数使用 &
进行拼接,进行动态传参,显示在地址栏上,刷新页面后参数不丢失
js
// 路由跳转,不用更改路由表
<li>
<NavLink to="/my?age=18&name=小明">my</NavLink>
</li>
// 路由组件接收参数 useSearchParams
import { useSearchParams } from 'react-router-dom';
function My() {
let [search] = useSearchParams()
let age = search.get("age")
let name = search.get("name")
console.log(search, age, name)
return (
<div>
<p>Hello, my!</p>
</div>
);
}
export default My
二级路由通过 useSearchParams 接收到的数据如下
路由嵌套
在有一些功能中,往往请求地址的前缀是相同的,不同的只是后面一部份,此时就可以使用多级路由(路由嵌套)来实现此路由的定义实现。
在一级路由定义 children 数组,里面用于配置二级路由,也就是进行路由嵌套。
js
// 配置
const router = [
{
path: '/home',
element: <Home />,
children: [
{
path: 'header',
element: <Header />
}
]
},
{
path: '/my',
element: <My />
},
{
path: '/',
element: <Navigate to="/home" />
},
]
要注意的是在一级路由组件里需要有<Outlet />
去进行占位,在页面排放位置。
js
// home.js home路由使用
import { Outlet } from 'react-router-dom';
function Home() {
return (
<div>
{/* 嵌套路由出口 */}
<Outlet />
<p>Hello, home!</p>
</div>
);
}
export default Home
总结
优点
-
加快页面响应速度,降低了对服务器的压力,因为单页应用只有第一次会加载完整的页面,切换内容只需请求该内容的数据,不用重新请求完整的页面
-
更好的用户体验,运行更加流畅,因为局部的更新请求的资源少,加载速度快
缺点
- 不利于 SEO ,因为 SEO 是根据首次请求的页面进行排名的,当路由的切换是监测不到的
- 因为是单页面应用,所以初次渲染首屏加载的内容会比较多,加载较慢