背景
由于react框架开发的项目是单页面应用,项目中的页面切换一般都是一个个组件之间来回切换,而这种组件之间的来回切换我们需要使用到路由来支持,所以react-router提供了这一解决方案,以下是react-router的安装方法以及使用方式:
安装
npm install react-router-dom -s
使用方式
在react-router-dom中有四种配置路由的方式,分别是BrowseRouter,MemeoryRouter、StaticRouter、HashRouter
,一般我们基本使用的都是BrowseRouter
,以下是BrowseRouter
的基本用法。
路由简单配置
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter, Routes, Route} from 'react-router-dom'
// A 页面
const PageA = () => {
return (
<div>
<h1>PageA</h1>
</div>
)
}
// B 页面
const PageB = () => {
return (
<div>
<h1>PageB</h1>
</div>
)
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<BrowserRouter>
<Routes>
<Route path="/pageA" element={<PageA />} />
<Route path="/pageB" element={<PageB />} />
</Routes>
</BrowserRouter>
);
基于上面的配置,我们在浏览器中访问http://localhost:3000/pageA 即可切换到A页面,访问http://localhost:3000/pageA 即可切换到B页面。
Outlet实现通用Layout
一般在开发中,一个页面的框架基本都是头部和左侧菜单栏是固定的,右侧是根据路由切换后跟随变动的,基于这种布局样式,我们会创建一个通用的Layout组件来处理这种布局,而Outlet就是用来给这些变换的内容作占位的,下面是基本的实现方式
// Layout.jsx
// 这里只是简单的代码,一般开发中都会用到一些UI库,比如antd
import { Outlet } from 'react-router-dom'
const Layout = () => {
return (
<div>
<div><h1>这里是固定的头部</h1></div>
<div>
<div>
这里是左侧菜单栏
</div>
<Outlet>
</div>
</div>
)
}
// index.jsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter, Routes, Route} from 'react-router-dom'
import Layout from './Layout';
// A 页面
const PageA = () => {
return (
<div>
<h1>PageA</h1>
</div>
)
}
// B 页面
const PageB = () => {
return (
<div>
<h1>PageB</h1>
</div>
)
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<BrowserRouter>
<Routes>
<Route path="/" element={<Layout />}>
<Route path="/pageA" element={<PageA />} />
<Route path="/pageB" element={<PageB />} />
</Route>
</Routes>
</BrowserRouter>
);
通过浏览器方位根目录,即可访问到通用Layout,访问/pageA,会将/pageA下的内容展示在Layout中的Outlet占位组件中,同理访问/pageB,会将/pageB下的内容展示在Layout中的Outlet占位组件中。
路由切换以及传递数据
切换路由可以使用useNavigate钩子,得到一个navigate方法,通过调用navigate()来实现页面切换(组件切换更准确,因为是单页面应用,只有一个页面,所以路由切换都是组件之间的切换)。通过useLocation钩子获取到location对象,该对象里有个state对象里会接收到navigate方法里传递的参数来实现组件间的数据传递。
// Layout.jsx
// 这里只是简单的代码,一般开发中都会用到一些UI库,比如antd
import { Outlet, useNavigate } from 'react-router-dom'
const Layout = () => {
const navigate = useNavigate();
const handleNavigate = (path, params) => {
navigate(path, {
state: params
})
}
return (
<div>
<div><h1>这里是固定的头部</h1></div>
<div>
<div>
<button onClick={() => handleNavigate('/pageA',{id: 'a'})}>切换到PageA</button>
<button onClick={() => handleNavigate('/pageB',{id: 'b'})}>切换到PageB</button>
</div>
<Outlet>
</div>
</div>
)
}
// index.jsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter, Routes, Route, useLocation} from 'react-router-dom'
import Layout from './Layout';
// A 页面
const PageA = () => {
// 通过location.state获取传递过来的数据
const location = useLocation();
console.log(location.state.id) // a
return (
<div>
<h1>PageA</h1>
</div>
)
}
// B 页面
const PageB = () => {
// 通过location.state获取传递过来的数据
const location = useLocation();
console.log(location.state.id) // b
return (
<div>
<h1>PageB</h1>
</div>
)
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<BrowserRouter>
<Routes>
<Route path="/" element={<Layout />}>
<Route path="/pageA" element={<PageA />} />
<Route path="/pageB" element={<PageB />} />
</Route>
</Routes>
</BrowserRouter>
);
动态路由
一般情况下,我们会在url中拼接一些动态参数,这时候就需要动态路由来实现了,以下是动态路由的简单实现
// index.jsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter, Routes, Route, useParams} from 'react-router-dom'
// A 页面
const PageA = () => {
// 通过useParams获取url上的动态参数
// 地址栏输入:http://localhost:3000/pageA/a
const params = useParams();
console.log(params) // a
return (
<div>
<h1>PageA</h1>
</div>
)
}
// B 页面
const PageB = () => {
// 通过useParams获取url上的动态参数
// 地址栏输入:http://localhost:3000/pageB/b/zhangsan
const params = useLocation();
console.log(params.id) // b
console.log(params.name) // zhangsan
return (
<div>
<h1>PageB</h1>
</div>
)
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<BrowserRouter>
<Routes>
<Route path="/pageA/:id" element={<PageA />} />
<Route path="/pageB/id:/:name" element={<PageB />} />
</Routes>
</BrowserRouter>
);
查询参数获取
url上的?name=xxxx这种参数的获取可以通过useSearchParams来获取
// index.jsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter, Routes, Route, useSearchParams} from 'react-router-dom'
// A 页面
const PageA = () => {
// 通过useSearchParams获取url上的动态参数
// 地址栏输入:http://localhost:3000/pageA?name=zhangsan
const [searchParams, setSearchParams] = useSearchParams();
console.log(searchParams.get('name')) // zhangsan
return (
<div>
<h1>PageA</h1>
</div>
)
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<BrowserRouter>
<Routes>
<Route path="/pageA" element={<PageA />} />
</Routes>
</BrowserRouter>
);
设置查询参数
可以通过useSearchParams()返回的列表中的第二项来设置url中的?name=xxx的值
// index.jsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter, Routes, Route, useSearchParams} from 'react-router-dom'
// A 页面
const PageA = () => {
// 通过useSearchParams获取url上的动态参数
// 地址栏输入:http://localhost:3000/pageA?name=zhangsan
const [searchParams, setSearchParams] = useSearchParams();
console.log(searchParams.get('name')) // zhangsan
return (
<div>
<h1>PageA</h1>
<button onClick={() => setSearchParamse({name: 'durant', age: 18})}>设置查询参数</button>
</div>
)
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<BrowserRouter>
<Routes>
<Route path="/pageA" element={<PageA />} />
</Routes>
</BrowserRouter>
);
上面的代码中通过点击按钮“设置查询参数”后,url上?后面的内容会变成name=durant&age=18
数据路由
在react-router-dom的6.4版本中推出了数据路由的概念,数据路由主要解决了react中存在的瀑布流问题。首先我们知道,在这之前我们在react项目中切换路由时,我们的项目一般会有以下顺序:
- 加载UI组件
- 如果存在分包,那么会第二步会异步加载js包
- 组件的生命周期执行,异步获取远程数据
- 完成页面展示
而且上面的顺序都是按照串行的方式执行的,所以如果期间某一个步骤比较耗时,那后面的就会处于等待的状态,这样有着很明显的用户体验问题以及性能问题,所以为了解决这一问题,react-router-dom在6.4的版本中推出了数据路由,下面我们看看数据路由在切换路由的时候是如何工作的:
- 加载UI组件,同时如果存在分包,会异步加载js包,并且会异步获取远程数据。
- 完成页面展示
可以看出,数据路由会将传统的路由中的1,2,3步骤并行值性,这样大大提高了用户体验以及性能。这也是react-router-dom在6.4版本中的革命性改动。