React router(v6)

V6的改变

  • 原来的component、render、children属性合并成element属性;
  • < Routes >替换 < Switch >;
  • 新增 < Outlet > 给子路由占位;
  • 支持子路由中的路径为相对于父组件的相对路径(Link和Route都改),v6以下只能用绝对路径;
  • 删除 < Redirect >,使用 < Navigate />实现重定向;
  • 删除query方式传参;

V6的用法

嵌套路由 + 默认路由

  1. v6支持 < Route > 直接嵌套
//index入口
 <Routes>
    <Route path="/" element={<Layout />}> //一级路由
       <Route index element={<About />} />//一级路由的默认路由(用index)
       <Route path="about" element={<About />} />//注意路径都是相对路径(相对于父路由)
       <Route path="home" element={<Home />}>
         <Route index element={<Child1 />} />//二级路由的默认路由
         <Route path="child1" element={<Child1 />} />
         <Route path="child2" element={<Child2 />} />
       </Route>
    </Route>
 </Routes>

//一级路由Layout(二级路由也是一样)
import React from "react";
import {
  BrowserRouter as Router,
  Link,
  Route,
  NavLink,
  Outlet,
} from "react-router-dom";

const Layout = () => {
  return (
    <div>
      <Link to="/about">About</Link>
      <Link to="/home">Home</Link>
      <Outlet/> //使用Outlet给子路由占位(Layout中嵌套的路由渲染在此处)
    </div>
  );
};

export default Layout;

上面的用法相当于:

//index入口
 <Routes>
    <Route path="/" element={<Layout />}>
       <Route index element={<About />} />
       <Route path="about" element={<About />} />
       <Route path="home/*" element={<Home />} />
    </Route>
 </Routes>

//二级路由Home(和一级路由一样)
import React from 'react'
import { BrowserRouter as Router, Link, Outlet, Route, NavLink, Routes } from 'react-router-dom'
import Child1 from './Child1';
import Child2 from './Child2';

export default function Home(props) {
  return (
    <div>
      <h2>我是Home</h2>
      <Link to='child1'>msg1</Link>
      <Link to='child2'>msg2</Link>
      <Routes> //这个部分相当于上面的<Outlet />
        <Route index element={<Child1 />} />
        <Route path='/child1' element={<Child1 />} />
        <Route path='/child2' element={<Child2 />} />
      </Routes>
    </div>
  )
}

默认路由

  1. 参考上面使用index指定默认路由
<Routes>
   <Route index element={<Child1 />} />
   <Route path='/child1' element={<Child1 />} />
   <Route path='/child2' element={<Child2 />} />
</Routes>
  1. 与之前版本的用法相同,增加一个 < Route > ,需要精准匹配,路径为父路由的根路径
//外层路由
 <Routes>
    <Route path="/" element={<Layout />}>
       <Route index element={<About />} />
       <Route path="about" element={<About />} />
       <Route path="home/*" element={<Home />} />//从父组件往后匹配
    </Route>
 </Routes>
//父组件内
<Routes>
   <Route exact path={"/"} element={<Child1 />} /> //path可以是'/'或者''
   <Route path='/child1' element={<Child1 />} />
   <Route path='/child2' element={<Child2 />} />
</Routes>

动态传参

依旧有params、state两种传参方式,但是删去了query方式,并且state传参方式有所改变;

  1. 通过params传参(和之前版本相同)
//传参
<Link to='/about/123'>About</Link>
<Route path='/about/:id' element={<About />} />

//About组件中获取参数(需要用useParams获取)
import React from 'react'
import { BrowserRouter as Router, useLocation, Route, NavLink, useParams } from 'react-router-dom'

export default function About(props) {
  let params = useParams();
  console.log('params', params);
  return (
    <div>我是About</div>
  )
}

在这里插入图片描述

  1. 通过state传参(直接给Link传state和to属性)原来的那种方式不可行了
//传参
import React from 'react'
import { BrowserRouter as Router, Link, Routes, Route} from 'react-router-dom'
import About from './About';
import Home from './Home';
import Child1 from './Child1';
import Child2 from './Child2';

