前端路由概念
前端路由是指在单页面应用中,通过改变 URL 来改变页面内容的一种技术。
早期的时候并不存在前端路由,那个时候只有后端路由,类似于下图:
![image-20221101200014494](https://i-blog.csdnimg.cn/blog_migrate/0da66b82c8f1702c2af8d80556da266d.png)
每次点击链接或者提交表单都会向服务器发送请求,服务器返回新的 HTML 页面,然后浏览器重新加载整个页面。
后来,随着单页应用的流行,整个 Web 应用之存在一个页面,通过 JS 调整模块来显示不同的内容,类似于下图:
![image-20221101200407424](https://i-blog.csdnimg.cn/blog_migrate/b4f3c381925ee990fa8421e3573deb72.png)
在单页面应用中,页面只加载一次,所有的操作都是在页面上动态加载内容并改变 URL。所谓前端路由实际上就是协调当前页面显示什么模块。
那么单页应用时代,还存在后端路由么?
实际上也是存在的,后端路由负责返回对应的数据,如下图:
![image-20221101200622242](https://i-blog.csdnimg.cn/blog_migrate/5dde60b513335db0a627f4b184e166cf.png)
后端路由主要用于处理URL的请求,将不同的URL请求映射到不同的逻辑处理函数上。在单页面应用中,后端路由可以用于实现以下功能:
-
服务器端渲染:单页面应用通常是在浏览器端通过JavaScript动态地生成页面内容。但是,有些情况下需要在服务器端生成页面内容,例如搜索引擎的爬虫需要获取完整的页面内容进行索引。后端路由可以用于将URL请求映射到服务器端的渲染函数上,从而在服务器端生成页面内容。
-
数据预取:单页面应用通常需要从服务器端获取数据,并在页面加载完成后显示。后端路由可以用于将URL请求映射到服务器端的数据获取函数上,从而在页面加载之前就预先获取数据。
-
服务端认证和权限控制:在单页面应用中,用户认证和权限控制通常是由服务器端处理的。后端路由可以用于将URL请求映射到服务器端的认证和权限控制函数上,从而对用户进行认证和权限验证。
-
URL参数处理:在单页面应用中,通常需要从URL中获取参数,例如页面的过滤条件、排序方式等。后端路由可以用于将URL请求映射到服务器端的参数处理函数上,从而解析URL参数并进行相应的处理。
React-router
React-router 是 React 官方所推出的前端路由库,官网地址:https://reactrouter.com/en/main
目前最新的版本为 v6 版本。相比之前的版本,该版本变化略大,加入了许多新的 Hook,比如 useRoutes 这个 Hook 就提供了类似于 Vue-router 相似的特性,从而使得使用起来更加的方便。
整个官网可以分为几大块:
- Components 组件
- Hooks 函数
- API 函数
为了更好更快的了解使用react-router,我们将按照功能讲解。本章节不会包含全部的API,更多的使用方法参考官网
不同的历史模式
Hash 模式
hash路由(hash-based routing)是一种在URL中使用哈希(#)来实现路由的方式。
hash 路由可以使用以下方式创建的:
- createHashRouter API
- HashRouter component
createHashRouter API
createHashRouter API 类似于 vue-router v4 创建路由的方式。在函数调用时,可以将项目中的路由配置好。
createHashRouter 返回值不能直接渲染,需要通过 RouterProvider 渲染
以下是关于两种方式的使用实例:
import * as React from "react";
import * as ReactDOM from "react-dom";
import {
createHashRouter,
RouterProvider,
} from "react-router-dom";
import Root, { rootLoader } from "./routes/root";
import Team, { teamLoader } from "./routes/team";
const router = createHashRouter([
{
path: "/",
element: <Root />,
loader: rootLoader,
children: [
{
path: "team",
element: <Team />,
loader: teamLoader,
},
],
},
]);
ReactDOM.createRoot(document.getElementById("root")).render(
<RouterProvider router={router} />
);
HashRouter component
以HashRouter方式创建 hash 路由, 需要在根组件 App 外面包一层 HashRouter 组件
import * as React from "react";
import * as ReactDOM from "react-dom";
import { HashRouter } from "react-router-dom";
ReactDOM.render(
<HashRouter>
<App />
</HashRouter>,
root
);
HTML5 模式
HTML5 模式的路由是一种网页导航方式,它使用 HTML5 History API 来实现无刷新的页面跳转和状态管理。
HTML5 模式的路由使用普通的 URL,例如 https://example.com/about
,这样的 URL 更加友好和直观。当用户点击网页中的链接时,浏览器不会刷新整个页面,而是通过 JavaScript 监听 URL 的变化,并根据新的 URL 加载对应的页面内容。
HTML5 模式可以通过以下方式创建
- createBrowserRouter Api
- BrowserRouter component
以下是关于两种方式的使用实例:
createBrowserRouter Api
import * as React from "react";
import * as ReactDOM from "react-dom";
import {
createBrowserRouter,
RouterProvider,
} from "react-router-dom";
import Root, { rootLoader } from "./routes/root";
import Team, { teamLoader } from "./routes/team";
const router = createBrowserRouter([
{
path: "/",
element: <Root />,
loader: rootLoader,
children: [
{
path: "team",
element: <Team />,
loader: teamLoader,
},
],
},
]);
ReactDOM.createRoot(document.getElementById("root")).render(
<RouterProvider router={router} />
);
BrowserRouter component
import * as React from "react";
import { createRoot } from "react-dom/client";
import { BrowserRouter } from "react-router-dom";
const root = createRoot(document.getElementById("root"));
root.render(
<BrowserRouter>
{/* The rest of your app goes here */}
</BrowserRouter>
);
如何配置路由
- 通过 createHashRouter Api 或者 createBrowserRouter Api 创建的路由模式,可以在 Api 使用时,直接配置。例如:
const router = createBrowserRouter([
{
path: "/",
element: <Root />,
loader: rootLoader,
children: [
{
path: "team",
element: <Team />,
loader: teamLoader,
},
],
},
]);
- 通过 HashRouter component 或者 BrowserRouter component 方式创建的路由可以通过 Routes 组件 或者 useRoutes 配置。例如:
Routes
<Routes>
<Route path="/" element={<Dashboard />}>
<Route
path="messages"
element={<DashboardMessages />}
/>
<Route path="tasks" element={<DashboardTasks />} />
</Route>
<Route path="about" element={<AboutPage />} />
</Routes>
useRoutes
import * as React from "react";
import { useRoutes } from "react-router-dom";
function App() {
let element = useRoutes([
{
path: "/",
element: <Dashboard />,
children: [
{
path: "messages",
element: <DashboardMessages />,
},
{ path: "tasks", element: <DashboardTasks /> },
],
},
{ path: "team", element: <AboutPage /> },
]);
return element;
}
嵌套路由
一些应用程序的 UI 由多层嵌套的组件组成。在这种情况下,URL 的片段通常对应于特定的嵌套组件结构,例如:
const router = createBrowserRouter([
{
path: "/",
element: <Root />,
loader: rootLoader,
children: [
{
path: "team",
element: <Team />,
loader: teamLoader,
},
],
},
]);
url /team
是 url /
的子路由,如果想要 Root 组件中显示 子路由的内容,需要在 Root 组件中 使用 Outlet, 例如:
function Root() {
return (
<Outlet />
)
}
声明式导航
声明式导航有以下几种方式
在 React Router v6 中,你可以使用以下几种方式进行声明式导航:
- 使用
<Link>
组件:<Link>
组件是 React Router 提供的一种声明式导航方式。你可以在应用中的任何地方使用它,通过设置to
属性指定导航目标。例如:
import { Link } from 'react-router-dom';
function MyComponent() {
return (
<div>
<Link to="/home">Home</Link>
<Link to="/about">About</Link>
</div>
);
}
- 使用
<NavLink>
组件:<NavLink>
组件是<Link>
的扩展版本,它支持在当前路径匹配时添加特定的样式或类名。可以使用activeClassName
或activeStyle
属性设置当前活动路径的样式。例如:
import { NavLink } from 'react-router-dom';
function MyComponent() {
return (
<div>
<NavLink to="/home" activeClassName="active">Home</NavLink>
<NavLink to="/about" activeClassName="active">About</NavLink>
</div>
);
}
- 使用
useNavigate
钩子:React Router v6 引入了新的 hookuseNavigate
,它返回一个可用于导航的函数。你可以在组件中使用useNavigate
钩子,然后在需要导航的地方调用它。例如:
import { useNavigate } from 'react-router-dom';
function MyComponent() {
const navigate = useNavigate();
const handleClick = () => {
navigate('/home');
}
return (
<div>
<button onClick={handleClick}>Go to Home</button>
</div>
);
}
这些是 React Router v6 中的几种声明式导航方式。你可以根据项目需求选择适合你的方式。在 React Router v6 中,你可以使用以下几种方式进行声明式导航:
- 使用
<Link>
组件:<Link>
组件是 React Router 提供的一种声明式导航方式。你可以在应用中的任何地方使用它,通过设置to
属性指定导航目标。例如:
import { Link } from 'react-router-dom';
function MyComponent() {
return (
<div>
<Link to="/home">Home</Link>
<Link to="/about">About</Link>
</div>
);
}
- 使用
<NavLink>
组件:<NavLink>
组件是<Link>
的扩展版本,它支持在当前路径匹配时添加特定的样式或类名。可以使用activeClassName
或activeStyle
属性设置当前活动路径的样式。例如:
import { NavLink } from 'react-router-dom';
function MyComponent() {
return (
<div>
<NavLink to="/home" activeClassName="active">Home</NavLink>
<NavLink to="/about" activeClassName="active">About</NavLink>
</div>
);
}
- 使用
useNavigate
钩子:React Router v6 引入了新的 hookuseNavigate
,它返回一个可用于导航的函数。你可以在组件中使用useNavigate
钩子,然后在需要导航的地方调用它。例如:
import { useNavigate } from 'react-router-dom';
function MyComponent() {
const navigate = useNavigate();
const handleClick = () => {
navigate('/home');
}
return (
<div>
<button onClick={handleClick}>Go to Home</button>
</div>
);
}
这些是 React Router v6 中的几种声明式导航方式。你可以根据项目需求选择适合你的方式。在 React Router v6 中,你可以使用以下几种方式进行声明式导航:
- 使用
<Link>
组件:<Link>
组件是 React Router 提供的一种声明式导航方式。你可以在应用中的任何地方使用它,通过设置to
属性指定导航目标。例如:
import { Link } from 'react-router-dom';
function MyComponent() {
return (
<div>
<Link to="/home">Home</Link>
<Link to="/about">About</Link>
</div>
);
}
- 使用
<NavLink>
组件:<NavLink>
组件是<Link>
的扩展版本,它支持在当前路径匹配时添加特定的样式或类名。可以使用activeClassName
或activeStyle
属性设置当前活动路径的样式。例如:
import { NavLink } from 'react-router-dom';
function MyComponent() {
return (
<div>
<NavLink to="/home" activeClassName="active">Home</NavLink>
<NavLink to="/about" activeClassName="active">About</NavLink>
</div>
);
}
- 使用
useNavigate
钩子:React Router v6 引入了新的 hookuseNavigate
,它返回一个可用于导航的函数。你可以在组件中使用useNavigate
钩子,然后在需要导航的地方调用它。例如:
import { useNavigate } from 'react-router-dom';
function MyComponent() {
const navigate = useNavigate();
const handleClick = () => {
navigate('/home');
}
return (
<div>
<button onClick={handleClick}>Go to Home</button>
</div>
);
}
这些是 React Router v6 中的几种声明式导航方式。你可以根据项目需求选择适合你的方式。在 React Router v6 中,你可以使用以下几种方式进行声明式导航:
- 使用
<Link>
组件:<Link>
组件是 React Router 提供的一种声明式导航方式。你可以在应用中的任何地方使用它,通过设置to
属性指定导航目标。例如:
import { Link } from 'react-router-dom';
function MyComponent() {
return (
<div>
<Link to="/home">Home</Link>
<Link to="/about">About</Link>
</div>
);
}
- 使用
<NavLink>
组件:<NavLink>
组件是<Link>
的扩展版本,它支持在当前路径匹配时添加特定的样式或类名。可以使用activeClassName
或activeStyle
属性设置当前活动路径的样式。例如:
import { NavLink } from 'react-router-dom';
function MyComponent() {
return (
<div>
<NavLink to="/home" activeClassName="active">Home</NavLink>
<NavLink to="/about" activeClassName="active">About</NavLink>
</div>
);
}
编程式导航
使用 useNavigate Hook, 返回值是一个函数 可以接收两个参数navigate(参数一, 参数二)
- 参数一: 可以是 url 或者在历史堆栈中传递您想要的增量,例如返回上一页
navigate(-1)
, 例如:
import { useNavigate } from 'react-router-dom';
const navigate = useNavigate();
// 在点击或触发某个事件时进行导航
const handleNavigation = () => {
navigate('/path');
}
// 返回上一页
const backPage = () => {
navigate(-1)
}
- 参数二: 是一个配置项 options,有以下配置
- options.replace:
replace: true
将导致导航替换历史堆栈中的当前url - options.state: 包含一个可选的状态值来存储在历史状态中,然后可以通过useLocation在目标路由上访问它。例如:
navigate("/new-route", { state: { key: "value" } });
- 更多用法参考官网
路由重定向
重定向也是通过 routes 配置来完成,下面例子是从 /home 重定向到 /:
import {Navigate} from "react-router-dom";
{ path: "/home", element: <Navigate to="/" />}
动态路由匹配
如果一个路径段以:开头,那么它就变成了一个“动态段”。当路由匹配URL时,动态段将从URL中解析出来,并作为参数提供给其他路由器api。例如:
{ path: "/user:id", element: <User />}
如何获取动态段的信息,可以通过 useParams() 获取, 例如:
function User() {
const { id } = useParams();
}
路由懒加载
当打包构建应用时,JavaScript 包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就会更加高效。例如:
const router = createBrowserRouter([
{
path: "/",
element: <Root />,
children: [
{
path: '',
lazy: () => import('./Page1'),
},
{
path: "team",
lazy: () => import('./Page2')
},
],
},
]);