2022-01-19 react-router-dom V6 Antd Layout布局實(shí)現(xiàn)路由的切換

react-router-dom升級(jí)到V6之后API上還是有很多不同的
今天就使用vite+react-ts初始化項(xiàng)目,嘗試一下
vite地址:https://cn.vitejs.dev/guide/#scaffolding-your-first-vite-project
react-router-dom地址:https://reactrouter.com/docs/en/v6/getting-started/installation
Antd地址:https://ant.design/components/layout-cn/

用到的路由API有: BrowserRouteruseRoutes、RouteObject、LinkuseSearchParams、useParamsmatchRoutes、OutletuseLocation、useNavigate

開(kāi)始
首先初始化項(xiàng)目

yarn create vite react-dom-6 --template react-ts

這樣我們就使用vite腳手架初始化了一個(gè)react+ts的項(xiàng)目
進(jìn)入到我們的項(xiàng)目里面

cd react-dom-6

接下來(lái),把我們所需要的依賴(lài)安裝一下

yarn
yarn add react-router-dom@6 antd --save

完事版本和目錄就是這樣的


image.png

啟動(dòng)我們項(xiàng)目

yarn dev     //  or: npm run dev

在瀏覽器打開(kāi)網(wǎng)址
http://localhost:3000/

image.png

接下來(lái)把一些不必要的文件可以刪除一下
image.png

App.css清空,然后把a(bǔ)ntd的css樣式引入

@import "antd/dist/antd.css"

創(chuàng)建我們自己的頁(yè)面
在src目錄下,創(chuàng)建pages文件夾,用來(lái)存放我們自己的組件
在src目錄下,創(chuàng)建routers文件夾,用來(lái)同意管理路由組件

  • 在routers文件夾創(chuàng)建路有組件index.tsx
import { lazy, ReactNode, Suspense } from 'react'
import { RouteObject } from 'react-router-dom'
// import AppLayout from '../AppLayout'
// import Detail from '../Detail'
// import Home from '../Home'
// import Login from '../Login'
// import User from '../User'

// 用懶加載實(shí)現(xiàn)優(yōu)化
// const AppLayout = lazy(() => import('../AppLayout'));
const Detail = lazy(() => import('../Detail'));
const Home = lazy(() => import('../Home'));
const Login = lazy(() => import('../Login'));
const User = lazy(() => import('../User'));

// 切換頁(yè)面會(huì)出現(xiàn)閃屏現(xiàn)象
// 解決思路:公共頁(yè)面不采用懶加載的方式 并在App.tsx去除Suspense的包裹
import AppLayout from '../AppLayout';

// 實(shí)現(xiàn)懶加載的用Suspense包裹 定義函數(shù)
const lazyLoad = (children: ReactNode): ReactNode =>{
  return <Suspense fallback={<h1>Loading...</h1>}>
    {children}
  </Suspense>
}


export const routers: RouteObject[] = [

  {
    path: '/',
    element: <AppLayout />,
    //路由嵌套,子路由的元素需使用<Outlet />
    children: [
      {
        index: true,
        element: lazyLoad(<Home />)
      }, 
      {
        path: '/user/list',
        element: lazyLoad(<User />)
      },
      {
        path: '/user/detail/:id',
        element: lazyLoad(<Detail />)
      }
    ]
  },
  {
    path: '/login',
    element: lazyLoad(<Login />)
  }

]
  • main.tsx 組件
import ReactDOM from 'react-dom'
import {BrowserRouter as Router} from "react-router-dom"
import App from './App'

ReactDOM.render(
  <Router>
    <App />
  </Router>,
  document.getElementById('root'))
  • App.tsx 組件
    useRoutes:
    返回與當(dāng)前位置匹配的路由元素,參數(shù)就是創(chuàng)建的路由對(duì)象
import { useRoutes } from 'react-router-dom';
import './App.css';
import { routers } from './pages/router'
function App() {
  return useRoutes(routers);
}
export default App;

依次看一下路由對(duì)應(yīng)組件里面的內(nèi)容

  • AppLayout.jsx 組件
    matchRoutes: 將給定路由匹配到某個(gè)位置并返回匹配數(shù)據(jù)。
    Outlet: 渲染子路由的元素(如果有)
    useLocation: 返回當(dāng)前位置對(duì)象,該對(duì)象表示web中的當(dāng)前URL
    Link: 用作鏈接跳轉(zhuǎn),會(huì)渲染成a標(biāo)簽
import { Layout, Menu, Breadcrumb } from 'antd';
import { UserOutlined, LaptopOutlined, NotificationOutlined } from '@ant-design/icons';
import { Link, matchRoutes, Outlet, useLocation } from 'react-router-dom';
import { useEffect, useState } from 'react';
import { routers } from './router';

const { SubMenu } = Menu;
const { Header, Content, Sider } = Layout;

