20230603----重返学习-react路由导航-路由传参-react-router-dom的V6版本

day-084-eighty-four-20230603-react路由导航-路由传参-react-router-dom的V6版本

react路由导航

编码式导航

  • 编码式导航:基于JavaScript实现路由的跳转。

  • 编码式导航:基于JavaScript实现路由的跳转。实际上是基于props中history对象中的相关方法实现路由跳转的。

  • 在路由当中:

    • 受控组件:受路由管控的组件,也就是基于Route组件进行路由匹配而渲染的组件。
    • 非受控组件:不是基于Route组件匹配渲染的组件。
  • 在react-router-dom的V5版本中:

    • 如果是受控组件,则路由内容,会给每个组件传递三个属性:

      • history 提供了路由跳转的方式。

        • push:跳转路由新增历史记录
        • replace:跳转路由与替换当前的历史记录
        • go:跳转到历史记录中指定位置
        • goBack:回退一步 -> go(-1);
        • goForward:前进一步 -> go(1);
      • location:记录当前路由的信息。

        • pathname:当前路由地址。
        • search:当前路由的问号传参信息。
        • state:当前路由的隐式传参信息。
      • match:记录当前路由匹配的规则与匹配记录。

        • params:存储匹配的路径参数信息。
    • 如果是非受控组件,默认不会传递这三个属性。

      • 这样就有一个问题:在非受控组件中,如何实现编程式导航?
        1. 基于路由内部提供的withRouter()高阶组件,把非受控组件变为受控组件

          • 或者说让非受控组件,也具备这三个属性信息,可适用于任何类型的组件
          import { withRouter } from "react-router-dom";
          const CommonMenu = function CommonMenu(props) {
            console.log(`CommonMenu-props`, props);
            return <div></div>
          }
          export default withRouter(CommonMenu);
          
        2. 基于路由提供的useHistory()/useLocation()/useParams()/useRouteMatch()等Hook函数,在函数组件中获取对应的信息!

          • useHistory() -> history对象
          • useLocation() -> location对象
          • useRouteMatch() -> match对象
          • useParams() -> 获取基于路径传参的信息。
        • 以上知识,必须有一个前提:所处理的组件,不管是受控还是非受控,必须都在HashRouter()/BrowserRouter()等Router的内部!

路由传参

  • 路由跳转传参的方式:
    • 例如从A组件跳转到B组件,需要给B组件传递一些参数信息。
      1. 基于问号传参传递信息。

        • 传递的信息在URL地址栏中可以看见(明文)。
          • 不安全有长度限制
        • 传递的信息都要变为urlencoded格式字符串,接收的时候也需要处理这样的字符串。
          • 可以基于qs库或者new URLSearchParams()处理。
        • 跳转后的目标组件进行刷新操作,因为传递的信息在地址栏中有,所以依然可以获取问号传参信息
      2. 基于隐式传参传递信息。

        • 传递的信息URL地址栏中看不见。
          • 不丑安全没有长度限制
        • 可以传递任何格式的数据。
        • 跳转后的目标组件进行刷新操作,因为地址栏没有传递的信息,那么之前传递的信息就消失了。
      3. 基于路径参数传递信息,把传参信息作为地址的一部分

        • 问号传参一样,也是要把传递的信息放在URL地址栏中,所以相关特点和问号传参几乎一致。
          • 只不过这种模式看上去比问号传参美观一些。
        • 步骤:
          • 第一步:修改路由匹配的规则。
            • path: '/home/message' 旧写法。
            • path: '/home/message/:lx?/:name?' 新写法。
              • : 说明是传递的参数。
              • ? 可传可不传。
          • 第二步:跳转的时候,把需要传递的值,作为地址的一部分;接收信息的时候,基于 match.params/useParams() 处理即可!

对问号传参的urlencoded格式字符串信息处理

