# React路由 > React框架配套的路由系统插件 是 React Router。目前版本为V6。 ## 1. 基本使用 1. 创建路由实例 createBroswerRouter或者createHashRouter 2. 引入RouterProvider组件 3. 定义相关路由组件 ```jsx // 定义路由的模块 router/index.js import { createBrowserRouter } from 'react-router-dom'; import Home from '../pages/Home'; import About from '../pages/About'; const routes = [ { path: '/', element: , }, { path: '/about', element: , }, ]; const router = createBrowserRouter(routes); export default router; ``` ```jsx // 入口文件 import { RouterProvider } from 'react-router-dom'; import router from './router'; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( ); ``` ## 2. 嵌套路由 实现分2步走: 1. 在指定父路由对象上添加children属性,值为路由对象数组; 2. 在父路由组件上通过Outlet组件来指定子路由组件的渲染位置 ```jsx // 定义嵌套子路由 const routes = [ { path: '/', element: , children: [ // 这是索引路由 { // path: '', index: true, element: , }, { path: 'about', element: , }, ], }, ]; ``` ```jsx // 父组件 -- 布局组件 import Header from '../components/Header'; import { Outlet } from 'react-router-dom'; function Layout() { return (
{/* 渲染子路由组件 */}
); } export default Layout; ``` ## 3. 动态路由 > 当实现一个类似详情页的需求时,我们可能会需要使用到动态路由 例如,在商品列表页中点击某一商品获取详情。 1. 假设 商品列表 对应的 路由是 `/products` 2. 需要单独页面显示某一商品的详情,此时 路由为这么定义:`/products/details/{product-id}` 因此这就是动态路由。 3. 定义路由时,使用":"去定义路由中的参数,即`{path: '/products/details/:productId'}` 4. 访问时,会自动匹配。成功后,会将路由参数与值解析出来,存储在params对象中 5. 通过路由Hook `useParams` 来获取上面解析后的路由参数对象 ```jsx // 列表页代码 function Products() { let [products] = useState(initialProducts); return (
{products.map((p, i) => ( ))}
# Name Price Action
{i + 1} {p.name} {p.price} 详情
); } export default Products; // 详情页代码 import { useParams } from 'react-router-dom'; function ProductDetail() { const { productId } = useParams(); // console.log(params); return (

详情页

