React 入门 02:从单页面应用到多页面应用
本文主要介绍如何使用路由控制来实现将一个单页面网站扩展成多页面网站,包括页面扩展的逻辑,React生态中最流行的路由框架react-router的基本用法以及扩展用法
文章目录
- 一、场景说明
- 二、基本的页面扩展
- 页面扩展是在扩什么
- 三、路由用法扩展:更多定制化的路由配置场景
- 动态路由实现详情页展示
- 四、控制路由跳转的API
一、场景说明
我们在进行网站开发的时候,大多数都是需要有多个页面展示不同内容或者提供不同功能。每个页面单独启动一个项目的做法不太现实,实际业务中是一个项目中,一个页面对应项目代码中一个代码文件,然后通过浏览器地址的不同后缀(比如/home, /about, /prodcut, /center等等)来对应到不同的代码文件,进而在浏览器上展示不同的内容和提供不同的功能。
二、基本的页面扩展
页面扩展是在扩什么
在前文介绍如何创建一个React项目并且修改页面内容时,我们已经探索到了页面渲染的逻辑,即:
- 浏览器根据地址栏输入的地址,可以访问到我们启动的React项目
- React项目会获取HTML页面内容中的body区域,然后将我们的应用内容通过React的渲染函数渲染到HTML中
- 页面展示填充了具体应用内容的完整HTML
就完成了从代码到浏览器内容的关联和渲染展示。现在我们希望除了最初的App.jsx页面之外,我们还可以有更多的页面,并且通过浏览器地址栏不同的后缀来显示不同的页面。那么根据页面渲染是一种对应关系,一个地址栏会对应到一个代码文件(比如"http://localhost:5173/"对应到App.jsx),添加一个新的页面文件(比如About.jsx)是很简单的,我们需要解决的是,如何拿到地址栏的路径,能够在项目中加入一个类似if-else的操作,让不同路径对应到不同的页面代码文件。(当前不管我们在地址栏输入什么后缀,都是显示的App.jsx)
这就需要往React项目里增加路由模块了,我们接下来以最流行的react-router来实现多页面扩展这件事。
 首先,通过如下命令安装react-router到我们的项目中:
 npm i react-router
然后在我们的入口文件main.jsx中将路由模块导入进来,并嵌入我们的应用中,如下:
 
 我们通过import { BrowserRouter, Routes, Route } from "react-router"; 来导入react-router提供的路由功能模块,从名称可以看出
- BrowserRouter的作用就是一个浏览器的路由器,也就是负责处理我们希望的从一个地址栏链接对应到一个代码文件的路由任务。
- Routes用来渲染或者说管理一组- Router,将当前地址栏的路径匹配到对应的- Router上
- Router用来写明我们希望的- 地址栏路径与- 代码文件之间的对应关系
于是经过上述配置后,对于根路径/,就是对应到App.jsx。可以额外试试其他路径,在配置之前我们输入任何路径都会显示App页面的内容,但是显示如果我们输入一个不存在的路径(比如/about),可以在【F12 - console】中看到如下信息提示没有找到匹配的路由:
 
 这说明我们的项目中其实已经有了路由功能,现在路由功能模块找不到路径/about应该对应到哪一块代码上,就会不显示页面内容并且输出如上提示。
接下来我们添加一些页面代码内容给/about,看是否会正常显示。修改如下:
 
 然后回到浏览器中,可以看到/about页面当前显示的内容如下:
 
 说明我们的修改确实生效了。/about路由按照我们期望地指向了<h1>Here is About Page</h1>。如果我们希望展示更多内容,可以像App.jsx一样单独新建一个代码文件,然后将页面内容写到代码文件里,再导入到main.jsx中使用。