export default function index() {
  let data = { s: '我是state传参' }
  return (
    <div>
      <Link to='/about' state={data}>About</Link> //state传参
      <Link to='/home'>Home</Link>
      <Routes>
        <Route path='/about' element={<About />} />
        <Route path='/home/*' element={<Home />} />
      </Routes>
    </div>
  )
}

//获取
import React from 'react'
import { BrowserRouter as Router, useLocation } from 'react-router-dom'

export default function About(props) {
  let { state } = useLocation();
  console.log('state', state);
  return (
    <div>我是About</div>
  )
}

在这里插入图片描述

注意:如果有通过params传参,它会将参数拼接到url后面,因此所有需要用到这个路由的地方都必须用完整的path(包含参数)

<Link to='/about/123'>About</Link>
<Route path='/about/:id' element={<About />} /> //下面需要对应

//需要跳转的页面
import { Button } from 'antd';
import React from 'react'
import { BrowserRouter as Router, useNavigate, Link, Outlet, Route, Routes } from 'react-router-dom'
import Child1 from './Child1';
import Child2 from './Child2';

export default function Home(props) {
  let navigate = useNavigate();
  return (
    <div>
      <h2>我是Home</h2>
      <Link to='child1'>msg1</Link>
      <Link to='child2'>msg2</Link>
      <Outlet />
      <Button onClick={() => navigate('/about/:id', { state: { name: 'xiaowang', age: 18 } })}>跳转到About</Button> //必须是完整path -----/about/:id
    </div>
  )
}

React-router Hook

  1. useLocation(与之前版本相同,返回当前url的动态的location对象)
    可以获取state方式传的参数(包括通过useNavigate传参),都是从location对象的state中获取。

在这里插入图片描述

  1. useNavigate(v6新增)(代替useHistory)
    可以用它实现跳转路由并传参,接收一个参数path或者path+参数对象两个参数;
    返回一个函数用来实现编程式导航(编程式导航就是写js,类似history.push(),声明式导航是a标签这种直接渲染在页面上的写法)。

(1)直接传入数字(1,-1),模拟的是浏览器的前进回退,相当于history.go()。

let navigate=useNavigate();
navigate(1);//前进一步 相当于history.go(1)
navigate(-1);//后退一步 相当于history.go(-1)

在这里插入图片描述

(2)传入path和state

//useNavigate实现跳转
import { Button } from 'antd';
import React from 'react'
import { BrowserRouter as Router,useLocation,useNavigate, Link, Outlet, Route, NavLink, Routes } from 'react-router-dom'
import Child1 from './Child1';
import Child2 from './Child2';

export default function Home(props) {
  let navigate=useNavigate();
  console.log('navigate',navigate);
  return (
    <div>
      <h2>我是Home</h2>
      <Link to='child1'>msg1</Link>
      <Link to='child2'>msg2</Link>
      <Outlet />
      <Button onClick={()=>navigate('/about')}>跳转到About</Button> //接收一个path实现跳转
      <Button onClick={() => navigate('/about', { state: { name: 'xiaowang', age: 18 } })}>跳转到About</Button>//跳转并传参,path+state参数
    </div>
  )
}

在这里插入图片描述

//useLocation获取参数
import React from 'react'
import { BrowserRouter as Router, useLocation, Route, NavLink } from 'react-router-dom'

export default function About() {
  let {state} = useLocation(); //useNavigate传参需要通过useLocation获取参数
  console.log('state', state);

  return (
    <div>我是About</div>
  )
}

在这里插入图片描述

  1. useParams(v6新增)
    可以用它获取通过params方式传的参数
let params = useParams();
console.log('params', params);

在这里插入图片描述

  1. useOutlet(v6新增)
    返回当前嵌套路由组件下已经渲染的子路由,没有或者没有挂载就返回null
import React from "react";
import { Outlet, useOutlet} from "react-router-dom";

const Home = () => {
  console.log('useOutlet',useOutlet());
  return (
    <div>
      <Outlet />
    </div>
  );
};

