React学习系列(二):React Router 路由跳转

288 阅读4分钟

1.  路由

1.1 路由渲染

一般路由的使用流程

/src/routes/index.js

    importNavigate } from 'react-router-dom'
    import About from '../components/About'
     import Home from '../components/Home'  
     const routes = [{     
     path: '/about',     
     element: <About /> }, 
     {     
     path: '/home',     
     element: <Home /> }, 
     {    
      path: '/',     
      element: <Navigate to='/about' /> }
      ]  
      export default routes

App.js

    import React from "react"; 
    importNavLink, useRoutes } from "react-router-dom";  //引入
    import routes from "./routes";

    export default function App() {       
    const elements = useRoutes(routes)      //初始化
    return (
         <div>
             {/* 注册路由 */}
             {elements}
         </div>
    )

● 嵌套路由

image.png

/src/routes/index.js

image.png

子路由 NavLink 的 to 可以像原来一样写全 path:to="/home/news"

 也可以直接 ./ + 子路由名:to="./news" 

最简单直接写子路由名:to="news" 

子路由组件呈现的位置直接使用 6 提供的  标签声明即可,会自动分配子路由管理

/Home/index.js

image.png

react router6 (vite创建react项目,实际项目使用)

使用 createBrowserRouter,RouterProvider

// src/routes/index.js 
    
import React from "react"
// AliveScope:react 实现keep-alive功能。将渲染过的组件(div.ka-content)存起来,等到再次用,从缓存拿到,使用js的 appendchild 到 KeepAlive中的 (div.ka-wrapper)
import { AliveScope } from "react-activation";
import { Navigate } from "react-router-dom";
import Layout from "../layouts";
import HomePage from "../pages/HomePage";
import Backlog from "../pages/Backlog";
import Personal from "../pages/Profile/Personal";
import Changepwd from "../pages/Profile/Changepwd";

export const rootRouter: any = [
  {
    path: "/",
    exact: true,
    element: (
      <AliveScope>
        <Layout />
      </AliveScope>
    ),
    children: [
      { path: "", element: <Navigate to="/homepage" /> },
      {
        path: "/homepage",
        exact: true,
        element: <HomePage />,
      },
      {
        path: "/backlog",
        exact: true,
        element: <Backlog />,
      },
      {
        path: "/profile/personal",
        exact: true,
        element: <Personal />,
      },
      {
        path: "/profile/changepwd",
        exact: true,
        element: <Changepwd />,
      },
    ],
  },
];

    
 // src/container/App.tsx
    
import { createBrowserRouter, RouterProvider } from "react-router-dom";
import { rootRouter } from "../routers";
function App() {
  const element = createBrowserRouter(rootRouter);

  return (
    <>
      <RouterProvider router={element} />
    </>
  );
}

export default App;

    
 // src/container/Main.tsx
    
import { observer } from "mobx-react";
import { App as AntdApp, ConfigProvider, theme } from "antd";
import zhCN from "antd/locale/zh_CN";
import App from "./App.tsx";

function Main() {
  return (
    <ConfigProvider
      locale={zhCN}
      prefixCls=""
      theme={{
        // algorithm: theme.defaultAlgorithm,
        // token: {
        //   colorPrimary: "#00b96b",
        //   borderRadius: 2,
        // },
        components: {
          Button: {
            colorPrimary: "pink",
          },
        },
      }}
    >
      <AntdApp>
        <App />
      </AntdApp>
    </ConfigProvider>
  );
}

export default observer(Main);
    
 // src/container/Root.tsx
    
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.tsx";

ReactDOM.createRoot(document.getElementById("root")).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);


// index.html

<body>
  <div id="root"></div>
  <script type="module" src="/src/container/Root.tsx"></script>
</body>


// src/layouts/index.tsx
import React from "react";
import { Outlet, useNavigate } from "react-router-dom";
import {
  UserOutlined,
  DesktopOutlined,
  PieChartOutlined,
} from "@ant-design/icons";
import type { MenuProps } from "antd";
import { Breadcrumb, Layout, Menu, theme } from "antd";

const { Header, Content, Footer, Sider } = Layout;

type MenuItem = Required<MenuProps>["items"][number];

function getItem(
  label: React.ReactNode,
  key: React.Key,
  icon?: React.ReactNode,
  children?: MenuItem[]
): MenuItem {
  return {
    key,
    icon,
    children,
    label,
  } as MenuItem;
}

const items1: MenuItem[] = [
  getItem("首页", "homepage", <PieChartOutlined />),
  getItem("待办", "backlog", <DesktopOutlined />),
  getItem("用户设置", "profile", <UserOutlined />, [
    getItem("人员列表", "/profile/list"),
    getItem("个人信息", "/profile/personal1"),
    getItem("修改密码", "/profile/changepwd"),
  ]),
  getItem("模块案例", "case", <UserOutlined />, [
    getItem("接口请求方法", "/case/interfaceRequest"),
  ]),
];

