本文章将在React应用中使用react-router v6.x实现一个简单博客,包含概念、安装、路由、导航、404页面等知识来快速入门;
项目完整版地址
目标效果:
React-Router 是什么?
Raact-Router是一个解决React应用的路由库,允许在单页面应用(SPA)中实现客户端路由;
- 传统的多页面应用中,每次导航都会向服务器发送请求并刷新浏览器重新加载整个页面
SPA中,react-router根据URL值的变化动态渲染对应的组件,无需重新加载整个页面;
安装 React-Router
使用Vite构建工具创建项目,选择React
npm create vite@latest
在项目中安装react-router
npm i react-router@6
基本使用
配置路由
创建一个router.jsx文件存放基本路由架构,其中createBrowserRouter函数是一种路由器模式,支持HTML5 History API管理URL和浏览器历史记录栈,并支持浏览器前进/后退导航;它包含了一组路由配置route,每个route对象由path属性配置路径,element属性指定路由对应的组件;
router.jsx
import { createBrowserRouter } from 'react-router-dom'
const router = createBrowserRouter([
{
path: '/',
element: <div>Hello, react-rouer</div>,
},
])
export default router
main.jsx 使用RouterProvider组件将路由表作为props传递
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import { RouterProvider } from 'react-router-dom'
import router from './router.jsx'
createRoot(document.getElementById('root')).render(
<StrictMode>
<RouterProvider router={router} />
</StrictMode>
)
运行项目浏览器显示
实现首页
在src下创建一个page文件夹存储对应的页面组件,在之下创建一个Home.jsx,在router.jsx将之前的div替换成Home组件
const Home = () => {
return (
<div>
<h3>首页</h3>
<p>Lorem ipsum dolor</p>
</div>
)
}
现在运行项目后,访问URL会发现页面显示Home组件:
实现文章及关于页面
创建Article.jsx
const Articles = () => {
return (
<div>
<h3>文章列表</h3>
</div>
)
}
export default Articles
创建About.jsx
const About = () => {
return (
<div>
<h3>About</h3>
<p>这里有关于该网站的信息</p>
</div>
)
}
export default About
在router.jsx中配置路由;
import { createBrowserRouter } from 'react-router-dom'
import Home from './page/Home'
import Articles from './page/Articles'
import About from './page/About'
const router = createBrowserRouter([
{
path: '/',
element: <Home />,
},
{
path: '/articles',
element: <Articles />,
},
{
path: '/about',
element: <About />,
},
])
export default router
在浏览器URL中后输入/about,即可访问到对应的About组件
配置导航
输入网址的方式很不方便,因此可以使用Link组件实现一个导航栏,点击访问到对应的网址,且不会刷新整个网页(默认的a标签会刷新整个页面重新请求);
创建导航栏组件
import { Link } from 'react-router-dom'
const NavBar = () => {
return (
<nav>
<Link to='/' style={{ padding: '10px' }}>
首页
</Link>
<Link to='/articles' style={{ padding: '10px' }}>
文章
</Link>
<Link to='/about' style={{ padding: '10px' }}>
关于
</Link>
</nav>
)
}
export default NavBar
根路径对应的组件将充当UI的根布局,在page/创建一个Root.jsx作为根布局,将现有的路由配置给注释;
router.jsx
import { createBrowserRouter } from 'react-router-dom'
import Home from './page/Home'
import Articles from './page/Articles'
import About from './page/About'
import Root from './page/Root'
const router = createBrowserRouter([
{
path: '/',
element: <Root />,
},
// {
// path: '/',
// element: <Home />,
// },
// {
// path: '/articles',
// element: <Articles />,
// },
// {
// path: '/about',
// element: <About />,
// },
])
export default router
将Navbar组件放入Root.jsx中
import Navbar from './Navbar'
const Root = () => {
return (
<>
<Navbar />
</>
)
}
export default Root
现在的效果是如下所示,目前点击文章和关于链接会发现页面url发生变化,但是页面内容会出现404 Not Found等文字;
处理错误
在page/下创建ErrorPage.jsx文件
export default function ErrorPage() {
return <div>404 页面</div>
}
修改router.jsx,添加一个由路由发生错误时页面显示的组件errorElement属性
import { createBrowserRouter } from 'react-router-dom'
import Home from './page/Home'
import Articles from './page/Articles'
import About from './page/About'
import Root from './page/Root'
import ErrorPage from './page/ErrorPage'
const router = createBrowserRouter([
{
path: '/',
element: <Root />,
errorElement: <ErrorPage />,
},
// {
// path: '/',
// element: <Home />,
// },
// {
// path: '/articles',
// element: <Articles />,
// },
// {
// path: '/about',
// element: <About />,
// },
])
export default router
现在点击文章或关于链接可以看见页面显示配置的错误组件
嵌套路由
嵌套路由:当想实现网页某个部分不变,只有网页的子部分发生变化
希望将首页等内容和导航栏都显示在一个页面中,可以将它们设置为根路由的子路由,增加一个children属性
index: true相当于path: ''即根父路由路径一致path: '/'表示根路径,子路由未添加/表示相对于父路由解析
import { createBrowserRouter } from 'react-router-dom'
import Home from './page/Home'
import Articles from './page/Articles'
import About from './page/About'
import Root from './page/Root'
import ErrorPage from './page/ErrorPage'
const router = createBrowserRouter([
{
path: '/',
element: <Root />,
errorElement: <ErrorPage />,
children: [
{
index: true,
element: <Home />,
},
{
path: 'articles',
element: <Articles />,
},
{
path: 'about',
element: <About />,
},
],
},
])
export default router
但现在首页或其它页都不显示内容,此时需要一个Outlet组件它告诉了父路由要在哪里显示子路由;
修改Root.jsx
import { Outlet } from 'react-router-dom'
import Navbar from './Navbar'
const Root = () => {
return (
<>
<Navbar />
<Outlet />
</>
)
}
export default Root
现在可以看到首页的内容在导航栏下显示
实现文章
1、先将文章列表展示:
在src下创建一个constants.js文件,存储模拟的文章数据
export const articles = [
{
id: '1',
title: 'react-router v6指南',
description: '在这里我要通过 简单博客 讲解 react-router v6',
},
{
id: '2',
title: 'redux-toolkit 指南',
description: '在这里我用通过 todolist 讲解 redux-toolkit',
},
]
修改Articles.jsx展示文章列表
import { Outlet } from 'react-router-dom'
import { Link } from 'react-router-dom'
import { articles } from '../constants'
const Articles = () => {
return (
<div>
<h3>文章列表</h3>
<ul>
{articles.map((article) => {
return (
<li key={article.id}>
{article.title}
</li>
)
})}
</ul>
</div>
)
}
export default Articles
2、添加子路由
路由可以嵌套在父路由内,修改router.jsx,index表示共用父组件的路由地址(默认子路由),展示InitArticle.jsx;
const InitArticle = () => {
return <div>这是一篇初始文章</div>
}
export default InitArticle
import { createBrowserRouter } from 'react-router-dom'
import Home from './page/Home'
import Articles from './page/Articles'
import About from './page/About'
import Root from './page/Root'
import InitArticle from './page/InitArticle'
import ErrorPage from './page/ErrorPage'
const router = createBrowserRouter([
{
path: '/',
element: <Root />,
errorElement: <ErrorPage />,
children: [
{
path: '',
element: <Home />,
},
{
path: 'articles',
element: <Articles />,
children: [
{
index: true,
element: <InitArticle />,
},
],
},
{
path: 'about',
element: <About />,
},
],
},
])
export default router
此时发现访问/articles无法显示InitArticle组件,在Articles.jsx添加Outlet组件用于子路由在父路由中渲染;
import { Outlet } from 'react-router-dom'
import { Link } from 'react-router-dom'
import { articles } from '../constants'
const Articles = () => {
return (
<div>
<h3>文章列表</h3>
<ul>
{articles.map((article) => {
return (
<li key={article.id}>
{article.title}
</li>
)
})}
</ul>
<Outlet />
</div>
)
}
export default Articles
可以看到ArtilceList组件渲染在Articles组件内,也就是Outlet的位置,效果图:
动态路由
实现在文章中点击文章的标题,在下方显示对应的文章内容,由于每篇文章的id都是不同的,因此需要动态的配置路由,path以:开头表示动态段,紧跟一个自命名参数;现在单个文章 article.jsx的路径为/articles/:id
router.jsx
import { createBrowserRouter } from 'react-router-dom'
import Home from './page/Home'
import Articles from './page/Articles'
import About from './page/About'
import Root from './page/Root'
import InitArticle from './page/InitArticle'
import Article from './page/Article'
import ErrorPage from './page/ErrorPage'
const router = createBrowserRouter([
{
path: '/',
element: <Root />,
errorElement: <ErrorPage />,
children: [
{
path: '',
element: <Home />,
},
{
path: 'articles',
element: <Articles />,
children: [
{
index: true,
element: <InitArticle />,
},
{
path: ':id',
element: <Article />,
},
],
},
{
path: 'about',
element: <About />,
},
],
},
])
export default router
修改Articles.jsx,给每个li添加Link to={/article/${article.id}},根据文章的id设置路径;
import { Outlet } from 'react-router-dom'
import { Link } from 'react-router-dom'
import { articles } from '../constants'
const Articles = () => {
return (
<div>
<h3>文章列表</h3>
<ul>
{articles.map((article) => {
return (
<li key={article.id}>
<Link to={`/articles/${article.id}`}>{article.title}</Link>
</li>
)
})}
</ul>
<Outlet />
</div>
)
}
export default Articles
在Article.jsx中,获取动态路径的参数,使用useParams钩子,该钩子返回一个对象包含所有的动态参数,通过解构获取刚刚设置的:id;
import { useParams } from 'react-router-dom'
import { articles } from '../constants'
const Article = () => {
const { id } = useParams()
const article = articles.find((item) => item.id === id)
// 如果文章列表没有该文章则返回提示
if (!article) {
return <div>没有找到你想要的文章! QAQ</div>
}
return <div>{article.description}</div>
}
export default Article
点击redux-toolkit指南,将访问id=2的文章,在下方显示文章内容
手动访问一个URL``/articles/333,会发现文章列表中没有,因此显示如下:
最后的想法
本文只讲到了react-router的基础使用,其中还有更多进阶的内容,比如表单处理、loader、action、路由鉴权等需要继续探索;
总结:
path和Link to配置路径时不以/开头是相对于父路由的路径useParams钩子获取动态参数,它返回的是一个对象,其中的属性是设置动态参数的名字Outlet组件是子路由组件在父路由组件显示的位置