export default Home;

返回Home子路由Children1或者Children2(选中的那一个)
在这里插入图片描述

5.useRoutes(v6新增)
接收一个路由配置数据,生成对应的路由表(路由元素),可以在动态路由中使用。(可以看下面动态路由的示例)

6.useSearchParams(v6新增)(用于修改并获取修改后的参数的方法)
与usestate用法相同,里面暴露了一组数据[search,setSearch],search是一个包含参数内容的对象(就是url后面?&连接的那部分内容)(不是state传参的state对象,也不是params对象),需要用get方法获取上面的属性;setSearch是用于改变search对象的方法。

  • 注意:初始使用state传参,setSearch会使得state为null
import { Button } from "antd";
import React, { useEffect } from "react";
import {
  BrowserRouter as Router,
  useSearchParams,
  Route,
  NavLink,
  useParams,
} from "react-router-dom";

export default function Search() {
  let [search, setSearch] = useSearchParams();
  let params=useParams();//获取通过params方式传递的参数
  let {params}=useMatch('/search/:id');//上面获取params也可以用useMatch,需要传path
  console.log(params)//{id: '123'}
  
  useEffect(() => {
    console.log("search", search);
    console.log('setSearch',setSearch);
  }, [search]);

  return (
    <div>
      <h1>Search</h1>
      <h3>我是初始参数id:{params.id}</h3>
      <h3>我是添加的参数name:{search.get('name')}</h3>
      <h3>我是添加的参数age:{search.get('age')}</h3>
      <Button onClick={() => setSearch({ name: "xiaowang", age: 18 })}>
        改变传参
      </Button>
    </div>
  );
}

打印的search和setSearch:
在这里插入图片描述

使用setSearch之前的参数要用useParams或者useMatch获取;
在这里插入图片描述

在这里插入图片描述

  1. useMatch(与之前版本的useRouteMatch相似,内部属性有所变化)(可以获取到match对象,里面包含的传过来的params)用法可以参照上面的例子🌰
  let match=useMatch('/search/:id');
  let {params} =useMatch('/search/:id');//直接解构获取params
  console.log(match);

在这里插入图片描述

  1. useInRouterContext(v6新增)
    用于判断当前组件是否处于Router的上下文之中(也就是有没有被< BrowserRouter >包裹),对应的返回true/false
//mac index文件
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import {
  BrowserRouter as Router,
  Route,
  NavLink,
  useInRouterContext,
} from "react-router-dom";
import Mac from "./mac";

const NotRouter = () => {
let flag = useInRouterContext();
console.log('flag',flag);//flag false
return <div>测试useInRouterContext:{!flag?'我是false':'我是true'}</div>;
};

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <React.StrictMode>
    <Router>
      <Mac />//被<Router>包裹 
    </Router>
    <NotRouter />//没有被<Router>包裹
  </React.StrictMode>
);

//Mac组件
console.log('useInRouterContext',useInRouterContext()); //useInRouterContext true
  1. useNavigationType
    返回当前的导航类型(是如何跳转到这个path的),返回pop(浏览器直接打开这个路径/刷新页面)/push/replace
//instance1
import React from "react";
import { useNavigationType } from "react-router-dom";
import { Button } from 'antd';

export default function About(props) {
console.log('useNavigationType',useNavigationType());//Link进入打印useNavigationType PUSH;默认都是PUSH;在路由刷新打印useNavigationType POP
  return (
    <div>
      <h2>我是About</h2>
    </div>
  );
}
//instance2
import { Button } from "antd";
import React, { useEffect } from "react";
import {
  BrowserRouter as Router,
  useSearchParams,
  Route,
  NavLink,
  useParams,
  useMatch,
  useLocation,
  useNavigationType,
} from "react-router-dom";