const App: React.FC = () => {
  const navigate = useNavigate();
  const {
    token: { colorBgContainer },
  } = theme.useToken();

  const onClick: MenuProps["onClick"] = (menuItem) => {
    console.log("click ", menuItem);
    const { key }: any = menuItem;
    navigate(key);
  };

  return (
    <Layout style={{ minHeight: "100vh" }}>
      <Header style={{ display: "flex", alignItems: "center" }}>
        <span style={{ color: "#fff", fontSize: "20px", textAlign: "center" }}>
          后台管理系统
        </span>
      </Header>
      <Layout>
        <Sider width={200} collapsible style={{ background: colorBgContainer }}>
          <Menu
            mode="inline"
            theme="light"
            defaultSelectedKeys={["homepage"]}
            defaultOpenKeys={["sub1"]}
            style={{ height: "100%", borderRight: 0 }}
            items={items1}
            onClick={onClick}
          />
        </Sider>
        <Layout style={{ padding: "0 24px 24px" }}>
          <Breadcrumb
            style={{ margin: "16px 0" }}
            items={[
              {
                title: "Home",
              },
              {
                title: <a href="">Application Center</a>,
              },
              {
                title: <a href="">Application List</a>,
              },
              {
                title: "An Application",
              },
            ]}
          />
          <Content
            style={{
              padding: 24,
              margin: 0,
              minHeight: 600,
              background: colorBgContainer,
            }}
          >
            <Outlet /> // 重点!!!
          </Content>
          <Footer style={{ textAlign: "center", padding: "6px " }}>
            Ant Design ©2023 Created by Ant UED
          </Footer>
        </Layout>
      </Layout>
    </Layout>
  );
};

export default App;

1.2 路由跳转

1. dom标签

通过 Link/NavLink 传值跳转:

importLink } from "react-router-dom";

const Home = () => {
  return (
    <div>
      <h1>你好很</h1>
      <Link to="/test1">跳转test1</Link>
    </div>
  );
};

export default Home;

2.  js

使用 useNavigate 跳转页面:

// test.jsx
import { useNavigate } from "react-router-dom";

const Test = () => {
  const navigate = useNavigate();
  const toTest2 = () => {
    navigate("/test2?name=tom&age=18", {
      state: "linjing",
    }); // search传参
    
    // navigate("/test2", {
    //   state: [{ code: "linjing", id: 10 }],
    // }); // state 传参
  };

  return <div onClick={toTest2}>跳转测试页面2</div>;
};

export default Test;
// /Home/index.jsx
// 总结三种跳转方式
import { observer } from "mobx-react";
import { NavLink, Link, useNavigate } from "react-router-dom";
import { Button, Divider } from "antd";

const HomePage = () => {
const navigate = useNavigate();
const handleClick = () => {
  navigate("/case/interfaceRequest");
};
return (
  <>
    <div>
      <NavLink to="/case/interfaceRequest">案例 NavLink</NavLink>
      <Divider />
      <Link to="/case/interfaceRequest">跳转 Link</Link>
      <Divider />
      <Button type="link" onClick={handleClick}>
        跳转案例 onClick
      </Button>
    </div>
  </>
);
};
export default observer(HomePage);

1.3 传参方式

1.params传参

优点:刷新页面,参数不丢失。

缺点:1.只能传字符串,传值过多url会变得很长。 2.参数必须在路由上配置

 // 配置  src/routers/index.jsx
 
  {
        path: "/case/interfaceRequest/:id/:name",
        exact: true,
        element: <InterfaceRequest />,
      },
 // 路由跳转与获取路由参数  src/HomePage/index.jsx
 
import {useNavigate } from "react-router-dom";
  const navigate = useNavigate();
// 跳转路由   地址栏:/detail/2/linjing
navigate("/case/interfaceRequest/2/linjing");
// 获取路由参数
const params = useParams()  
console.log(params) // {id: "2",name:"linjing"}

2.search 传参

优点:刷新页面,参数不丢失。

缺点:1.只能传字符串,传值过多url会变得很长。 2.获取参数需要自定义hooks

 // 配置  src/routers/index.jsx
 
 { path: '/detail', component: Detail }
 // 路由跳转与获取路由参数  src/HomePage/index.jsx

import {useNavigate } from "react-router-dom";
  const navigate = useNavigate();
// 跳转路由   地址栏:?id=2&name=%27linjing%27
navigate("/case/interfaceRequest?id=2&name='linjing'"); // search

// 获取参数
   const getUrlData = () => {
    const urlArr = window.location.href.split("?")[1].split("&");
    console.log("urlArr: ", urlArr);
    const obj: any = {};
    urlArr.forEach((item) => {
      console.log("item: ", item);
      const name = item.substring(0, item.indexOf("="));
      console.log("name: ", name);
      let value = item.substring(item.indexOf("=") + 1);
      console.log("value: ", value);
      value = decodeURIComponent(value); // 解码由 encodeURIComponent 方法或者其他类似方法编码的部分统一资源标识符(URI)
      obj[name] = value;
    });
    return obj;
  };
  const params = getUrlData();
    console.log("params: ", params);
  

3.state 传参

优点:可以传对象。

缺点:<HashRouter>刷新页面,参数丢失

 // 配置  src/routers/index.jsx
 
 { path: '/detail', element: <Detail/> }
 // 路由跳转与获取路由参数  src/HomePage/index.jsx
 
import { useNavigate } from 'react-router-dom'; 
// 路由跳转 
const item = { id: 10, name: "linjing" };
navigate("/case/interfaceRequest", { state: item }); // state

// 参数获取 
import { useLocation } from 'react-router-dom'; 
const {state} = useLocation() 
console.log(state) // {id:10,name:"linjing"}

<HashRouter> 不支持 location.keylocation.state<HashRouter>通过state传递参数,刷新页面后参数丢失,官方建议使用<BrowserRouter><BrowserRouter>页面刷新参数也不会丢失

总结:不同的路由传参一定要注意,是否要在注册路由的时候接收,一定要使用对应的hook接收对应的参数。