文章目录
- 一、前言
- 二、安装
- 使用 npm 安装(推荐)
- 使用 yarn 安装
- 三、基础使用
- 设置路由基础结构
- 定义路由和组件关联
- 直接在组件中定义路由
- 定义单独一个路由表
- 创建导航链接
- 四、核心组件和功能
- BrowserRouter 和 HashRouter
- Route 组件
- Link 组件
- Switch 组件
- 五、路由参数和嵌套路由
- 路由参数
- 嵌套路由
- 嵌套路由层级加深及应用场景拓展
- 六、编程式导航
- 七、路由权限
- 路由表
- 权限组件
- 404 页面
一、前言
react-router
库在 React 应用开发中扮演着至关重要的角色,它是实现页面路由管理的核心工具,能够让开发者根据不同的 URL 路径精准地渲染对应的组件,这对于构建流畅、交互性强的单页面应用(SPA)来说不可或缺。通过它,用户在浏览应用时无需频繁刷新整个页面,就能实现页面内容的动态切换,极大地提升了用户体验。
例如,许多知名的 React 应用,如一些大型的电商平台前端、在线办公软件等,都借助 react-router
来管理繁多的页面路由,实现复杂的功能模块切换和页面导航,使得用户可以方便快捷地在各个功能页面之间穿梭。
一般来说,React-Router v6 需要 React v16.8 或更高版本。因为 React-Router v6 利用了 React 的 Hooks 等特性,而 React v16.8 是第一个引入 Hooks 的版本。如果使用较低版本的 React,可能会导致 React-Router 无法正常工作或某些功能缺失。
二、安装
使用 npm 安装(推荐)
- 打开终端,进入 React 项目目录。
- 若没安装 React 项目,可用 Create React App 等创建新项目。
- 在项目目录下,运行
npm install react-router-dom
命令,安装成功后,可查看node_modules
目录有无react-router-dom
文件夹,或在代码文件导入相关模块(如import { BrowserRouter } from'react-router-dom'
)验证,无报错即安装正确。
使用 yarn 安装
- 打开终端,进入 React 项目目录。
- 运行
yarn add react-router-dom
命令,完成后即可使用。
三、基础使用
项目 React 版本: 18.3.1;react-router-dom 版本:7.1.1。
设置路由基础结构
在 React 应用入口文件(通常是 index.js
或 main.js
),用 BrowserRouter
(基于浏览器历史记录,URL 更自然)或 HashRouter
(特殊部署需求或兼容旧浏览器)包裹整个应用。如在 main.js
中:
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import { BrowserRouter as Router } from 'react-router-dom';
import App from './App.jsx';createRoot(document.getElementById('root')).render(<StrictMode><Router><App /></Router></StrictMode>
);
这里将 App
组件包裹在 Router
(BrowserRouter
的别名)中,这样 App
组件及其子组件就可以访问到路由相关的功能。
定义路由和组件关联
在 App
组件或其他组件内,用 Route
组件定义路由规则。
直接在组件中定义路由
这种方式将路由定义直接放在组件内部,对于小型应用或者路由规则比较简单的情况,具有直观的优点。开发者可以一目了然地看到某个组件与具体路由路径的关联情况,并且路由规则与组件紧密结合,很适合组件自身内部的路由管理,尤其当组件是独立的功能模块时,能使模块功能更加内聚。
import React from 'react';
import { Route } from 'react-router-dom';
import Home from './Home';
import About from './About';
const App = () => {return (<div>{/* Route 组件定义路由,exact 精确匹配,确保路径完全是'/'才渲染 Home 组件,访问'/about'时不会渲染 Home 组件。访问'/'渲染 Home 组件,访问'/about'渲染 About 组件。*/}<Route exact path="/" component={Home} /><Route path="/about" component={About} /></div>);
}
export default App
然而,当应用规模逐渐变大,路由规则变得复杂时,这种写法会暴露出明显的缺点。比如在一个大型电商应用中,若有上百个路由都写在 App 组件内,代码会变得极为臃肿,查找特定路由关联的组件、修改路由参数等维护操作将变得异常繁琐,代码的可读性也会大打折扣,不利于后续的开发和维护。
定义单独一个路由表
单独创建一个路由表可以将所有的路由规则集中管理,这对于大型应用开发来说尤为重要。在开发过程中,开发者能够在一个文件中清晰地查看所有的路由路径、对应的组件以及可能的路由参数等关键信息。而且,路由表具备良好的复用性,比如在不同的布局组件(如具有不同侧边栏的布局)中,都可以引用这个统一的路由表来实现相同的路由功能,无需重复配置。同时,当需要对路由规则进行修改时,只需在路由表文件中操作即可,无需逐个在使用路由的组件中进行修改,极大地提高了开发效率和代码的可维护性。随着应用的不断发展,添加新路由也十分方便,并且对于路由的中间件(如权限验证,只有登录用户才能访问某些路由)等功能,在路由表中也能更便捷地进行配置。
创建 routes.js
文件定义路由表,集中管理路由规则。
import { lazy } from 'react';
// lazy 用于设置路由懒加载
const Home = lazy(() => import('@/pages/Home'));
const About = lazy(() => import('@/pages/About'));const Router = [{path: '/',element: <Home />},{path: '/about',element: <About />},// 设置 404 页面{path: '*',element: <NotFound />}
];export default Router
然后在 App 组件中使用这个路由表:
import router from './router';
import { Suspense } from 'react';
import { useRoutes } from 'react-router-dom';function App() {// useRoutes 依传入路由配置生成相应路由元素,代表应渲染组件树结构。const element = useRoutes(router);// Suspense 设页面跳转加载提示,组件加载时显示 fallback 指定内容。return <Suspense fallback={'loading...'}>{element}</Suspense>;
}export default App
此方式适用于大型应用,复用性好,修改路由便捷,还方便配置中间件(如权限验证)。
创建导航链接
使用 Link 组件来创建应用内部的导航链接,它与传统的 标签不同,当用户点击 Link 组件时,不会引起整个页面的刷新,而是通过 JavaScript 更新 URL 并触发相应的组件渲染,实现单页面应用内的无缝导航。
import React from 'react';
import { Link } from 'react-router-dom';const Navigation = () => {return (<nav>{/* Link 组件 to 属性指定导航目标路径,点击'Home'链接导航到根路径渲染 Home 组件,点击'About'链接导航到'/about'路径渲染 About 组件。*/}<Link to="/">Home</Link><Link to="/about">About</Link></nav>);
}export default Navigation
四、核心组件和功能
BrowserRouter 和 HashRouter
- BrowserRouter:基于 HTML5 浏览器历史记录 API,URL 格式自然,无“#”符号,如电商产品详情页 URL 可为“/products/123”,利于 SEO。
- HashRouter:用 URL 哈希部分管理路由,适用于旧浏览器或服务器不支持 HTML5 历史记录 API 的情况,部署在简单静态服务器上表现良好。
hash和history
Route 组件
Route
组件是核心,定义路由规则,关联 URL 路径与组件。
import React from 'react';
import { Route, BrowserRouter as Router } from 'react-router-dom';
import Home from './Home';
import About from './About';
const App = () => {return (<Router><Route exact path="/" component={Home} /><Route path="/about" component={About} /></Router>);
}
export default App
exact
属性精确匹配路径,避免误渲染。
Link 组件
与传统 <a>
标签不同,点击不刷新页面,更新 URL 触发组件渲染。
import React from 'react';
import { Link } from 'react-router-dom';
const Navigation = () => {return (<nav><Link to="/">Home</Link><Link to="/about">About</Link></nav>);
}
export default Navigation
Switch 组件
包裹一组 Route
组件,只渲染第一个匹配成功的 Route
,防止多个组件同时渲染。
import React from 'react';
import { Route, Switch, BrowserRouter as Router } from 'react-router-dom';
import Home from './Home';
import About from './About';
import NotFound from './NotFound';
const App = () => {return (<Router><Switch><Route exact path="/" component={Home} /><Route path="/about" component={About} /><Route component={NotFound} /></Switch></Router>);
}
export default App
在这个例子中,如果没有Switch组件,当用户访问一个不存在的路径时,NotFound组件和前面可能部分匹配的组件(比如Home组件)都可能会被渲染。有了Switch组件,只有NotFound组件会被渲染,因为前面的路由都没有匹配成功
五、路由参数和嵌套路由
路由参数
React-Router
允许在路由路径中定义参数。例如,在一个列表中,内容详情页的路由可以定义为 /articles/:id
,其中 :id
是一个参数,表示详情的唯一标识符。在组件中可以通过 props.match.params
获取这个参数。
import React from 'react';
import { Route, BrowserRouter as Router } from 'react-router-dom';
const ArticleDetail = props => {const articleId = props.match.params.id;// 根据 articleId 获取文章详情并渲染return (<div><h1>Article Detail {articleId}</h1></div>);
}
const App = () => {return (<Router><Route path="/articles/:id" component={ArticleDetail} /></Router>);
}
export default App
用户访问 /articles/123
时,articleId
为“123”。
嵌套路由
复杂应用有嵌套路由情况,如电商产品详情页有评论区。
import React from 'react';
import { Route, BrowserRouter as Router, Switch } from 'react-router-dom';
import ProductDetail from './ProductDetail';
import ProductReviews from './ProductReviews';
const App = () => {return (<Router><Switch><Route path="/products/:id" component={ProductDetail}><Route path="/products/:id/reviews" component={ProductReviews} /></Route></Switch></Router>);
}
export default App
访问“/products/123/reviews”时,先渲染 ProductDetail
组件,再在其内部合适位置渲染 ProductReviews
组件。
嵌套路由层级加深及应用场景拓展
以企业级应用为例,展示部门详情页、员工列表页、员工个人详情页和员工绩效页等多层嵌套路由场景,通过合理配置实现页面有序切换。
import React from 'react';
import { Route, BrowserRouter as Router, Switch } from 'react-router-dom';const DepartmentDetail = props => {return (<div><h1>Department Detail</h1>{/* 展示部门相关信息 */}<Route path={`${props.match.path}/employees`} component={EmployeeList} /></div>);
}const EmployeeList = props => {return (<div><h2>Employee List</h </div>{/* 展示员工列表 */}<Route path={`${props.match.path}/:employeeId`} component={EmployeeDetail} /><Route path={`${props.match.path}/:employeeId/performance`} component={EmployeePerformance} /></div>);
}const EmployeeDetail = props => {const { employeeId } = props.match.params;return (<div><h3>Employee Detail - ID: {employeeId}</h3>{/* 展示员工个人详细信息 */}</div>);
}const EmployeePerformance = props => {const { employeeId } = props.match.params;return (<div><h3>Employee Performance - ID: {employeeId}</h3>{/* 展示员工绩效相关信息 */}</div>);
}const App = () => {return (<Router><Switch><Route path="/departments/:departmentId" component={DepartmentDetail} /></Switch></Router>);
}export default App
通过这样的复杂示例,可以更好理解嵌套路由在构建大型、功能丰富的应用中是如何发挥作用的,以及如何根据实际业务需求去设计和配置多层嵌套的路由结构,更好地应对复杂的页面关系和功能模块划分
六、编程式导航
除 Link
组件,React-Router 支持编程式导航,通过 history
对象实现,可在组件中用 props.history
获取(组件需通过 Route
组件渲染)。
import React from 'react';
import { Route, BrowserRouter as Router, useNavigate } from 'react-router-dom';
const MyComponent = props => {const Navigate = useNavigate();const handleClick = () => {Navigate('/about');}return (<div><button onClick={handleClick}>Go to About</button></div>);
}
const App = () => {return (<Router><Route path="/" component={MyComponent} /></Router>);
}
export default App
点击按钮,调用 handleClick
函数,导航到“/about”路径,适用于表单提交成功等内部逻辑导航。
七、路由权限
路由表
import { lazy } from 'react';
import Auth from './AuthRoute';
const Home = lazy(() => import('@/pages/Home'));
const About = lazy(() => import('@/pages/About'));
const List = lazy(() => import('@/pages/List'));
const User = lazy(() => import('@/pages/User'));
const NotFound = lazy(() => import('@/pages/NotFound'));const Router = [{path: '/',element: <Home />},{path: '/about',element: <About />},{path: '/app/',element: (// 自定义权限组件<Auth><BasicLayout /></Auth>),errorElement: <NotFound></NotFound>,children: [{path: 'list',element: <List />},{path: 'list/user',element: <User />}]},// 设置404页面{path: '*',element: <NotFound />}
];export default Router
权限组件
import { useEffect, useRef } from 'react';
import { useNavigate, useLocation } from 'react-router-dom';
import { message } from 'antd';
import useUpdateEffect from '@/components/useUpdateEffect';export default function Auth({ children }: any) {const location = useLocation().pathname;const didMountRef = useRef(false);const Navigate = useNavigate();const token = localStorage.getItem('token');useEffect(() => {if (didMountRef.current) {if (!token) {message.error('您未登录,将跳转');setTimeout(() => {Navigate('/login');}, 1000);}} else {didMountRef.current = true;}}, [token]);return children;
}
404 页面
import React from 'react';const CommonLine = props => (// 根据个人需求改动<div>未找到该页面</div>
);
export default CommonLine