export default function Search() {
  let [search, setSearch] = useSearchParams();
  let { state } = useLocation();
  console.log("search", useNavigationType());//直接Link打开路由界面打印 search PUSH;Navigate重定向打印search REPLACE;

  return (
    <div>
      <h1>Search</h1>
      <h3>我是初始参数id:{state?.id}</h3>
      <h3>我是添加的参数name:{search.get("name")}</h3>
      <h3>我是添加的参数age:{search.get("age")}</h3>
      <Button onClick={() => setSearch({...search, name: "xiaowang", age: 18 })}>
        改变传参
      </Button>
    </div>
  );
}

  1. useResolvedPath
    给定一个url(随便一个url),解析其中的path/search/hash值
console.log('useResolvedPath',useResolvedPath('/home?name=xiaowang&age=18#detail'));

在这里插入图片描述

动态路由

  1. 懒加载+useRoutes实现动态路由,点击跳转demo的gitee仓库

(1)页面展示
在这里插入图片描述

(2)依赖版本
在这里插入图片描述

(3)代码部分

项目入口文件

//mac index文件
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import {
  BrowserRouter as Router,
  Route,
  NavLink,
  useInRouterContext,
} from "react-router-dom";
import Mac from "./mac";

const NotRouter = () => {
  let flag = useInRouterContext();
  console.log("flag", flag);
  return (
    <h3 style={{ padding: "5px" }}>
      NotRouter--测试useInRouterContext:{!flag ? "我是false" : "我是true"}
    </h3>
  );
};

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <React.StrictMode>
    <Router>//直接把< Router >放在最外层
      <Mac />
    </Router>
    <NotRouter />
  </React.StrictMode>
);

路由index入口文件

import React, { useEffect, Suspense } from "react";
import {
  BrowserRouter as Router,
  useRoutes,
  useNavigate,
  Link,
} from "react-router-dom";
import { Layout } from "antd";
import config from "./config";
import Menus from "./Menus";

const { Sider } = Layout;

const RouteEntry = () => {
  let routes = useRoutes(config);
  return routes;
};

export default function Mac() {
  let navigate = useNavigate();

  //刷新页面后重置路由
  useEffect(() => {
    //performance.navigation.type == 1监听刷新事件 这个已经被弃用了,所以换成performance.getEntriesByType("navigation")[0].type=='reload'
    console.log(performance.getEntriesByType("navigation")[0].type);
    if (performance.getEntriesByType("navigation")[0].type == 'reload') {
      console.info("This page is reloaded");
      navigate("/about"); //注意:⚠️因为这行代码,所以刷新以后About组件中useNavigationType打印PUSH而不是POP
    } else {
      console.info("This page is not reloaded");
    }
  }, []);

  return (
    <div>
      <Layout>
        <Sider width={200} className="site-layout-background">
          <Menus />
        </Sider>
        <Layout style={{ padding: "24px" }}>
          <Suspense fallback={<div>加载中...</div>}>
            <RouteEntry />
          </Suspense>
        </Layout>
      </Layout>
    </div>
  );
}

config配置文件

import React, { lazy } from "react";
import {
  SmileTwoTone,
  ProfileTwoTone,
  ContactsTwoTone,
  GithubOutlined,
  TwitterOutlined,
} from "@ant-design/icons";

const handleLazy = (name) => {
  let Comp = lazy(() => import(`./${name}`));
  return <Comp />;//element需要传入<Element />这种形式,与component不同
};

const config = [
  {
    key: "about",
    label: "首页",
    index: 1, //默认展示 没有path属性,添加index属性
    icon: <SmileTwoTone />,
    element: handleLazy("About"),
    exact: 1, //1代表true,用1或者0代替true/false更好(因为React对boolean类型的attribute的识别方式问题)
  },
  {
    key: "about",
    label: "首页",
    icon: <SmileTwoTone />,
    path: "/about",
    element: handleLazy("About"),
    exact: 1,
  },
  {
    key: "home",
    label: "嵌套路由",
    icon: <ProfileTwoTone />,
    path: "/home",
    element: handleLazy("Home"),
    children: [
      {
        key: "child1",
        label: "son1",
        index: 1, //默认展示 没有path属性,添加index属性
        element: handleLazy("Children1"),
        exact: 1,
      },
      {
        key: "child1",
        label: "son1",
        icon: <GithubOutlined />,
        path: "/home/child1",
        element: handleLazy("Children1"),
        exact: 1,
      },
      {
        key: "child2",
        icon: <TwitterOutlined />,
        label: "son2",
        path: "/home/child2",
        element: handleLazy("Children2"),
        exact: 1,
      },
    ],
  },
  {
    key: "search",
    label: "Search",
    icon: <ContactsTwoTone />,
    path: "/search",
    state: { id: 123 },
    element: handleLazy("Search"),
    exact: 1,
  },
  {
    key: "navigate",
    label: "重定向",
    navigate:1, //加这个属性重定向
    icon: <ContactsTwoTone />,
    state: { winter: 'yes' },
    path:'/search',
  },
];