export default function AppLayout() {
  const location = useLocation();
  const [defaultSelectedKeys, setDefaultSelectedKeys] = useState<string[]>([]);
  const [defaultOpenKeys, setDefaultOpenKeys] = useState<string[]>([]);
  const [isInit, setIsInit] = useState<Boolean>(false)

  useEffect(() => {
    const routes = matchRoutes(routers, location.pathname); // 返回匹配到的路由數(shù)組對(duì)象,每一個(gè)對(duì)象都是一個(gè)路由對(duì)象
    const pathArr: string[] = [];
    if(routes !== null) {
      routes.forEach((item) => {
      const path = item.route.path;
        if(path) {
          pathArr.push(path);
        }
      })
    }
    setDefaultSelectedKeys(pathArr);
    setDefaultOpenKeys(pathArr);
    setIsInit(true);
  }, [location.pathname]);
  if(!isInit) {
    return null;
  }
  return (
    <>
      <Layout>
        <Header className="header">
          <div className="logo" />
            
          <Menu theme="dark" mode="horizontal" defaultSelectedKeys={['2']}>
            <Menu.Item key="1">nav 1</Menu.Item>
            <Menu.Item key="2">nav 2</Menu.Item>
            <Menu.Item key="3">nav 3</Menu.Item>
          </Menu>
        </Header>
        <Layout>
          <Sider width={200} className="site-layout-background">
            <Menu
              mode="inline"
              {/* 根據(jù)url地址實(shí)現(xiàn)選中高亮 */}
              defaultSelectedKeys={defaultSelectedKeys}   
              defaultOpenKeys={defaultOpenKeys}
              style={{ height: '100%', borderRight: 0 }}
            >
              <SubMenu key="/user" icon={<UserOutlined />} title="用戶(hù)管理">
                <Menu.Item key="/user/list">
                  <Link to='/user/list'>用戶(hù)信息</Link>
                </Menu.Item>
                <Menu.Item key="2">option2</Menu.Item>
                <Menu.Item key="3">option3</Menu.Item>
                <Menu.Item key="4">option4</Menu.Item>
              </SubMenu>
              <SubMenu key="sub2" icon={<LaptopOutlined />} title="subnav 2">
                <Menu.Item key="5">option5</Menu.Item>
                <Menu.Item key="6">option6</Menu.Item>
                <Menu.Item key="7">option7</Menu.Item>
                <Menu.Item key="8">option8</Menu.Item>
              </SubMenu>
            </Menu>
          </Sider>
          <Layout style={{ padding: '0 24px 24px' }}>
            <Breadcrumb style={{ margin: '16px 0' }}>
              <Breadcrumb.Item>Home</Breadcrumb.Item>
              <Breadcrumb.Item>List</Breadcrumb.Item>
              <Breadcrumb.Item>App</Breadcrumb.Item>
            </Breadcrumb>
            <Content
              className="site-layout-background"
              style={{
                padding: 24,
                margin: 0,
                minHeight: 280,
              }}
            >
              <Outlet />
            </Content>
          </Layout>
        </Layout>
      </Layout>
    </>
  )
}
  • User.tsx 組件
    useSearchParams: 一個(gè)語(yǔ)法糖,用于通過(guò)URLSearchParams接口讀取和寫(xiě)入搜索參數(shù)。
    URLSearchParams有g(shù)et、getAll、has、set、sort、toString等方法
    用來(lái)獲取URL傳遞的參數(shù)
import React from 'react'
import { Link, useSearchParams } from 'react-router-dom'

export default function User() {
  const [params, _] = useSearchParams();
  console.log(params.get("qq"));  // 獲取參數(shù)
  return (
    <div>
     <ul>
       <li><Link to={'/user/detail/1'}>用戶(hù)1</Link></li>
       <li><Link to={'/user/detail/2'}>用戶(hù)2</Link></li>
       <li><Link to={'/user/detail/3'}>用戶(hù)3</Link></li>
       <li><Link to={'/user/detail/4'}>用戶(hù)4</Link></li>
     </ul>
    </div>
  )
}
  • Detail.tsx 組件
    useParams: 從當(dāng)前參數(shù)返回動(dòng)態(tài)參數(shù)的鍵/值對(duì)的對(duì)象與路由路徑匹配的URL
import { useParams } from  'react-router-dom'

export default function Detail() {
  const params = useParams();

  return (
    <div>
      user Detail-- {params.id}
    </div>
  )
}

下面就是Login登陸組件了

  import { useNavigate } from 'react-router-dom';
  import { Button } from 'antd';
  export default funciton Login()  {
    const navigate = useNavigate();
    funciton clickToUser() {
      navigate('/');
    };

    return <Button type='primary' onClick={clickToUser} >登陸<Button/>
  }

差不多就是這個(gè)樣子,完事之后是這個(gè)樣子的

image.png

點(diǎn)擊用戶(hù)
image.png

就沒(méi)了,有興趣的同學(xué)可以嘗試一下@~@

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容