内容介绍
本专题假设你已经具备了基本的 HTML 5 、Javascript(ES6) 和 CSS 3 知识。本专题是为了补全 AXUM 全栈开发所需要的技术栈,而不是从零开始的前端课程,如果你还不具备以上知识,请通过其它途径学习完成之后,再来学习本专题。React: 简介与安装
本章将通过编程世界通用的“Hello World”程序,告诉你:创建 React 应用的几种方法、虚拟DOM、JSX以及将 React 挂载到真实 DOM 的步骤。- 支持试读
React:类式组件、函数式组件及state和props
本章将讨论 React 的类式组件、函数式组件以及 React 两个最重要的属性:`state` 和 `props`。 React: 组件的生命周期
本章将讨论 React 组件的常用生命周期。- 支持试读
React: 事件处理、表单处理及受控组件与非受控组件
本章讨论 React 的事件处理,以及表单处理时涉及两个策略:受控组件与非受控组件。 React: 流程控制与key
本章将讨论 React(JSX) 的条件判断和循环。React: 自定义组件及组件通讯
本章我们将正式开启 React 组件之路。虽然我们之前章节的都叫组件,但整个应用只有一个组件,略显单薄。同时,我们还将讨论组件之间如何进行通讯。React: 路由
本章将讨论 React 的路由:通过路由,你可以制作出“多页面”的系统。React: 状态共享
本章将通过一个非常典型的案例来讨论 React 的状态共享:只有登录用户才能看到某些内容。早期的 React 完全依靠其生态中的 redux 等第三方库来实现状态共享;现在 React 提供了官方的 `Context` 来实现这一目的。React: SEO挑战、服务端渲染及本地存储
经过紧张的学习,React 课程终于暂告一个段落了。本章将是一个相对轻松的内容,我们一起探讨一下 React 应用的 SEO 以及为什么需要服务端渲染,同时对 React 课程做个简单的小结。NextJS: 简介与安装
NextJS 是一个 React 框架,它提供了很多有用的功能把 React 的力量发挥地淋漓尽致。本章我们将开始 NextJS 之旅,首先自然是安装它,然后来一个「你好,NextJS」NextJS: 渲染模式和数据获取
趁你现在对我们刚刚讨论的有关 React 和 SEO 的问题还保持有较强的印象,我们先来讨论 NextJS 是如何利用多种渲染模式来应用 SEO 挑战的。NextJS: 内置组件及自动路由
NextJS 既然是 React 的框架,自然提供了一些内置组件来扩展 React。本章将介绍几个常用的 NextJS 内置组件,同时也将介绍 NextJS 的路由系统。NextJS: 开发博客系统
本章我们将使用 NextJS 开发一个小型的博客系统,你将学习到如何从远程服务器获取数据以及数据过滤、NextJS 常用组件的用法、NextJS 的自动路由等功能。Tailwind:简介与响应式设计的基本原则
TailwindCSS 是一款响应式的、移动设备优先的 CSS 工具类框架。本章对响应式布局、移动设备优先、断点等基本概念进行简要说明;并讨论几种安装 tailwind 的方式。Tailwind: 通过小示例体验它
本章通过两个小示例来体验一下 tailwind 的魔力。Tailwind: 默认配置
Tailwind 定义了一系列变量,比如颜色、大小、间距等。本章将介绍 tailwind 的一些默认设置。Tailwind: 撸一个按钮
本章将带你使用 tailwind 撸一个按钮Tailwind: 使用 flex 和 grid 进行响应式布局
本章我们将讨论使用 `flex` 和 `grid` 进行响应式布局,以及为什么不建议再使用 `float` 进行布局。- 支持试读
Tailwind: 撸一个带图标和动画效果的下拉框
本章将使用 tailwind 实现一个没有任何 Javscript 代码的纯 CSS 的下拉框,把应用到导航栏、菜单栏时,也被称为下拉菜单。同时我们将讨论如何在 tailwind 中使用图标,包括图标的进化史:从字体文件到SVG。 Tailwind: 撸一个报价卡片
本章我们将使用 tailwind 撸一个报价卡片。Tailwind: 撸一个响应式的纯CSS导航栏
本章将使用 tailwind 撸一个响应式的、纯 CSS 的导航栏。Tailwind: 集成到React/NextJS
本章将介绍如何将 Tailwind 集成到 React 或 NextJS 项目中。Tailwind: 复用
本章通过将之前撸的按钮改成 NextJS 版,进而讨论 Tailwind 的复用原则。Tailwind: 配置和插件
本章将讨论如何配置 tailwind,以及几个 tailwind 官方插件。Tailwind: 制作响应式博客
本章我们使用 tailwind 将之前课程中 NextJS 迷你博客改造为响应式的布局。
React: 路由
本章将讨论 React 的路由:通过路由,你可以制作出“多页面”的系统。
React 是典型的SPA(单页面应用),这里所说的“多页面”是指逻辑上的,或者说是从访客的感观上的。
react-router
应该是 React 生态中最受欢迎的路由组件了,在开始使用它之前,需要先把它安装到当前项目里:
yarn add react-router-dom
本教程使用的是 react-router 6 ,你从其它途径搜索或学习到的大概率会是旧版的 react-router 5。两个版本差异非常大,请注意甄别。
本教程使用的是 react-router 6 ,你从其它途径搜索或学习到的大概率会是旧版的 react-router 5。两个版本差异非常大,请注意甄别。
常用的路由组件
组件/hook | 说明 |
---|---|
BrowserRouter | 使用直观的 URL 将当前 location 存储在浏览器的地址栏中,并使用浏览器的内置历史堆栈进行导航。 |
HashRouter | 将当前localtion 存储在当前 URL 的Hash部分中(# ),因此它永远不会发送到服务器。 |
Routes | 匹配当前 location 的一组子路由 |
Route | 路由系统最重要的部分。将 URL 段耦合到组件中 |
Link | 允许用户通过单击或点击它导航到另一个页面。类似于 HTML 中的 <a> 标签 |
NavLink | Link 的特例,会自动加上 active 类,以便高亮显示与当前 location 匹配的链接。主要用于导航栏、面包屑等场景。 |
Navigate | 改变当前 location ,主要用于跳转。 |
Outlet | 用于嵌套路由中渲染子路由对应的组件。 |
useNavigate | 以编程方式使用 Navigate 组件,即以编程方式实现跳转 |
useParams | 以编程方式获取动态路由中的参数,比如 /user/:userid 中的 userid 参数 |
useSearchParams | 以编程方式获取路由中的 URL 参数,比如 /users?page=1&sort=desc 中的 page 和sort 参数 |
useRoutes | 它的功能和Routes 组件一样。但它使用 JavaScript 的简单对象来代替 Route 元素的定义。 |
BrowserRouter 还是 HashRouter
首先我想用最通俗的方式介绍 BrowserRouter
和 HashRouter
。假设 location
是 /user/profile
,那么:
- BrowserRouter长成这样:
https://axum.rs/user/profile
- HashRouter长成这样:
https://axum.rs/#/user/profile
BrowserRouter | HashRouter | |
---|---|---|
直观性 | 高 | 一般 |
SEO | 有利 | 基本不支持 |
浏览器支持度 | 主流浏览器都支持 | 基本上所有浏览器都支持 |
部署 | 需要 Web 服务器做一些简单设置 | 基本上所有环境都能部署,包括 Github Page、Cloudflare Page 等 |
服务端是否能接收到URL | 能 | 不能。它永远不会发送到服务端 |
本教程将使用 BrowserRouter,原因如下:
- 哪怕是最受诟病的 Windows 平台也很少人用老掉牙的 IE 浏览器,而是使用 Chrome(及其衍生品)等主流浏览器
- 由于后面章节会讲到SEO和服务端渲染,HashRouter 根本做不到
- 虽然说部署时需要做一些设置,但这不是问题。使用 Nginx 部署,只要加一条指令即可。
基于以下原因,你可能不得不使用 HashRouter:
- 面向的访客主要以老旧的操作系统为主,他们无法使用现代的、主流的浏览器(题外话:如果真是要面向 WIN XP 这种客户,还是安心用你的
<table>
布局吧,本专题你都可以不用学了,本专题的 React、NextJS 和 TailwindCSS 都需要现代的主流浏览器) - 不关心SEO和服务端渲染
- 需要部署到 Github Page 或 Cloudflare Page 等平台
react-router 要求每个路由组件都要包裹在一个 Router
(抽象类,BrowserRouter
和 HashRouter
是它的具体实现。注意,不要和后面的 Route
组件混淆,一个字母的不同,造就了两个完全不一样的组件) 中,所以我们在入口文件中,加上 BrowserRouter
:
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom'; // 引入 BrowserRouter
import App from './App';
import './index.css';
ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
{/* 使用 BrowserRouter 包裹主组件 */}
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>
);
案例
下面开始以案例驱动学习的方式,开始对 react-roter 进行学习。
“页面”组件 | 路由 | 说明 |
---|---|---|
Home | / | 首页 |
News | /news | 新闻列表 |
NewsDetail | /news-detail/:id | 新闻详情 |
About | /about | 关于我们 |
在“多页面”应用中,我们通常把“页面”放在 pages
目录下(还记得之前章节说过,组件放在 components
目录下吗),所以一个典型的 react 应用的目录结构应该是:
- src
- components
- Foo.jsx
- Bar
- index.jsx
- Foobar.jsx
- pages
- Page1.jsx
- PageN
- index.jsx
- SubPage.jsx
- App.jsx
开始动手之前,别忘了将
BrowserRouter
添加到入口文件,并包裹整个App
主组件。往上翻你就能看到代码
开始动手之前,别忘了将 BrowserRouter
添加到入口文件,并包裹整个 App
主组件。往上翻你就能看到代码
创建页面组件
在 src
目录下创建 pages
目录,然后分别在其中创建页面组件:
首页: src/pages/Home.jsx
import React from 'react';
export default function Home() {
return <div>主页</div>;
}
新闻列表: src/pages/News/index.jsx
import React from 'react';
export default function News() {
return <div>新闻列表</div>;
}
新闻详情: src/pages/News/Detail.jsx
import React from 'react';
export default function NewsDetail() {
return <div>新闻详情</div>;
}
关于我们: src/pages/Home.jsx
import React from 'react';
export default function About() {
return <div>关于我们</div>;
}
本小节代码:axum-rs-react-router-1-init
创建导航栏
为了演示 NavLink
和 Link
的区别,本案例我们将分别用这两个组件创建两个不同版本的导航栏组件。在实际开发中完全没有这个必要——直接使用 NavLink
就好了。在后续案例中,我们也只会使用 NavLink
来创建导航栏组件。
使用 Link
的 NavBarUsingLink
// src/compontents/NavBar/UsingLink.jsx
import React from 'react';
import { Link } from 'react-router-dom';
export default function NavBarUsingLink() {
return (
<nav>
<Link to="/">首页</Link>
<Link to="/news">新闻</Link>
<Link to="/about">关于我们</Link>
</nav>
);
}
使用 NavLink
的 NavBarUsingNavLink
// src/compontents/NavBar/UsingNavLink.jsx
import React from 'react';
import { NavLink } from 'react-router-dom';
export default function NavBarUsingLink() {
return (
<nav>
<NavLink to="/">首页</NavLink>
<NavLink to="/news">新闻</NavLink>
<NavLink to="/about">关于我们</NavLink>
</nav>
);
}
可以看到,Link
和 NavLink
的 API 基本一样, 这是自然的,NavLink
只是 Link
的一种特例,也就是一个子类,它们拥有相同的某些属性,比如:
to
:指定要跳转的目标地址。这和 HTML 的<a href="">
非常类似
为了让导航栏看起来更直观,同时为了 NavLink
,我们在 index.css
上加了简单的 CSS:
nav {
display: flex;
gap: 0.25rem;
}
.active {
color: red;
}
在主组件上添加导航栏
现在,我们把定义好的导航栏添加到 App
组件上。先用 Link
实现的试试:
import React from 'react';
import NavBarUsingLink from './components/NavBar/UsingLink';
function App() {
return (
<div>
<NavBarUsingLink />
<h1>你好,axum中文网 - 来自 react 的问候</h1>
</div>
);
}
export default App;
很好,导航栏出现了,但也只是出现了而已。让我们换成用 NavLink
实现的试试:
import React from 'react';
// import NavBarUsingLink from './components/NavBar/UsingLink';
import NavBarUsingNavLink from './components/NavBar/UsingNavLink';
function App() {
return (
<div>
<NavBarUsingNavLink />
<h1>你好,axum中文网 - 来自 react 的问候</h1>
</div>
);
}
export default App;
哦吼,奇迹出现了,导航栏中的“首页”竟然变成了红色。
这个红色怎么来的?此刻麻烦你把 index.css
中的 .active
的颜色改一下,比如:
.active {
color: green;
}
NavLink
是一种特殊的 Link
,它会自动将那个和当前URL匹配的项,应用 .active
样式
如果使用原始的 Link
,要实现这种效果,就要写一大堆代码了
继续点击导航栏中其它链接,它们也都会出现高亮,但页面内容却没变,我们将在下一小节解决这个问题。
本小节代码:axum-rs-react-router-1-navbar
让页面动起来:路由匹配
本小节我们将让页面动起来:点击导航栏不同的链接,页面显示对应的内容。我们只需要在 App
组件中需要显示页面内容的区域加上路由匹配就行了。就是说,只要把上例中的 <h1>你好,axum中文网 - 来自 react 的问候</h1>
这部分内容替换为对应页面的内容即可。
import React from 'react';
// import NavBarUsingLink from './components/NavBar/UsingLink';
import NavBarUsingNavLink from './components/NavBar/UsingNavLink';
import { Routes, Route } from 'react-router-dom';
import Home from './pages/Home';
import News from './pages/News';
import About from './pages/About';
import NewsDetail from './pages/News/Detail';
function App() {
return (
<div>
<NavBarUsingNavLink />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/news" element={<News />} />
<Route path="/news-detail/:id" element={<NewsDetail />} />
<Route path="/about" element={<About />} />
</Routes>
</div>
);
}
export default App;
我们重点关注这段代码:
<Routes>
<Route path="/" element={<Home />} />
<Route path="/news" element={<News />} />
<Route path="/news-detail/:id" element={<NewsDetail />} />
<Route path="/about" element={<About />} />
</Routes>
<Routes>
:这里就是定义路由匹配了,它只是提供了一种“容器”,告诉 React,这个部分要匹配路由啦。具体的匹配规则由它那些<Route>
子组件定义。<Route>
:定义具体的路由匹配规则:path
:指明要匹配的路由。你可以指定多种路由:- 静态路由:像
/
、/news
、/about
这种,直接以字面量的形式指定的称为静态路由 - 动态路由:像
/news-detail/:id
这种的称为动态路由,因为它包含一个参数:id
- 静态路由:像
element
:指定路由对应的组件。第一眼看上去,你对它的写法可能有点迷惑,让我们以<Route path="/" element={<Home />} />
为例进行说明。element={}
是什么意思?- 首先,
element
是一个props
- 其次,因为用了
{}
,所以它需要传递一个 JS 表达式
- 首先,
<Home />
是什么?- 它是 JSX
- JSX 是什么?它是
React.createElement()
的语法糖 React.createElement()
是什么?它是一个函数(你也可以称其为方法)。我们在讲组件通讯时说过,JS 里万物皆对象,React.createElement()
自然可以作为表达式传递给element
这个props
path
:指明要匹配的路由。你可以指定多种路由:- 静态路由:像
/
、/news
、/about
这种,直接以字面量的形式指定的称为静态路由 - 动态路由:像
/news-detail/:id
这种的称为动态路由,因为它包含一个参数:id
- 静态路由:像
element
:指定路由对应的组件。第一眼看上去,你对它的写法可能有点迷惑,让我们以<Route path="/" element={<Home />} />
为例进行说明。element={}
是什么意思?- 首先,
element
是一个props
- 其次,因为用了
{}
,所以它需要传递一个 JS 表达式
- 首先,
<Home />
是什么?- 它是 JSX
- JSX 是什么?它是
React.createElement()
的语法糖 React.createElement()
是什么?它是一个函数(你也可以称其为方法)。我们在讲组件通讯时说过,JS 里万物皆对象,React.createElement()
自然可以作为表达式传递给element
这个props
- 静态路由:像
/
、/news
、/about
这种,直接以字面量的形式指定的称为静态路由 - 动态路由:像
/news-detail/:id
这种的称为动态路由,因为它包含一个参数:id
element={}
是什么意思?- 首先,
element
是一个props
- 其次,因为用了
{}
,所以它需要传递一个 JS 表达式
- 首先,
<Home />
是什么?- 它是 JSX
- JSX 是什么?它是
React.createElement()
的语法糖 React.createElement()
是什么?它是一个函数(你也可以称其为方法)。我们在讲组件通讯时说过,JS 里万物皆对象,React.createElement()
自然可以作为表达式传递给element
这个props
- 首先,
element
是一个props
- 其次,因为用了
{}
,所以它需要传递一个 JS 表达式
- 它是 JSX
- JSX 是什么?它是
React.createElement()
的语法糖 React.createElement()
是什么?它是一个函数(你也可以称其为方法)。我们在讲组件通讯时说过,JS 里万物皆对象,React.createElement()
自然可以作为表达式传递给element
这个props
很好,目前为止至少让页面动起来了。
你会注意到,”新闻详情“并没有在导航栏上,这是因为要进到”新闻列表“里才会出现,因为它的URL是由”新闻列表“在渲染时,根据ID动态生成的。
下一小节我们将实现”新闻列表“和”新闻详情“
本小节代码:axum-rs-react-router-1-match-routes
实现“新闻列表”和“新闻详情”
首先,我们实现“新闻列表”。我们使用模拟数据来做一个列表:
import React, { useEffect, useState } from 'react';
import { Link } from 'react-router-dom';
export default function News() {
const [newsList, setNewsList] = useState([]); // 第5行
useEffect(() => { // 第7行
// 模拟从后端获取数据
setNewsList([
{ id: 1, title: '新闻1' },
{ id: 2, title: '新闻2' },
{ id: 3, title: '新闻3' },
{ id: 4, title: '新闻4' },
{ id: 5, title: '新闻5' },
{ id: 6, title: '新闻6' },
{ id: 7, title: '新闻7' },
{ id: 8, title: '新闻8' },
{ id: 9, title: '新闻9' },
{ id: 10, title: '新闻10' },
]);
}, []); // 第21 行
return (
<div>
<ul>
{newsList.map((news) => (
<li key={news.id}>
<Link to={`/news-detail/${news.id}`}>{news.title}</Link>
</li>
))}
</ul>
</div>
);
}
-
第5行:创建了用于维护新闻列表的状态及其设置函数
-
第7~21行:创建了模拟数据
-
第25~29行:遍历这个模拟数据
{newsList.map((news) => ( <li key={news.id}> <Link to={`/news-detail/${news.id}`}>{news.title}</Link> </li> ))}
<Link>
的to
属性:使用 JS 的字符串模板功能,将当前新闻的ID拼接成一个完整的 URL
第5行:创建了用于维护新闻列表的状态及其设置函数
第7~21行:创建了模拟数据
第25~29行:遍历这个模拟数据
{newsList.map((news) => (
<li key={news.id}>
<Link to={`/news-detail/${news.id}`}>{news.title}</Link>
</li>
))}
<Link>
的to
属性:使用 JS 的字符串模板功能,将当前新闻的ID拼接成一个完整的 URL
那么,“新闻详情”如何能接收到传递过来的 :id
参数呢?答案是使用 usePramas
hook:
import React, { useEffect, useState } from 'react';
import { useParams } from 'react-router-dom';
export default function NewsDetail() {
const { id } = useParams(); // 第4行
const [detail, setDetail] = useState('');
useEffect(() => {
// 模拟从后端获取数据
setDetail(`ID为${id}的新闻详情`);
}, []);
return <div>{detail}</div>;
}
第4行:通过 useParams
hook 获取到传递过来的 :id
参数:
-
useParams
会将动态路由里定义的所有参数打包成一个 JS 普通对象,类似这样:{ id: 1, foo: 'bar', }
-
通过解构,可以很方便的获取到其中某个参数:
const {id} = useParams()
,如果不使用解构操作,你可能需要这样:const ps = useParams(); const id = ps.id;
-
useParams
在将动态路由的参数打包时,会将参数名作为key,所以你要使用和参数同名变量进行解构。比如:/news-detail/:newsid
这样的定义,你就要const { newsid } = useParams()
才能得到正确的结果
useParams
会将动态路由里定义的所有参数打包成一个 JS 普通对象,类似这样:
{
id: 1,
foo: 'bar',
}
通过解构,可以很方便的获取到其中某个参数:const {id} = useParams()
,如果不使用解构操作,你可能需要这样:
const ps = useParams();
const id = ps.id;
useParams
在将动态路由的参数打包时,会将参数名作为key,所以你要使用和参数同名变量进行解构。比如:/news-detail/:newsid
这样的定义,你就要 const { newsid } = useParams()
才能得到正确的结果
看上去很OK了,但这里有个严重的问题:“新闻详情”应该是和“新闻列表”一起组成了一个完整的“新闻”,它们应该使用相同的 URL 前缀。并且,在进入“新闻详情”时,导航栏中的“新闻”应该保持actvie
状态,但目前并没有。下一节我们将通过嵌套路由解决这个问题。
本小节代码:axum-rs-react-router-1-news
使用嵌套路由实现完整的“新闻”
我们只需要把 App
中对应的路由匹配部分进行修改就可以了:
//...
function App() {
return (
<div>
<NavBarUsingNavLink />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/news">
<Route path="" element={<News />} />
<Route path="detail/:id" element={<NewsDetail />} />
</Route>
<Route path="/about" element={<About />} />
</Routes>
</div>
);
}
// ...
重点来看:
<Route path="/news">
<Route path="" element={<News />} />
<Route path="detail/:id" element={<NewsDetail />} />
</Route>
<Route path="/news">
,声明一个匹配/news
的规则,作为前缀,它不需要指定element
- 然后在它里面声明了两个子路由
<Route path="" element={<News />} />
,这里的path
为空字符串,所以它实际匹配的就是/news
<Route path="detail/:id" element={<NewsDetail />} />
,它实际匹配的是/news/detail/:id
<Route path="" element={<News />} />
,这里的path
为空字符串,所以它实际匹配的就是/news
<Route path="detail/:id" element={<NewsDetail />} />
,它实际匹配的是/news/detail/:id
这里和 axum 有所不同,在 axum 里,声明子路由时可以使用绝对路径的形式,axum 会正确处理,但 react-router不行:
{/* 错误示例 */} <Route path="/news"> <Route path="/" element={<News />} /> <Route path="/detail/:id" element={<NewsDetail />} /> </Route>
这里和 axum 有所不同,在 axum 里,声明子路由时可以使用绝对路径的形式,axum 会正确处理,但 react-router不行:
{/* 错误示例 */}
<Route path="/news">
<Route path="/" element={<News />} />
<Route path="/detail/:id" element={<NewsDetail />} />
</Route>
当然,“新闻列表”里指向“新闻详情”的链接也要做出修改:
//...
export default function News() {
//...
return (
<div>
<ul>
{newsList.map((news) => (
<li key={news.id}>
<Link to={`/news/detail/${news.id}`}>{news.title}</Link>
</li>
))}
</ul>
</div>
);
}
Link
的 to
由原来的 /news-detail
改成了 /news/detail
。
至此,我们使用 react-router 实现了一个“多页面”应用。恭喜恭喜,撒花撒花 🎉🎉。但作为一个无私奉献的站长,我还想补充两个知识点。继续下一小节吧/
本节代码:axum-rs-react-router-1-nested
获取查询参数
虽然现在大家越来越习惯使用 Path 参数(就是我们这个案例中的“新闻详情”这种动态路由),但 URL 查询参数也还是需要的。利用 react-router 的 useSearchParams
hook就能获取到传递过来的 URL 查询参数。
//...
export default function News() {
const [newsList, setNewsList] = useState([]);
const [searchParams, setSearchParams] = useSearchParams({
page: 0,
keyword: '',
});
const page = searchParams.get('page') || 0;
const keyword = searchParams.get('keyword') || '';
// ...
return (
<div>
<div>
当前页码:{page}, 搜索关键字:
{keyword || '(无)'}
</div>
<hr />
<ul>
{newsList.map((news) => (
<li key={news.id}>
<Link to={`/news/detail/${news.id}`}>{news.title}</Link>
</li>
))}
</ul>
<hr />
<ul>
<li>
<Link to="/news?page=5&keyword=aaa">第5页-aaa</Link>
</li>
<li>
<Link to="/news?page=10&keyword=bbb">第10页-bbb</Link>
</li>
<li>
<button
onClick={() => setSearchParams({ page: 20, keyword: 'axum中文网' })}
>
编程设置:第20页,axum中文网
</button>
</li>
</ul>
</div>
);
}
本节代码:axum-rs-react-router-1-search-params
-
使用
useSearchParams
hookconst [searchParams, setSearchParams] = useSearchParams({ page: 0, keyword: '', });
- 它的返回值是一个数组,分别是:维护所有参数的变量名,以及设置参数的函数
- 它的参数是可选的,可以通过该参数设置URL参数的默认值
-
获取 URL 参数
const page = searchParams.get('page') || 0; const keyword = searchParams.get('keyword') || '';
- 使用
get()
方法获取到指定的 URL 参数。通过||
运算符给定默认值是一种非常好的编程技巧(此方法适用于很多 C 语言系的编程语言,包括 JavaScript、PHP、Bash等)。当然,由于我们useSearchParams
时已经给了默认值,所以此处的||
并不是那么有必要。
- 使用
-
使用 URL 参数
<div> 当前页码:{page}, 搜索关键字: {keyword || '(无)'} </div>
- 将获取到的 URL 参数显示出来,没什么好讲的。
-
设置 URL 参数有很多种方法:
-
通过拼接 URL:
<Link to="/news?page=5&keyword=aaa">第5页-aaa</Link>
-
通过调用
setSearchParams
:- 它允许使用 Javascript 的简单对象
- 它会自动对中文、特殊字符进行 URL 编码
<button onClick={() => setSearchParams({ page: 20, keyword: 'axum中文网' })} > 编程设置:第20页,axum中文网 </button>
-
还有一种是通过
method="get"
的表单进行提交,本案例没有使用这种方法,你自己可以尝试 -
我们不建议使用拼接URL的方式:
- 需要写成
?key=value&key=value
的形式 - 更重要的是,对于中文和特殊字符,需要使用 URL 编码后的内容进行拼接
- 而
setSearchParams
和 表单提交的方式显然更智能一些
- 需要写成
-
使用 useSearchParams
hook
const [searchParams, setSearchParams] = useSearchParams({
page: 0,
keyword: '',
});
- 它的返回值是一个数组,分别是:维护所有参数的变量名,以及设置参数的函数
- 它的参数是可选的,可以通过该参数设置URL参数的默认值
获取 URL 参数
const page = searchParams.get('page') || 0;
const keyword = searchParams.get('keyword') || '';
- 使用
get()
方法获取到指定的 URL 参数。通过||
运算符给定默认值是一种非常好的编程技巧(此方法适用于很多 C 语言系的编程语言,包括 JavaScript、PHP、Bash等)。当然,由于我们useSearchParams
时已经给了默认值,所以此处的||
并不是那么有必要。
使用 URL 参数
<div>
当前页码:{page}, 搜索关键字:
{keyword || '(无)'}
</div>
- 将获取到的 URL 参数显示出来,没什么好讲的。
设置 URL 参数有很多种方法:
-
通过拼接 URL:
<Link to="/news?page=5&keyword=aaa">第5页-aaa</Link>
-
通过调用
setSearchParams
:- 它允许使用 Javascript 的简单对象
- 它会自动对中文、特殊字符进行 URL 编码
<button onClick={() => setSearchParams({ page: 20, keyword: 'axum中文网' })} > 编程设置:第20页,axum中文网 </button>
-
还有一种是通过
method="get"
的表单进行提交,本案例没有使用这种方法,你自己可以尝试 -
我们不建议使用拼接URL的方式:
- 需要写成
?key=value&key=value
的形式 - 更重要的是,对于中文和特殊字符,需要使用 URL 编码后的内容进行拼接
- 而
setSearchParams
和 表单提交的方式显然更智能一些
- 需要写成
通过拼接 URL:<Link to="/news?page=5&keyword=aaa">第5页-aaa</Link>
通过调用 setSearchParams
:
- 它允许使用 Javascript 的简单对象
- 它会自动对中文、特殊字符进行 URL 编码
<button
onClick={() => setSearchParams({ page: 20, keyword: 'axum中文网' })}
>
编程设置:第20页,axum中文网
</button>
还有一种是通过 method="get"
的表单进行提交,本案例没有使用这种方法,你自己可以尝试
我们不建议使用拼接URL的方式:
本小节代码:axum-rs-react-router-1-search-params
使用路由表统一管理路由规则
路由表允许我们将路由的规则使用一个Javascript的简单对象来定义,方便统一管理和维护。
在与antd等UI库集成时,路由表会非常有用。
在与antd等UI库集成时,路由表会非常有用。
目前,我们的路由匹配规则写在 App
里的,让我们回顾一下:
<Routes>
<Route path="/" element={<Home />} />
<Route path="/news">
<Route path="" element={<News />} />
<Route path="detail/:id" element={<NewsDetail />} />
</Route>
<Route path="/about" element={<About />} />
</Routes>
现在,我们要把这些规则用 JavaScript 的普通对象来表示,然后通过 useRoutes
把它们加载到 App
组件中。
编写路由表
// src/routeTable.jsx
import Home from './pages/Home';
import News from './pages/News';
import About from './pages/About';
import NewsDetail from './pages/News/Detail';
const routeTable = [
{
path: '/',
element: <Home />,
},
{
path: '/news',
children: [
{
path: '',
element: <News />,
},
{
path: 'detail/:id',
element: <NewsDetail />,
},
],
},
{
path: '/about',
element: <About />,
},
];
export default routeTable;
- 整个路由表是一个数组,用于代替
<Routes>
- 每个路由规则是一个对象,用于代替
<Route>
- 如果是嵌套路由,则将子路由放在
children
中
非常直观,和之前的直接以标签形式写的几乎是一一对应。
使用路由表
要想使用路由表,必须借助 useRoutes
hook:
// src/App.jsx
import React from 'react';
// import NavBarUsingLink from './components/NavBar/UsingLink';
import NavBarUsingNavLink from './components/NavBar/UsingNavLink';
import { useRoutes } from 'react-router-dom';
import routeTable from './routeTable';
function App() {
const routes = useRoutes(routeTable);
return (
<div>
<NavBarUsingNavLink />
{routes}
</div>
);
}
export default App;
import routeTable from './routeTable';
导入刚刚定义的路由表const routes = useRoutes(routeTable);
将路由表进行转换,并赋值给routes
变量- 在需要匹配路由的地方直接使用该变量:
{routes}