React 路由知识点汇总
安装
npm install react-router-dom localforage match-sorter sort-by -S
# 如果是ts项目 下面的需要安装
npm i --save-dev @types/sort-by新建路由目录
src
├── routes
│ ├── router.jsxrouter.jsx
import React from "react";
import { createBrowserRouter } from "react-router-dom";
const router = createBrowserRouter([
// 2. 路由配置
{
path: "/",
element: <h1>欢迎你,ReactRouter</h1>,
},
]);
export default router;引入 router
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { RouterProvider } from "react-router-dom";
import router from "./routes/router";
createRoot(document.getElementById("root")).render(
<RouterProvider router={router}></RouterProvider>,
);警告
这样运行起来就能看见欢迎你,ReactRouter
失败处理|嵌套路由|Link 组件
失败处理(自定义)
- 失败处理:当路由匹配不到任何路径时,会触发失败处理
src
├── error.jsx(1) 新建 error.jsx
function ErrorPage() {
return (
<div>
<h1>404 Not Found</h1>
</div>
);
}
export default ErrorPage;(2) 绑定到路由上面
- router.jsx 里面修改
import React from "react";
import { createBrowserRouter } from "react-router-dom";
import ErrorPage from "../error";
const router = createBrowserRouter([
// 2. 路由配置
{
path: "/",
element: <h1>欢迎你,ReactRouter</h1>,
errorElement: <ErrorPage></ErrorPage>,
},
]);
export default router;通配错误页面
import React from "react";
import { createBrowserRouter } from "react-router-dom";
import ErrorPage from "../error";
const router = createBrowserRouter([
// 2. 路由配置 放到最后一个,匹配不到就走这个
{
path: "/",
element: <h1>欢迎你,ReactRouter</h1>,
},
{
path: "*",
element: <ErrorPage></ErrorPage>,
},
]);
export default router;重定向
- 重定向:当路由匹配到某个路径时,会自动跳转到另一个路径
import React from "react";
import { createBrowserRouter, Navigate } from "react-router-dom";
import ErrorPage from "../error";
const router = createBrowserRouter([
// 2. 路由配置 放到最后一个,匹配不到就走这个
{
path: "/",
element: <h1>欢迎你,ReactRouter</h1>,
},
{
path: "/403",
element: <Navigate to="*" />,
},
{
path: "*",
element: <ErrorPage></ErrorPage>,
},
]);
export default router;嵌套路由
- 嵌套路由:路由里面嵌套路由,子路由
src
├── layOut
│ ├── Layout.jsx
├── views
│ ├── index.jsx
│ ├── Page1.jsx
│ ├── Page2.jsxLayOut.jsx
- Outlet 组件:用于渲染子路由
import { Outlet } from "react-router-dom";
function LayOut() {
return (
<div>
<h1>Layout</h1>
<p>这是Layout组件</p>
<div>-------下面是子路由----------</div>
<Outlet />
</div>
);
}
export default LayOut;index.jsx|Page1.jsx|Page2.jsx
- 默认页面
function IndexPage() {
return <div>默认页面</div>;
}
export default IndexPage;- Page1.jsx
function Page1() {
const gotourl = () => {
console.log("跳转到路由2");
};
const gotourlcanshu = () => {
console.log("携带参数跳转到路由2");
};
return (
<div>
路由1的页面
<button onClick={gotourl}>跳转到路由2</button>
<button onClick={gotourlcanshu}>携带参数跳转到路由2</button>
</div>
);
}
export default Page1;- Page2.jsx
function Page2() {
const gotourl = () => {
console.log("跳转到路由1");
};
const gotourlcanshu = () => {
console.log("携带参数跳转到路由1");
};
return (
<div>
路由1的页面
<button onClick={gotourl}>跳转到路由2</button>
<button onClick={gotourlcanshu}>携带参数跳转到路由2</button>
</div>
);
}
export default Page2;Router.jsx
修改 router.jsx
import React from "react";
import { createBrowserRouter } from "react-router-dom";
import ErrorPage from "../error";
import LayOut from "../layOut/Layout";
import Page1 from "../views/Page1";
import Page2 from "../views/Page2";
// 默认页面
import IndexPage from "../views/index";
const router = createBrowserRouter([
// 2. 路由配置
{
path: "/",
element: <LayOut></LayOut>,
errorElement: <ErrorPage></ErrorPage>,
children: [
{
index: true,
element: <IndexPage></IndexPage>,
},
{
path: "page1",
element: <Page1></Page1>,
},
{
path: "page2",
element: <Page2></Page2>,
},
],
},
]);
export default router;注意
增加一个属性index:true 表示默认子路由.访问 / 时,会渲染 IndexPage 组件.
跳转路由
Link 组件:用于跳转路由 但是获取不到样式
NavLink 组件:用于跳转路由 获取到样式,并且可以自定义样式
方法
Link
<Link to={`/page1`}>link跳转到page1</Link>NavLink
注意
NavLink 能判断当前路由是否是当前点击的路由,如果是,则添加 active 类名,否则添加 pending 类名
<NavLink
to={`/page1`}
className={({ isActive, isPending }) =>
isActive ? "active" : isPending ? "pending" : ""
}
>
NavLink点击我跳转
</NavLink>方法跳转
- useNavigate
// 跳转函数
const navigate = useNavigate();
const gotourl = () => {
console.log("跳转到路由2");
navigate(`/page2`);
};获取参数和路由状态
通过路由参数获取
警告
格式如下: xxx/:id
获取参数通过 useParams
- 新建一个路由
{
path: "/page2/:id",
element: <Page2></Page2>
},- 在组件中获取参数
import { useParams } from "react-router-dom";
function Page2() {
const Params = useParams();
console.log("*****");
console.log(Params.id);
console.log("*******");
}第二种通过参数来获取
警告
格式如下: xxx?id=xxx
获取参数通过 URL
- 获取参数
// 这样就能获取到了
const location = useLocation();
console.log(location);
const searchParams = new URLSearchParams(location.search);
const paramsObject = Object.fromEntries(searchParams.entries());
console.log(paramsObject);第三种 通过参数来获取
警告
格式如下: xxx?id=xxx
获取参数通过 URL
import { useSearchParams } from "react-router-dom";
const [searchParams] = useSearchParams();
console.log(searchParams);
const id = searchParams.get("id");路由跳转传递参数
在 React Router v6 中,可以使用 useNavigate 钩子实现编程式页面跳转,并在跳转时传递参数。主要有两种方式:state 状态参数(隐式) 和 URL 搜索参数(显式)。
引入并实例化 useNavigate
// 从 react-router-dom 中引入 useNavigate 钩子
import { useNavigate } from "react-router-dom";
function HomePage() {
// 实例化 navigate 函数,用于编程式跳转
const navigate = useNavigate();
return <div>首页</div>;
}方式一:state 状态参数传递(隐式传参)
参数不会显示在 URL 中,通过 useLocation 钩子获取。适合传递不希望暴露在地址栏的数据。
跳转发起页面:
import { useNavigate } from "react-router-dom";
export default function HomePage() {
// 实例化 navigate 跳转函数
const navigate = useNavigate();
// 点击按钮时触发跳转,通过 state 隐式携带 id 和 name 参数
const handleClick = () => {
navigate("/detail", {
state: {
id: 1001, // id:推荐 number 或 string 类型
name: "张三", // name:string 类型
},
});
};
return (
<div>
<h2>首页</h2>
{/* 点击按钮跳转到详情页并携带 state 参数 */}
<button onClick={handleClick}>跳转到详情页</button>
</div>
);
}目标页面接收参数:
// 引入 useLocation 钩子,用于读取路由 state
import { useLocation } from "react-router-dom";
export default function DetailPage() {
// 获取路由 location 对象,其中包含跳转时传递的 state
const location = useLocation();
// 从 state 中解构出 id 和 name;使用 || {} 兜底,防止直接访问页面时 state 为 null 报错
const { id, name } = location.state || {};
return (
<div>
<h2>详情页</h2>
<p>接收到的 id:{id}</p>
<p>接收到的 name:{name}</p>
</div>
);
}方式二:URL 搜索参数传递(显式传参)
参数拼接在 URL 中(如 /detail?id=1001&name=张三),通过 useSearchParams 钩子解析。适合需要分享、收藏链接的场景。
跳转发起页面:
import { useNavigate } from "react-router-dom";
export default function HomePage() {
// 实例化 navigate 跳转函数
const navigate = useNavigate();
// 点击按钮时触发跳转,将 id 和 name 拼接为 URL 查询参数
const handleClick = () => {
// 定义要传递的参数;id 推荐 number 或 string 类型,name 为 string 类型
const id = 1001;
const name = "张三";
// 拼接为查询字符串,中文字符会被自动编码
navigate(`/detail?id=${id}&name=${name}`);
};
return (
<div>
<h2>首页</h2>
{/* 点击按钮跳转到详情页并携带 URL 查询参数 */}
<button onClick={handleClick}>跳转到详情页</button>
</div>
);
}目标页面接收参数:
// 引入 useSearchParams 钩子,用于解析 URL 查询参数
import { useSearchParams } from "react-router-dom";
export default function DetailPage() {
// 获取搜索参数对象 searchParams(setSearchParams 未用到可不解构)
const [searchParams] = useSearchParams();
// 通过 get 方法读取指定参数;注意:读取到的值均为 string 类型
const id = searchParams.get("id"); // 得到字符串 "1001",如需数值需手动 Number(id)
const name = searchParams.get("name"); // 得到字符串 "张三"
return (
<div>
<h2>详情页</h2>
<p>接收到的 id:{id}</p>
<p>接收到的 name:{name}</p>
</div>
);
}参数安全注意事项
- 敏感参数(如密码、token、手机号等)不要通过 URL 搜索参数传递,因为参数会明文显示在地址栏、浏览器历史记录和服务器日志中,存在泄露风险,此类数据应使用 state 或其他安全方式传递。
useSearchParams读取到的参数均为字符串类型,即使传入的是数字也需自行转换类型。
参数类型约定
id:推荐使用number或string类型name:推荐使用string类型- 通过 state 传递时参数保持原始类型;通过 URL 搜索参数传递时会被序列化为字符串
路由状态
- 一般写在根组件中,作为判断 loading 是否完成的依据
警告
- 通过 useNavigation 来获取路由状态
- 判断是 状态是 loading 还是其他的,状态是 loading 就加载 loading 的类
export default function Root() {
// 路由信息对象
const navigation = useNavigation(); // 路由信息对象
return (
<div id="detail" className={navigation.sate == "loading" ? "loading" : ""}>
<Outlet />
</div>
);
}Loader 和 action
警告
loader 设计的理念是用户没进入路由之前,先获取到异步请求的数据,然后渲染到页面上
action 设计的理念是拦截用户通过 Form 组件包裹的数据转发到 action 上面
表单提交动作处理,不能是 get,只能是 post,del,put,它会通过 Form 拦截,找到下面的 action 属性来匹配,get 请求会触发 loader
loader
不获取参数
警告
页面里面暴露出 loader,必须用{}包裹
组件里面通过使用 useLoaderData 来获取数据
- page2.jsx
import { useLoaderData } from "react-router-dom";
export const loader = async () => {
// const contact = await xxxx() 你自己获取数据的方法
const contact = { name: "李四" };
console.log(contact);
return { contact }; // 返回联系人详细信息
};
function Page2() {
const { contact } = useLoaderData();
return (
<div>
路由2的页面
<div>{contact.name}</div>
</div>
);
}
export default Page2;- 绑定到 router 上面,配置 loader 属性
import React from "react";
import { createBrowserRouter } from "react-router-dom";
import ErrorPage from "../error";
import LayOut from "../layOut/Layout";
import Page1 from "../views/Page1";
import Page2, { loader as page2Loader } from "../views/Page2";
// 默认页面
import IndexPage from "../views/index";
const router = createBrowserRouter([
// 2. 路由配置
{
path: "/",
element: <LayOut></LayOut>,
errorElement: <ErrorPage></ErrorPage>,
children: [
{
index: true,
element: <IndexPage></IndexPage>,
},
{
path: "/page1",
element: <Page1></Page1>,
},
{
path: "/page2",
element: <Page2></Page2>,
loader: page2Loader,
},
],
},
]);
export default router;获取参数
- 创建新的路由
// 引入loader
import Page2, { loader as page2Loader } from "../views/Page2";
// 创建新的路由
{
path: "/page2/:id",
element: <Page2></Page2>,
loader: page2Loader,
},- 修改 loader
page2.jsx 修改 loader,通过 params 获取参数
export const loader = async ({ params }) => {
// const contact = await xxxx() 你自己获取数据的方法
if (params.id) {
console.log(params.id);
const contact = { name: `李四---${params.id}` };
console.log(contact);
return { contact };
} else {
const contact = { name: "李四" };
console.log(contact);
return { contact };
}
};- 组件渲染
function Page2() {
const { contact } = useLoaderData();
return (
<div>
路由2的页面
<div>{contact.name}</div>
</div>
);
}
export default Page2;action 用法
action 是拦截用户通过 Form 组件包裹的数据转发到 action 上面
创建 action
- page2.jsx 里面创建 action
export const action = async ({ request, params }) => {
const formData = await request.formData();
const updates = Object.fromEntries(formData);
console.log(updates); // 获取到你提交的数据
// await updateContact(params.contactId, updates); // 这里就是你发送走的数据
// return redirect(`/contacts/${params.contactId}`);
};组件里面使用
- 不加 action 的话 默认就是找的本页面的
<Form method="post">
<input type="text" name="name" />
<button type="submit">提交</button>
</Form>- 如果加 action 的 话 找的就是本页面+action 的
路径: /page2/edit
<Form method="post" action="edit">
<input type="text" name="name" />
<button type="submit">提交</button>
</Form>绑定到 router 上面
// 引入
import Page2, {
loader as page2Loader,
action as page2Action,
} from "../views/Page2";
// 路由里面加载
{
path: "/page2/:id",
element: <Page2></Page2>,
loader: page2Loader,
action: page2Action,
},路由懒加载
警告
懒加载必须使用异步函数 lazy 返回出 loader,action,Component
default 里面的名称 必须和下面的组件名称一致
修改 router
const router = createBrowserRouter([
{
path: "/",
element: <Root />,
// 进入路由器前面 就加载数据,然后在渲染数据
loader: rootloader,
// 表单提交动作处理,不能是get,只能是post,del,put,它会通过Form拦截,找到下面的action属性来匹配,get请求会触发loader
action: rootaction,
// 加载错误组件
errorElement: <ErrorPage />,
// 嵌套路由 还得在根组件里面加入Outlet
children: [
// 找到默认页面
{
index: true,
element: <Index></Index>,
},
{
path: "contacts/:contactId",
async lazy() {
const {
default: Contact,
loader,
action,
} = await import("../routes/contact");
return {
Component: Contact,
loader,
action,
};
},
},
{
path: "contacts/:contactId/edit",
async lazy() {
const { default: EditCom, action } = await import("./edit");
// 因为这里引用了别的组件的logo
const { loader } = await import("../routes/contact");
return {
Component: EditCom,
loader,
action,
};
},
},
{
path: "contacts/:contactId/destroy",
element: <EditComponent />,
action: destroyAction,
// 单独配置错误信息,局部优先于全局
errorElement: <h1>删除失败</h1>,
},
],
},
]);