路由
react 前端路由的本质是路径和组件的一一对应关系, 即不同的 path 对应不同的 component, 访问不同路径时渲染对应的组件.
安装
npm install --save react-router-dom
快速使用
-
引入 createBrowserRouter, RouterProvider
-
createBrowserRouter: 创建路由实例, 在方法中定义路由 path 和组件的对应关系
-
RouterProvider: 作为一个组件渲染, 并且传入 createBrowserRouter 方法执行后生成的 router 实例
-
-
调用 createBrowserRouter, 生成 router 实例
-
渲染 RouterProvider 组件并传入 router 实例
src/index.js
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import { createBrowserRouter, RouterProvider } from "react-router-dom";
const router = createBrowserRouter([
{
path: "/",
element: <div>this is home</div>,
},
{
path: "/login",
element: <div>this is login</div>,
},
]);
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<RouterProvider router={router} />
</React.StrictMode>
);
npm start 后在浏览器输入 localhost:3000/ 和 localhost:3000/login 即可看到效果
抽离单独组件和路由独立文件
目录结构 tree -I node_modules
.
├── package.json
├── package-lock.json
├── public
│ ├── favicon.ico
│ └── index.html
├── README.md
└── src
├── index.js
├── page
│ ├── About.jsx
│ ├── Home.jsx
│ └── Login.jsx
└── router
└── index.jsx
src/index.js
import React from "react";
import ReactDOM from "react-dom/client";
import { RouterProvider } from "react-router-dom";
import router from "./router"; // 引入 router 路由组件
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<RouterProvider router={router} />
</React.StrictMode>
);
src/router/index.jsx
// 配置路由对应关系
import { createBrowserRouter } from "react-router-dom";
import Home from "../page/Home";
import Login from "../page/Login";
import About from "../page/About";
const router = createBrowserRouter([
{
path: "/",
element: <Home />,
},
{
path: "/login",
element: <Login />,
},
{
path: "/about",
element: <About />,
},
]);
export default router;
src/page/Home.jsx
const Home = () => {
return <div>this is home</div>;
};
export default Home;
两种路由模式
| react 函数 | 对应的路由模式 | url 表现 | 底层原理 | 是否需要后端支持 | 兼容性 |
|---|---|---|---|---|---|
| createBrowerRouter | history | url/login | history 对象 + pushState | 需要 | ie10 |
| createHashRouter | hash | url/#/login | 监听 hashchange 事件 | 不需要 | ie8 |
src/router/index.jsx
// 引入 createHashRouter
import { createBrowserRouter, createHashRouter } from "react-router-dom";
import Home from "../page/Home";
import Login from "../page/Login";
import About from "../page/About";
// hash 模式
const router = createHashRouter([
{
path: "/",
element: <Home />,
},
{
path: "/login",
element: <Login />,
},
{
path: "/about",
element: <About />,
},
]);
export default router;
url 从 localhost:3000/login 变为 localhost:3000/#/login
编程式导航
通过 js 编程的方式进行路由页面跳转, 比如从首页跳转到登陆页
import { useNavigate } from "react-router-dom";
const Login = () => {
// 执行 useNavigate, 得到跳转函数
const navigate = useNavigate();
const goToAbout = () => {
// 执行跳转函数
navigate("/about");
};
return (
<div>
this is login
<button onClick={goToAbout}>go to about</button>
</div>
);
};
export default Login;
在 localhost:3000/#/login 点击 goToAbout 按钮即可跳转到 localhost:3000/#/about
这是叠加状态, 即 /about 页面叠加在 /login
如果在跳转时, 想要替换记录, 可以在 navigate 跳转函数的第二个参数加上 {replace: true}
const goToAbout = () => {
navigate("/about", { replace: true });
};
这时候 localhost:3000/#/login 点击 goToAbout 跳转到 localhost:3000/#/about,
但是在浏览器的后退按钮时, 会回到 localhost:3000 而不是 localhost:3000/#/login
即 /about 页面替换了 /login 页面
路由跳转传参
-
searchParams 传参
scr/page/Login.jsx
import { useNavigate } from "react-router-dom"; const Login = () => { const navigate = useNavigate(); const goToAbout = () => { // 路由传参方式一: searchParams // 在跳转路由时拼接参数 navigate("/about?id=1001"); }; return ( <div> this is login <button onClick={goToAbout}>go to about</button> </div> ); }; export default Login;登陆
localhost:3000/login页面后, 点击go to about按钮, 会跳转到localhost:3000/about?id=1001在 About 组件里获取参数
src/page/About.jsx
// 导入 useSearchParam 函数 import { useSearchParams } from "react-router-dom"; const About = () => { // 获取通过 searchParams 传参过来的 id 参数 const [params] = useSearchParams(); const id = params.get("id"); return <div>this is about {id}</div>; }; export default About;传递多个参数
src/page/Login.jsx
import { useNavigate } from "react-router-dom"; const Login = () => { const navigate = useNavigate(); const goToAbout = () => { navigate("/about?id=1001&name=pyq"); }; return ( <div> this is login <button onClick={goToAbout}>go to about</button> </div> ); }; export default Login;src/page/About.jsx
import { useSearchParams } from "react-router-dom"; const About = () => { const [params] = useSearchParams(); const id = params.get("id"); const name = params.get("name"); return ( <div> this is about {id} {name} </div> ); }; export default About; -
params 传参
scr/router/index.jsx
import { createBrowserRouter, createHashRouter } from "react-router-dom"; import Home from "../page/Home"; import Login from "../page/Login"; import About from "../page/About"; const router = createBrowserRouter([ { path: "/", element: <Home />, }, { path: "/login", element: <Login />, }, { path: "/about/:id", // 首先用 id 占位符 element: <About />, }, ]); export default router;src/page/Login.jsx
import { useNavigate } from "react-router-dom"; const Login = () => { const navigate = useNavigate(); const goToAbout = () => { navigate("/about/1001"); // 在跳转时, 加上参数 }; return ( <div> this is login <button onClick={goToAbout}>go to about</button> </div> ); }; export default Login;scr/page/About.jsx
import { useParams } from "react-router-dom"; const About = () => { // 接受时, 使用 useParams const params = useParams(); return <div>this is about {params.id}</div>; // 也可以 const { id } = useParams(); // const { id } = useParams(); // return <div>this is about {id}</div>; }; export default About;
嵌套路由
如果当前组件的内容, 是在另一个组件模板中的局部进行切换, 那它就是一个嵌套路由
在路由表中, 通过 children 属性进行二级路由配置
通过内置组件 Outlet 渲染二级路由组件
使用内置组件 Link 进行声明式导航配置
scr/router/index.jsx
import { createBrowserRouter } from "react-router-dom";
import Layout from "../page/Layout";
import Login from "../page/Login";
import About from "../page/About";
import Article from "../page/Article";
import Board from "../page/Board";
const router = createBrowserRouter([
{
path: "/",
element: <Layout />,
// 二级路由配置
children: [
{
path: "article",
element: <Article />,
},
{
path: "board",
element: <Board />,
},
],
},
{
path: "/login",
element: <Login />,
},
{
path: "/about/:id",
element: <About />,
},
]);
export default router;
src/page/Layout.jsx
import { Link, Outlet } from "react-router-dom";
const Layout = () => {
return (
<div>
这是一级路由 layout 对应的模板
<div>
<p>
<Link to="/board">看板</Link>
<Link to="/article">文章</Link>
</p>
</div>
<div>
<p>这里是二级路由的出口位置</p>
<Outlet />
</div>
</div>
);
};
export default Layout;
默认二级路由渲染
如果没有设置默认的二级路由
localhost:3000 不会渲染二级路由的组件,
只有访问 localhost:3000/board, localhost:3000/article 才会渲染二级路由组件
设置了默认的二级路由就能实现 localhost:3000 直接渲染二级组件
src/router/index.jsx
import { createBrowserRouter } from "react-router-dom";
import Layout from "../page/Layout";
import Login from "../page/Login";
import About from "../page/About";
import Article from "../page/Article";
import Board from "../page/Board";
const router = createBrowserRouter([
{
path: "/",
element: <Layout />,
children: [
{
path: "article",
element: <Article />,
},
{
// 删除 path: "/board"
// 加上 index: true
// 将 Board 设置为默认的二级路由组件
index: true,
element: <Board />,
},
],
},
{
path: "/login",
element: <Login />,
},
{
path: "/about/:id",
element: <About />,
},
]);
export default router;
src/page/Layout.jsx
import { Link, Outlet } from "react-router-dom";
const Layout = () => {
return (
<div>
这是一级路由 layout 对应的模板
<div>
<p>
{/* 将 Board 的路径改为 /, 因为这是默认的二级路由组件 */}
<Link to="/">看板</Link>
<Link to="/article">文章</Link>
</p>
</div>
<div>
<p>这里是二级路由的出口位置</p>
<Outlet />
</div>
</div>
);
};
export default Layout;
404 页面配置
src/router/index.jsx
import { createBrowserRouter } from "react-router-dom";
// 省略
import NotFound from "../page/NotFound";
const router = createBrowserRouter([
// 省略
{
path: "*",
element: <NotFound />,
},
]);
export default router;
src/page/NotFound.jsx
import { Link } from "react-router-dom";
const NotFound = () => {
return (
<div>
你访问的页面飞往了月球
<Link to="/">回到首页</Link>
</div>
);
};
export default NotFound;