您正在浏览的商品ID:{productId}

); } export default ProductDetail; ``` ## 4. 查询参数 > 在实际开发中,可以通过路由参数实现页面间数据传递;当然也可以通过查询参数。 下面代码演示,如果通过查询参数实现页面间数据传递: ```jsx {products.map((p, i) => ( {i + 1} {p.name} {p.price} {/* 1 路由参数 在页面间传递数据 */} {/* 详情 */} {/* 2 查询参数 在页面间传递数据 */} {/* 详情 */} 详情 ))} ``` ```jsx let [searchParams] = useSearchParams();
{/* Back */} { // go('/products'); // go({ pathname: '/products' }); go(-1); }} > Back

详情页

您正在浏览的商品ID:{searchParams.get('pid')}

``` ## 5. 页面导航 ### 5.1 声明式导航 1. Link组件 2. NavLink组件 NavLink在使用时,更容易实现选中效果。 ```jsx const handleLinkStyle = ({ isActive, isPending }) => isPending ? 'pending' : isActive ? 'actived' : 'link'; function Header() { return (
  • 主页
  • 关于
  • 商品
); } ``` ### 5.2 编程式导航 通过 `useNavigate()`Hook 获取到 `navigate`函数,接着通过调用navigate函数实现页面跳转。 ```jsx const navigate = useNavigate(); // navigate(to: Path, option?) // navigate(delta: number) navigate(-1) navigate(1) navigate('/login') navigate({pathname: '/login'}) navigate({pathname: '/login', search: '?id=1'}) navigate({pathname: '/login'}, {replace: true, state: {}}) ``` ## 6. 数据获取 > 在路由系统中,如何选择时机去获取数据? > > 我们知道在VueRouter中,已经提供了两种方式,都是用户能够接收并且体验良好的方案 > > 1. 在组件渲染后,添加Loading效果后去获取数据 > 2. 在路由开启导航时先去获取数据,这样组件渲染时就会连同数据一起渲染出来了 ### 6.1 Loading效果 1. 在路由组件中定义对应数据的状态对象 2. 在组件渲染后,获取数据并修改状态 3. 组件模板中通过条件渲染实现当数据没有回来时渲染Loading组件,而数据返回来后在做列表渲染即可。 ### 6.2 Data Loading 1. 在定义路由的时候,给路由对象添加loader数据加载器(值为函数),它会在在导航过程中启动数据加载。 2. 在路由组件中,通过`useLoaderData()`Hook函数来获取上面loader返回的数据 ## 7. 页面访问权限 > Vue Router中可以通过导航守卫钩子来拦截到所有的页面导航,根据当前用户权限决定是否继续导航。 > > 但是React Router需要根据不同的路由定义方式来自行实现。 ### 7.1 自定义hook ```js /** *! 自定义hook ** 1 hook 函数的名称 必须 以 'use'开头去命名 ** 2 自定义hook中可以任意使用内置的所有hook函数 */ import { useEffect } from 'react'; import { useLocation, useNavigate } from 'react-router-dom'; const whiteList = ['/', '/about', '/login']; export function useAuth() { let location = useLocation(); let navigate = useNavigate(); let token = localStorage.getItem('token'); useEffect(() => { if (!token && !whiteList.includes(location.pathname)) { navigate(`/login?from=${location.pathname}`); } }, [location]); } ``` ### 7.2 Router Provider实现 ```jsx import { useNavigate } from 'react-router-dom'; import { useAuth } from '../hooks/useAuth'; export default function Protected() { const navigate = useNavigate(); // 页面访问鉴权 useAuth(); return (

受保护页面——需要登录后才可以访问

); } ``` ## 8. 其他 ### 8.1 路由懒加载 > 为了提高React项目首次加载速度,推荐使用路由懒加载。 实现时,需要从React核心库中引入`lazy`函数和`Suspense`组件。像这样, ```jsx import { createBrowserRouter } from 'react-router-dom'; import Home from '../pages/Home'; import { lazy, Suspense } from 'react'; const About = lazy(() => import('../pages/About')); const router = createBrowserRouter([ { path: '/', Component: Home }, { path: '/about', element: ( 加载中...}> ), }, ]); export default router; ``` 在定义路由时,React Router还提供了一系列组件: ```jsx import './App.css'; import { NavLink, Routes, Route } from 'react-router-dom'; import { lazy, Suspense } from 'react'; import Home from './pages/Home'; // import About from './pages/About'; // import User from './pages/User'; import UserProfile from './pages/UserProfle'; import UserPost from './pages/UserPost'; const About = lazy(async () => { await new Promise((resolve) => { setTimeout(() => { resolve(); }, 1000); }); return import('./pages/About.jsx'); }); const User = lazy(() => import('./pages/User')); // 实现路由懒加载的高阶组件 function withSuspense(Component) { return ( 组件疯狂加载中...}> ); } function App() { return (
主页 {' '} 关于 用户
} /> } /> import('./pages/UserPost.jsx')} element={} />
); } export default App; ``` 其中,`Route`组件就是定义路由的。当其path属性和当前地址匹配成功后,就会在其位置下渲染element绑定的React元素。 同时可以定义多个`Route`组件,这样就可以在页面中定义多个路由。并且这些`Route`组件必须作为`Routes`组件的直接子代元素。 为了将路由功能注入到项目中,需要在App组件中将整个项目代码包裹在一个`BrowserRouter`或者`HashRouter`组件中。 - BrowserRouter 会实现一个基于History 模式的路由 - HashRouter 会实现一个基于hash 模式的路由