export default config;

Menus菜单(Link组件)

import React, { useState, useEffect } from "react";
import { Link } from "react-router-dom";
import { Menu } from "antd";
import config from "./config";
import PubSub from "pubsub-js";

const Menus = () => {
  const [selectedKeys, setSelectedKeys] = useState([]);
  const [openKeys, setOpenKeys] = useState([]);

  const handleLabel = (value) => {
    if(!value.path)return;
    return value.children
      ? {
          ...value,
          children: value.children.map((child) => handleLabel(child)),
        }
      : {
          ...value,
          label: <Link to={value.path} state={value.state}>{value.label}</Link>, //这里增加了state参数
        };
  };

  useEffect(() => {
    let openkeys=config.map(l=>{
      if(l.children)return l.key
    })
    setOpenKeys(openkeys);
    setSelectedKeys(config[0].key)
  }, [config])
  
  useEffect(() => {
    PubSub.subscribe("backtohome", (_, data) => {
      setSelectedKeys([data]);
    });
  }, []);

  return (
    <Menu
      mode="inline"
      openKeys={openKeys} //默认展开嵌套路由
      selectedKeys={selectedKeys}
      onClick={(v) => setSelectedKeys([v.key])}
      style={{
        height: "100%",
        borderRight: 0,
      }}
      items={config.map((i) => handleLabel(i))}
    ></Menu>
  );
};

export default Menus;

首页About(刷新后默认展示)

import React from "react";
import {useNavigationType} from "react-router-dom";

export default function About(props) {
  console.log("useNavigationType", useNavigationType());//刷新后打印useNavigationType PUSH,通过Link进入也打印useNavigationType PUSH
  return (
    <div>
      <h2>我是About</h2>
    </div>
  );
}

嵌套路由Home

import React from "react";
import { Outlet } from "react-router-dom";

const Home = () => {
  return (
    <div>
      <Outlet /> //只需要占位
    </div>
  );
};

export default Home;

Children1(嵌套子路由文件)

import { Button } from 'antd';
import React from 'react'
import { BrowserRouter as Router,useLocation,useNavigate, Link, Outlet, Route, NavLink, Routes } from 'react-router-dom'
import PubSub from "pubsub-js";

const Children1 = (porps) => {
  let navigate=useNavigate();

  const handleClick = () => {
    PubSub.publish('backtohome','about');
    navigate('/about', { state: { name: 'xiaowang', age: 18 } });
  };

  return (
    <div>
      <ul>
        <li>我是children1</li>
        <li>我是children1</li>
        <li>我是children1</li>
        <li>我是children1</li>
        <li>我是children1</li>
      </ul>
      <Button onClick={handleClick}>跳转到About</Button>
    </div>
  );
};

export default Children1;

常用组件

新增Navigate(重定向,代替< Redirect >)
Navigate有to和replace两个属性,replace属性用于改变跳转方式(push 或 replace,默认是push也就是默认false)。
路由的跳转有两种模式,push和replace,push模式会将这个url压入路由history栈顶; 而replace模式会将栈顶的url替换 (也就是是否可以回退的区别)。

 <Navigate to="/about" replace={true}/> //replace模式 替换url

Link/NavLink/Outlet(新增)/BrowserRouter/HashRouter就不再赘述了

V6以下版本详解快捷通道:点击查看文章—React router (v6以下版本)

  • 4
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值