路由懒加载

  • 默认情况下,我们会把所有组件的代码全部打包到一个js文件中-即主js文件。这样当页面第一次渲染的时候,需要从服务器获取这个主js文件才能执行,但是因为其文件过大,需要加载很长时间,在此期间内,页面呈现白屏。

    • 为了加快页面第一次渲染的速度,我们只把默认需要展示的组件打包到主js文件中,让主js文件小一些,其余组件的代码单独打包到别的js文件中,刚开始加载页面的时候不加载这些代码。
      • 当基于路由切换,需要加载某个组件的时候,再动态的从服务器获取对应的js文件或对应的代码,进行渲染!
      • 我们把以上这种优化手段,称之为路由懒加载。
        • 这个是前端必做的性能优化之一。
  • 路由懒加载步骤:

    1. 基于React.lazy、ES6中的import函数、/webpackChunkName:‘home’/,保证webpack打包后的时候,可以把各个组件代码打包到不同的js文件中!页面渲染的时候,最开始只加载主js文件。
      • 路由切换时,会再加载其它基于webpackChunkName的的js文件。
    2. 基于React.Suspense来处理动态加载的组件或异步组件,只有这样才能把异步组件中加载出来。
      • fallback: 在异步组件没有渲染出来之前,先展示fallback中的信息,异步组件渲染出来后,fallback中的信息就会销毁!
  • 在jsx文件及js代码中,使用src引入文件如图片这类需要使用文件地址时,要先用ES6把相对路径的文件导入进来再引用。或者使用绝对路径。

  • 在css或less时,使用图片这类需要使用文件地址时,是可以直接使用相对路径的。

组件封装-loading

react-router-dom的V6版本

react路由V6版本-基础版文件结构

  • 文件结构
    • React阶段/day0603_router6/src/index.jsx
    • React阶段/day0603_router6/src/App.jsx 构建一级路由
    • React阶段/day0603_router6/src/layout 布局类组件放置处,实际上这里放的大多为一级路由对应的组件。
      1. 因为布局类的文件大多是一级路由对应的组件。
      2. 个人2023年感觉很多项目是这样拆分的,这样可以让文件更明确些。
        • 个人做过的项目和参考的项目大多是这样布置的。
        • 理论上,也都可以全部放在views文件夹下。
      3. 放在这里,也可以让项目层级减少一层。
      4. 这个理论上可以凭个人喜好来。
      • React阶段/day0603_router6/src/layout/BasicLayout.jsx
      • React阶段/day0603_router6/src/layout/Error.jsx
      • React阶段/day0603_router6/src/layout/UserLayout.jsx
    • React阶段/day0603_router6/src/views 页面组件,一般是二级及以下的业务组件。
      • React阶段/day0603_router6/src/views/home 二级路由组件-Home.jsx下的三级路由组件。
        • React阶段/day0603_router6/src/views/home/Message.jsx
        • React阶段/day0603_router6/src/views/home/Watch.jsx
        • React阶段/day0603_router6/src/views/home/Worker.jsx
      • React阶段/day0603_router6/src/views/Category.jsx
      • React阶段/day0603_router6/src/views/Home.jsx
      • React阶段/day0603_router6/src/views/Login.jsx
      • React阶段/day0603_router6/src/views/Personal.jsx
      • React阶段/day0603_router6/src/views/Register.jsx