现在我们可以很清楚,当我们要增加一个新的页面的时候,其实就是新增一个从代码到浏览器地址的关联关系。我们具体要做的就是:增加一个页面,然后增加一个浏览器地址路径到具体页面代码文件的对应关系
三、路由用法扩展:更多定制化的路由配置场景
动态路由实现详情页展示
我们在实际开发过程中,一个页面展示的内容布局是不变的,只是页面的数据不同而已,比如:
- 博客网站的博文详情页
- 电商网站的商品详情页
- 视频网站的视频播放页
- …
关于详情页这种只是数据变化的页面,我们的实现方法一般是拿到数据之后将数据展示到对应的位置上。至于数据可以是在页面跳转前将数据传入,常见于我们说的MVC架构中;也可以是从网页请求中获取对应的数据参数,然后进行数据请求得到数据,常见于前后端分离的架构。
如果我们是从网页请求中获取的数据参数,则我们可以常见到如下两种请求方式:
- 参数作为url请求传入:http://localhost:8080/user?user_id=1
- restful的方式,作为路由的一部分,由路由逻辑按照指定模式去匹配获取:http://localhost:8080/user/1
如果要实现http://localhost:8080/user/1这种方式,我们可以可以使用react-router的动态路由参数,将路由配置修改如下:
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import { BrowserRouter, Routes, Route } from "react-router";
import './index.css'
import App from './App.jsx'
import Team from './Team.jsx';createRoot(document.getElementById('root')).render(<StrictMode><BrowserRouter><Routes><Route path="/" element={<App />} /><Route path="/team/:teamName" element={<Team />} /></Routes></BrowserRouter></StrictMode>
)
新增的Team.jsx代码文件内容如下:
import { useParams } from "react-router";export default function Team() {// 通过 useParams 获取路由参数// 外部路由设置了 /team/:teamName, 那么就可以得到 teamName 的值let params = useParams();return <h1>Here is Team Page About<h2>{params.teamName}</h2></h1>
}
于是回到浏览器页面,我们可以看到浏览器地址栏和页面内容对应关系对下:
 
 页面代码成功获取了地址栏中的团队名称并且显示在了页面内容中。
除此之外,还有一些其他的常见用法:
- 不同路径指向相同页面:
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import { BrowserRouter, Routes, Route } from "react-router";
import './index.css'
import App from './App.jsx'createRoot(document.getElementById('root')).render(<StrictMode><BrowserRouter><Routes><Route path="/" element={<App />} /><Route path="/home" element={<App />} /><Route path="/index" element={<App />} /></Routes></BrowserRouter></StrictMode>
)- 嵌套路由: - 只替换某些部分的内容,公共内容不变,减少重复代码
- /dashboard/profile:显示用户个人信息
- /dashbord/settings:显示用户设置
 
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import { BrowserRouter, Routes, Route } from "react-router";
import './index.css'
import App from './App.jsx'
import Dashboard from './Dashboard.jsx';createRoot(document.getElementById('root')).render(<StrictMode><BrowserRouter><Routes><Route path="dashboard" element={<Dashboard />}><Route index element={<h1>Content: Home</h1>} />{/* 等价写法:<Route path="" element={<h1>Content: Home</h1>} /> */}<Route path="settings" element={<h1>Content: Setting</h1>} /></Route></Routes></BrowserRouter></StrictMode>
)
公共部分的Dashboard.jsx文件内容如下:
import { Outlet } from "react-router";export default function Dashboard() {return (<div><h1>Common Title: Dashboard</h1>{/* 渲染时 <Outlet /> 会被替换成 Home 对应的组件内容或者 Settings 对应的组件内容 */}<Outlet /></div>);
}
- 懒加载: - 功能:只有当路由被访问(即用户第一次访问对应页面)的时候才加载对应页面组件
- 优势:防止页面加载过慢以及构建应用时打包的js包过大
- 写法: - 不是懒加载的写法:import App from './App.jsx'
- 懒加载写法:const App = lazy(() => import("./App"));(需要先导入lazy方法:import { lazy } from 'react';)
 
- 不是懒加载的写法:
 
四、控制路由跳转的API
我们也可以通过在组件中调用Router API来实现路由的跳转、回退等功能。如下:
import { useNavigate } from "react-router";function SomeComponent() {let navigate = useNavigate();return (<button onClick={() => navigate(-1)}>Go Back</button>);
}
上面的代码中我们通过react-router提供的useNavigate获取到了网站路由的导航器navigate(这是一个方法),通过调用这个方法就可以实现对路由的精准控制,传入的参数可以是指定的路由/about,也可以是距离(比如-1代表回退一个页面,1代表前进一个页面)
其他的路由控制还有:
- 带参数跳转: - navigate("/some/route?search=keyword");
- 获取参数: - import { useSearchParams } from "react-router";
- const [searchParams, setSearchParams] = useSearchParams();
- searchParams.get("search") // 得到值:keyword
 
 
- ……
掌握了以上内容基本就可以解决实际业务开发中的大部分场景,第四部分的API不需要记忆,只需要记住「我们有办法能够通过代码逻辑来控制路由」即可。
快去试试搭建自己的多页面网站吧~