react路由V6版本-基础版路由代码

  • 文件结构
    • React阶段/day0603_router6/src/index.jsx

      import React from "react";
      import ReactDOM from "react-dom/client";
      /* ANTD */
      import { ConfigProvider } from "antd";
      import zhCN from "antd/locale/zh_CN";
      /* 组件&样式 */
      import "./index.less";
      import App from "./App";
      
      const root = ReactDOM.createRoot(document.getElementById("root"));
      root.render(
        <ConfigProvider locale={zhCN}>
          <App />
        </ConfigProvider>
      );
      
    • React阶段/day0603_router6/src/App.jsx 主视图文件,构建一级路由。

      import React from "react";
      
      // 1. 引入HashRouter, Routes, Route, Navigate等react-router-dom提供的组件。引入HashRouter用于把组件包起来,Routes用于设置路由信息组-把多条路由信息组合成一组以便只渲染一个路由视图,Route用于设置单条路由信息,Navigate用于与Route配合起来设置重定向。
      import { HashRouter, Routes, Route, Navigate } from "react-router-dom";
      
      // 2. 引入路由需要用到的业务视图组件。
      import BasicLayout from "./layout/BasicLayout";
      import UserLayout from "./layout/UserLayout";
      import Error from "./layout/Error";
      import Login from "./views/Login";
      import Register from "./views/Register";
      import Home from "./views/Home";
      import Personal from "./views/Personal";
      import Category from "./views/Category";
      import Watch from "./views/home/Watch";
      import Worker from "./views/home/Worker";
      import Message from "./views/home/Message";
      
      const App = function App() {
        // 3. 构建路由。
        return (
          <HashRouter>
            <Routes>
              <Route path="/" element={<BasicLayout />}>
                <Route path="" element={<Navigate to="/home" />} />
                <Route path="home" element={<Home />}>
                  <Route path="" element={<Navigate to="/home/watch" />} />
                  <Route path="watch" element={<Watch />} />
                  <Route path="worker" element={<Worker />} />
                  <Route path="message" element={<Message />} />
                </Route>
                <Route path="category" element={<Category />} />
                <Route path="personal" element={<Personal />} />
              </Route>
              <Route path="/user" element={<UserLayout />}>
                <Route path="" element={<Navigate to="/user/login" />} />
                <Route path="login" element={<Login />} />
                <Route path="register" element={<Register />} />
              </Route>
              <Route path="/404" element={<Error />} />
              <Route path="*" element={<Navigate to="/404" />} />
            </Routes>
          </HashRouter>
        );
      };
      export default App;
      
    • React阶段/day0603_router6/src/layout

      • React阶段/day0603_router6/src/layout/BasicLayout.jsx 一级路由组件。

        import { useState } from "react"
        import { Menu, Button } from "antd"
        import { HomeOutlined, ClusterOutlined, UserOutlined, MenuUnfoldOutlined, MenuFoldOutlined } from '@ant-design/icons'
        import styled from "styled-components"
        import logo from "@/assets/images/logo.svg"
        import avatar from "@/assets/images/touxiang.png"
        import { Outlet } from "react-router-dom"
        
        /* 组件样式 */
        const BasicLayoutStyle = styled.div`
            height: 100%;
            overflow: hidden;
        
            .header{
                box-sizing: border-box;
                padding: 0 15px;
                height: 48px;
                background: #001529;
                display: flex;
                justify-content: space-between;
                align-items: center;
        
                .logo,
                .info{
                    line-height: 48px;
                    font-size: 18px;
                    color: #FFF;
                    display: flex;
                    align-items: center;
        
                    img{
                        margin-right: 10px;
                        width: 35px;
                        height: 35px;
                    }
                }
        
                .info{
                    font-size: 14px;
        
                    img{
                        width: 30px;
                        height: 30px;
                    }
                }
            }
        
            .content{
                height: calc(100% - 48px);
                display: flex;
        
                .menu-box{
                    height: 100%;
                    background: #001529;
        
                    .ant-btn{
                        margin-left: 4px;
                        background: transparent;
                        box-shadow: none;
                    }
        
                    .ant-menu-item{
                        padding: 0 24px;
                    }
        
                    .ant-menu-inline-collapsed{
                        .ant-menu-item{
                            padding: 0 28px;
                        }
                    }
                }
        
                .view-box{
                    box-sizing: border-box;
                    padding: 15px;
                    height: 100%;
                    flex-grow: 1;
        
                    .component{
                        height: 100%;
                        overflow-y: auto;
                        overflow-x: hidden;
                        background: #FFF;
                    }
                }
            }
        `
        
        const BasicLayout = function BasicLayout() {
            // 左侧Menu的数据
            const menuItem = [{
                key: 'home',
                label: '控制面板',
                icon: <HomeOutlined />
            }, {
                key: 'category',
                label: '分类管理',
                icon: <ClusterOutlined />
            }, {
                key: 'personal',
                label: '个人中心',
                icon: <UserOutlined />
            }]
        
            // 定义状态
            let [collapsed, setCollapsed] = useState(false)
        
            return <BasicLayoutStyle>
                <div className="header">
                    <h2 className="logo">
                        <img src={logo} alt="" />
                        Ant Design
                    </h2>
                    <div className="avatar">
                        <p className="info">
                            <img src={avatar} alt="" />
                            海贼王-路飞
                        </p>
                    </div>
                </div>
                <div className="content">
                    <div className="menu-box">
                        <Button type="primary"
                            onClick={() => {
                                setCollapsed(!collapsed)
                            }}>
                            {collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
                        </Button>
                        <Menu mode="inline" theme="dark"
                            items={menuItem}
                            defaultSelectedKeys={['home']}
                            inlineCollapsed={collapsed} />
                    </div>
                    <div className="view-box">
                        <div className="component">
                            {/* 主页的二级路由 */}
                            <Outlet />
                        </div>
                    </div>
                </div>
            </BasicLayoutStyle>
        }
        export default BasicLayout
        
      • React阶段/day0603_router6/src/layout/Error.jsx 一级路由组件。

        import React from "react";
        import { Button, Empty } from "antd";
        import styled from "styled-components";
        
        /* 组件的样式 */
        const ErrorStyle = styled.div`
          height: 100%;
          overflow: hidden;
        
          .ant-empty {
            position: relative;
            top: 100px;
          }
        `;
        
        const Error = function Error() {
          return (
            <ErrorStyle>
              <Empty description={<span>很遗憾,您访问的页面不存在!</span>}>
                <Button type="primary">返回首页</Button>
              </Empty>
            </ErrorStyle>
          );
        };
        export default Error;
        
      • React阶段/day0603_router6/src/layout/UserLayout.jsx 一级路由组件。

        import React from "react";
        import { Tabs } from "antd";
        import styled from "styled-components";
        import backgroundImg from "@/assets/images/background.svg";
        import logo from "@/assets/images/logo.svg";
        import { Outlet } from "react-router-dom";
        
        /* 组件样式 */
        const UserLayoutStyle = styled.div`
          position: relative;
          height: 100%;
          background: url(${backgroundImg}) no-repeat;
          background-size: 100%;
        
          .content {
            position: absolute;
            top: 100px;
            left: 50%;
            margin-left: -165px;
            width: 330px;
          }
        
          .header {
            .title {
              display: flex;
              justify-content: center;
              align-items: center;
              line-height: 44px;
              font-size: 33px;
              font-weight: normal;
        
              img {
                margin-right: 10px;
                width: 44px;
                height: 44px;
              }
            }
        
            .subtitle {
              margin: 12px 0 40px 0;
              text-align: center;
              font-size: 14px;
              color: rgba(0, 0, 0, 0.45);
            }
          }
        
          .ant-tabs {
            margin-bottom: 10px;
        
            .ant-tabs-nav {
              &:before {
                border-bottom-color: #ddd;
              }
            }
        
            .ant-tabs-tab {
              padding: 0 10px;
              font-size: 16px;
              line-height: 40px;
            }
          }
        `;
        
        const UserLayout = function UserLayout() {
          // Tab页卡的数据
          const tabItem = [
            {
              label: `用户登录`,
              key: "login",
            },
            {
              label: `用户注册`,
              key: "register",
            },
          ];
        
          return (
            <UserLayoutStyle>
              <div className="content">
                <div className="header">
                  <h1 className="title">
                    <img src={logo} alt="" />
                    <span>Ant Design</span>
                  </h1>
                  <p className="subtitle">
                    Ant Design 是西湖区最具影响力的 Web 设计规范
                  </p>
                </div>
                <Tabs centered defaultActiveKey="login" items={tabItem} />
                {/* 登录/注册二级路由的位置 */}
                <Outlet />
              </div>
            </UserLayoutStyle>
          );
        };
        export default UserLayout;
        
    • React阶段/day0603_router6/src/views 页面组件,一般是二级及以下的业务组件。

      • React阶段/day0603_router6/src/views/home 二级路由组件-Home.jsx下的三级路由组件。

        • React阶段/day0603_router6/src/views/home/Message.jsx

          import React from "react";
          const Message = function Message() {
            return <div>控制面板 - 消息中心</div>;
          };
          export default Message;
          
        • React阶段/day0603_router6/src/views/home/Watch.jsx

          import React from "react";
          const Watch = function Watch() {
            return <div>控制面板 - 数据监控</div>;
          };
          export default Watch;
          
        • React阶段/day0603_router6/src/views/home/Worker.jsx

          import React from "react";
          const Worker = function Worker() {
            return <div>控制面板 - 工作台</div>;
          };
          export default Worker;
          
      • React阶段/day0603_router6/src/views/Category.jsx

        import React from "react";
        const Category = function Category() {
          return <div>分类管理的内容</div>;
        };
        export default Category;
        
      • React阶段/day0603_router6/src/views/Home.jsx 二级路由组件。

        import React from "react";
        import styled from "styled-components";
        import { NavLink, Outlet } from "react-router-dom";
        
        /* 组件样式 */
        const HomeStyle = styled.div`
          padding: 10px;
        
          .nav-box {
            display: flex;
            border-bottom: 1px solid #eee;
        
            a {
              padding: 0 20px;
              line-height: 40px;
              font-size: 15px;
              color: #000;
        
              &.active {
                color: #1677ff;
                font-size: 16px;
              }
            }
          }
        `;
        
        const Home = function Home() {
          return (
            <HomeStyle>
              <nav className="nav-box">
                <NavLink to="/home/watch">数据监控</NavLink>
                <NavLink to="/home/worker">工作台</NavLink>
                <NavLink to="/home/message">消息中心</NavLink>
              </nav>
              {/* 首页的三级路由 */}
              <Outlet />
            </HomeStyle>
          );
        };
        export default Home;
        
      • React阶段/day0603_router6/src/views/Login.jsx 二级路由组件。

        import React from "react";
        import { Form, Input, Checkbox, Button } from "antd";
        import { UserOutlined, LockOutlined } from "@ant-design/icons";
        import styled from "styled-components";
        
        /* 组件的样式 */
        const LoginStyle = styled.div`
          .remember {
            display: flex;
            justify-content: space-between;
            align-items: center;
        
            a {
              font-size: 14px;
              color: #1890ff;
            }
          }
        
          .submit {
            width: 100%;
          }
        `;
        
        const Login = function Login() {
          return (
            <LoginStyle>
              <Form
                autoComplete="off"
                initialValues={{
                  account: "",
                  password: "",
                }}
              >
                <Form.Item name="account">
                  <Input
                    size="large"
                    placeholder="请输入账号"
                    prefix={<UserOutlined />}
                  />
                </Form.Item>
                <Form.Item name="password">
                  <Input.Password
                    size="large"
                    placeholder="请输入密码"
                    prefix={<LockOutlined />}
                  />
                </Form.Item>
                <Form.Item>
                  <div className="remember">
                    <Checkbox checked>记住账号密码</Checkbox>
                    <a href="">忘记密码?</a>
                  </div>
                </Form.Item>
                <Form.Item>
                  <Button className="submit" type="primary" size="large">
                    立即登录
                  </Button>
                </Form.Item>
              </Form>
            </LoginStyle>
          );
        };
        export default Login;
        
      • React阶段/day0603_router6/src/views/Personal.jsx

        import React from "react";
        const Personal = function Personal() {
          return <div>个人中心的内容</div>;
        };
        export default Personal;
        
      • React阶段/day0603_router6/src/views/Register.jsx

        import React from "react";
        import { Form, Input, Button } from "antd";
        import { LockOutlined, PhoneOutlined } from "@ant-design/icons";
        import styled from "styled-components";
        
        /* 组件的样式 */
        const RegisterStyle = styled.div`
          .submit {
            width: 100%;
          }
        
          .code-box {
            display: flex;
            justify-content: space-between;
            align-items: center;
        
            .ant-form-item {
              &:nth-child(1) {
                margin-right: 10px;
              }
            }
          }
        `;
        
        const Register = function Register() {
          return (
            <RegisterStyle>
              <Form
                autoComplete="off"
                initialValues={{
                  phone: "",
                  code: "",
                }}
              >
                <Form.Item name="phone">
                  <Input
                    size="large"
                    placeholder="请输入手机号"
                    prefix={<PhoneOutlined />}
                  />
                </Form.Item>
                <div className="code-box">
                  <Form.Item name="code">
                    <Input
                      size="large"
                      placeholder="请输入验证码"
                      prefix={<LockOutlined />}
                    />
                  </Form.Item>
                  <Form.Item>
                    <Button size="large">发送验证码</Button>
                  </Form.Item>
                </div>
                <Form.Item>
                  <Button className="submit" type="primary" size="large">
                    立即注册
                  </Button>
                </Form.Item>
              </Form>
            </RegisterStyle>
          );
        };
        export default Register;
        

react路由V6版本-基础版构建步骤

  1. 在主视图文件里引入构建一级路由。

    1. 引入HashRouter, Routes, Route, Navigate等react-router-dom提供的组件。
      • HashRouter用于把组件包起来。
      • Routes用于设置路由信息组-把多条路由信息组合成一组以便只渲染一个路由视图。
      • Route用于设置单条路由信息。
      • Navigate用于与Route配合起来设置重定向。
    2. 引入路由需要用到的业务视图组件。
    3. 使用HashRouter, Routes, Route, Navigate等及业务视图组件来构建路由。
      • <HashRouter></HashRouter> 所有需要用到路由信息的,都要用这个组件包起来。

      • <Routes></Routes> 包裹一组路由信息。

        1. 内部只能放<Route>标签,放其它标签会报错。
        2. 所有级别的路由匹配规则路由表都写在一起。
      • <Route/><Route></Route> 用来设置一条路由信息。

        1. <Route></Route>内部可以嵌套<Route/><Route></Route>

        2. 默认匹配规则类似于精准匹配。

        3. 被嵌套的<Route/>标签就是子级路由。

        4. 子级路由可以省略父级路由地址。

          • 不过子级路由以/开头,就会被认为是绝对路径。
          • 子级路由不以/开头,那么其前方就会自动添加上父级路由的地址。
        5. 要渲染的组件用element属性来设置,但element属性的值必须是一个组件标签的格式,而不能只是一个函数变量。

          <Route path="personal" element={<Personal />} />//一个组件标签的格式,是对的。
          
          <Route path="personal" element={Personal} />//一个函数变量,是错的。
          
        import React from "react";
        
        // 1. 引入HashRouter, Routes, Route, Navigate等react-router-dom提供的组件。引入HashRouter用于把组件包起来,Routes用于设置路由信息组-把多条路由信息组合成一组以便只渲染一个路由视图,Route用于设置单条路由信息,Navigate用于与Route配合起来设置重定向。
        import { HashRouter, Routes, Route, Navigate } from "react-router-dom";
        
        // 2. 引入路由需要用到的业务视图组件。
        import BasicLayout from "./layout/BasicLayout";
        import UserLayout from "./layout/UserLayout";
        import Error from "./layout/Error";
        import Login from "./views/Login";
        import Register from "./views/Register";
        import Home from "./views/Home";
        import Personal from "./views/Personal";
        import Category from "./views/Category";
        import Watch from "./views/home/Watch";
        import Worker from "./views/home/Worker";
        import Message from "./views/home/Message";
        
        const App = function App() {
          // 3. 构建路由。
          return (
            <HashRouter>
              <Routes>
                <Route path="/" element={<BasicLayout />}>
                  <Route path="" element={<Navigate to="/home" />} />
                  <Route path="home" element={<Home />}>
                    <Route path="" element={<Navigate to="/home/watch" />} />
                    <Route path="watch" element={<Watch />} />
                    <Route path="worker" element={<Worker />} />
                    <Route path="message" element={<Message />} />
                  </Route>
                  <Route path="category" element={<Category />} />
                  <Route path="personal" element={<Personal />} />
                </Route>
                <Route path="/user" element={<UserLayout />}>
                  <Route path="" element={<Navigate to="/user/login" />} />
                  <Route path="login" element={<Login />} />
                  <Route path="register" element={<Register />} />
                </Route>
                <Route path="/404" element={<Error />} />
                <Route path="*" element={<Navigate to="/404" />} />
              </Routes>
            </HashRouter>
          );
        };
        export default App;
        
      • <Route path="*" element={<Navigate to="/404" />} /> 用于设置路由跳转。

  2. 在父级路由匹配组件的指定位置,基于<Outlet/>组件把子级路由匹配的内容进行渲染!

    // 1. 引入Outlet组件。
    import { Outlet } from "react-router-dom"
    
    {/* 2. 在父级路由匹配组件的指定位置,基于`<Outlet/>组件`把子级路由匹配的内容进行渲染! */}
    <Outlet />
    
    import { useState } from "react"
    import { Menu, Button } from "antd"
    import { HomeOutlined, ClusterOutlined, UserOutlined, MenuUnfoldOutlined, MenuFoldOutlined } from '@ant-design/icons'
    import styled from "styled-components"
    import logo from "@/assets/images/logo.svg"
    import avatar from "@/assets/images/touxiang.png"
    
    // 1. 引入Outlet组件。
    import { Outlet } from "react-router-dom"
    
    /* 组件样式 */
    const BasicLayoutStyle = styled.div`
        height: 100%;
        overflow: hidden;
    
        .header{
            box-sizing: border-box;
            padding: 0 15px;
            height: 48px;
            background: #001529;
            display: flex;
            justify-content: space-between;
            align-items: center;
    
            .logo,
            .info{
                line-height: 48px;
                font-size: 18px;
                color: #FFF;
                display: flex;
                align-items: center;
    
                img{
                    margin-right: 10px;
                    width: 35px;
                    height: 35px;
                }
            }
    
            .info{
                font-size: 14px;
    
                img{
                    width: 30px;
                    height: 30px;
                }
            }
        }
    
        .content{
            height: calc(100% - 48px);
            display: flex;
    
            .menu-box{
                height: 100%;
                background: #001529;
    
                .ant-btn{
                    margin-left: 4px;
                    background: transparent;
                    box-shadow: none;
                }
    
                .ant-menu-item{
                    padding: 0 24px;
                }
    
                .ant-menu-inline-collapsed{
                    .ant-menu-item{
                        padding: 0 28px;
                    }
                }
            }
    
            .view-box{
                box-sizing: border-box;
                padding: 15px;
                height: 100%;
                flex-grow: 1;
    
                .component{
                    height: 100%;
                    overflow-y: auto;
                    overflow-x: hidden;
                    background: #FFF;
                }
            }
        }
    `
    
    const BasicLayout = function BasicLayout() {
        // 左侧Menu的数据
        const menuItem = [{
            key: 'home',
            label: '控制面板',
            icon: <HomeOutlined />
        }, {
            key: 'category',
            label: '分类管理',
            icon: <ClusterOutlined />
        }, {
            key: 'personal',
            label: '个人中心',
            icon: <UserOutlined />
        }]
    
        // 定义状态
        let [collapsed, setCollapsed] = useState(false)
    
        return <BasicLayoutStyle>
            <div className="header">
                <h2 className="logo">
                    <img src={logo} alt="" />
                    Ant Design
                </h2>
                <div className="avatar">
                    <p className="info">
                        <img src={avatar} alt="" />
                        海贼王-路飞
                    </p>
                </div>
            </div>
            <div className="content">
                <div className="menu-box">
                    <Button type="primary"
                        onClick={() => {
                            setCollapsed(!collapsed)
                        }}>
                        {collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
                    </Button>
                    <Menu mode="inline" theme="dark"
                        items={menuItem}
                        defaultSelectedKeys={['home']}
                        inlineCollapsed={collapsed} />
                </div>
                <div className="view-box">
                    <div className="component">
                        {/* 主页的二级路由 */}
                        {/* 2. 在父级路由匹配组件的指定位置,基于`<Outlet/>组件`把子级路由匹配的内容进行渲染! */}
                        <Outlet />
                    </div>
                </div>
            </div>
        </BasicLayoutStyle>
    }
    export default BasicLayout
    

与路由V5版本的区别

  • react-router-dom(V6)react-router-dom(V5)对比
  1. 都是基于HashRouter()BrowserRouter()来指定路由的模式,而且建议所有的组件都放在HashRouter()与BrowserRouter()这类Router中。

  2. V6版本中没有了<Switch>,因为不需要了,其内部自己做了类似于<Switch>的处理。

  3. V6中没有了<Redirect>,但是可以基于<Navigate to="/404"/>来代替它。

    • 在V5版本中,<Redirect />相当于特殊的<Route/>,我们是基于它设置重定向的规则的。
      • <Redirect from="*" to="/404" />
    • 在V6版本中,<Navigate/>是一个组件,渲染这个组件时会重定向到该组件的to属性指定的地址
      • <Route path="*" element={<Navigate to="/404" />} />
  4. V6中也是基于<Route/>设置匹配规则,只不过都需要放在<Routes></Routes>中,而且在<Route/>上设置的详细规则,和V5版本有很大的区别:

    • 无需在<Route/>上设置exact属性用于进行精准匹配了,因为默认都是类似于精准匹配
    • <Route/>path属性和之前一样,设置对应的匹配地址。
    • 匹配成功后需要渲染那个组件不再基于<Route/>component属性处理,而是基于<Route/>element属性渲染,而且也没有了<Route/>render函数属性这样的配置,element渲染的内容需要写成<Component/>这种格式!
      • <Routes></Routes>只能放<Route/>

      • V5旧:

        import { HashRouter, BrowserRouter, Route, Switch, Redirect } from 'react-router-dom'
        import BasicLayout from "./layout/BasicLayout"
        import UserLayout from "./layout/UserLayout"
        import Error from "./layout/Error"
        <HashRouter>
            <Switch>
                <Route path="/user" component={UserLayout} />
                <Route path="/404" component={Error} />
                <Route path="/" component={BasicLayout} />
                <Redirect to="/404" />
            </Switch>
        </HashRouter>
        //或
        <HashRouter>
          <Route path="/user" component={UserLayout} />
        </HashRouter>
        
      • V6新:

        import { HashRouter, Routes, Route, Navigate } from "react-router-dom";
        
        import BasicLayout from "./layout/BasicLayout";
        import UserLayout from "./layout/UserLayout";
        import Error from "./layout/Error";
        <HashRouter>
          <Routes>
            <Route path="/" element={<BasicLayout />}></Route>
            <Route path="/user" element={<UserLayout />}></Route>
            <Route path="/404" element={<Error />} />
            <Route path="*" element={<Navigate to="/404" />} />
          </Routes>
        </HashRouter>
        //或
        <HashRouter>
          <Routes>
            <Route path="/" element={<BasicLayout />}></Route>
          </Routes>
        </HashRouter>
        
  5. V6有一个重大的变革:所有级别的路由匹配规则路由表都写在一起,而不再像V5一样,需要分散到指定组件的特定位置上,这样有利于路由的统一管理

    <Route path="/user" element={<UserLayout />}>
    /* - 在`子级路由中`其path可以省略`父级路由的地址`
        - 但是也不要自己再加`/`
        - `path=""` 等价于 `path="/user"`
        - `path="login"`等价于 `path="/user/login"`
      - 如果路由地址是`/user/xxx`,首先和一级路由`/user`进行匹配了,渲染`UserLayout组件`后,进入`/user`的`子级路由`进行匹配;
        - 在`子级路由`中`一项项地进行匹配`,匹配成功,就把`子级路由`中`element属性指定的组件`,放在`UserLayout组件`中的`<Outlet>容器`中进行渲染!
        - 如果在`子级路由`中没有任何匹配的,则跳出这一级,继续向下匹配`与/user路由同级`的`其它的一级路由`! */
        <Route path="" element={<Navigate to="/user/login" />} />
        <Route path="login" element={<Login />} />
        <Route path="register" element={<Register />} />
    </Route>
    
    • 子级路由中其path可以省略父级路由的地址

      • 但是也不要自己再加/
      • path="" 等价于 path="/user"
      • path="login"等价于 path="/user/login"
    • 如果路由地址是/user/xxx,首先和一级路由/user进行匹配了,渲染UserLayout组件后,进入/user子级路由进行匹配;

      • 子级路由一项项地进行匹配,匹配成功,就把子级路由element属性指定的组件,放在UserLayout组件中的<Outlet>容器中进行渲染!
      • 如果在子级路由中没有任何匹配的,则跳出这一级,继续向下匹配与/user路由同级其它的一级路由
    • 但是需要在上级路由匹配组件的指定位置,基于<Outlet/>把下级路由匹配的内容进行渲染!

进阶参考

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
React Router v6中,路由传参可以通过以下几种方式实现: 1. 使用URL参数传递数据: 在路由路径中使用冒号(:)定义参数,然后在组件中通过`useParams`钩子函数获取参数的值。例如: ```jsx import { BrowserRouter as Router, Routes, Route, Link, useParams } from 'react-router-dom'; function User() { const { id } = useParams(); return <h2>User ID: {id}</h2>; } function App() { return ( <Router> <nav> <ul> <li> <Link to="/user/1">User 1</Link> </li> <li> <Link to="/user/2">User 2</Link> </li> </ul> </nav> <Routes> <Route path="/user/:id" element={<User />} /> </Routes> </Router> ); } export default App; ``` 当点击链接时,会根据参数的不同显示不同的用户ID。 2. 使用查询参数传递数据: 在URL中使用查询参数传递数据,可以通过`useLocation`钩子函数获取查询参数的值。例如: ```jsx import { BrowserRouter as Router, Routes, Route, Link, useLocation } from 'react-router-dom'; function User() { const location = useLocation(); const params = new URLSearchParams(location.search); const name = params.get('name'); return <h2>Hello, {name}</h2>; } function App() { return ( <Router> <nav> <ul> <li> <Link to="/user?name=John">User John</Link> </li> <li> <Link to="/user?name=Jane">User Jane</Link> </li> </ul> </nav> <Routes> <Route path="/user" element={<User />} /> </Routes> </Router> ); } export default App; ``` 当点击链接时,会根据查询参数的不同显示不同的用户名称。 3. 使用状态传递数据: 在React Router v6中,可以使用状态传递数据。通过使用`useNavigate`钩子函数获取导航函数,然后在组件中使用状态来传递数据。例如: ```jsx import { BrowserRouter as Router, Routes, Route, Link, useNavigate } from 'react-router-dom'; import { useState } from 'react'; function User() { const navigate = useNavigate(); const [name, setName] = useState(''); const handleSubmit = (e) => { e.preventDefault(); navigate(`/user/${name}`); }; return ( <div> <form onSubmit={handleSubmit}> <input type="text" value={name} onChange={(e) => setName(e.target.value)} /> <button type="submit">Go</button> </form> </div> ); } function UserDetails() { const { name } = useParams(); return <h2>Hello, {name}</h2>; } function App() { return ( <Router> <nav> <ul> <li> <Link to="/user">User</Link> </li> </ul> </nav> <Routes> <Route path="/user" element={<User />} /> <Route path="/user/:name" element={<UserDetails />} /> </Routes> </Router> ); } export default App; ``` 当在输入框中输入用户名并点击提交按钮时,会根据输入的用户名显示相应的用户详情。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值