React RouterV6

文章目录

  • 1.Router(V6)简介
  • 2.对比V5
  • 3.用法详解
    • 3.1一级路由和多级路由
    • 3.2指定默认路由
    • 3.3路由重定向
    • 3.4 404
    • 3.5代码
    • 3.6路由嵌套(maizuo.com为例)
  • 4.声明式导航和编程式导航
    • 4.1声明式导航
    • 4.2编程式导航
      • 4.21query(URL Search传参 /detail?id=1000)
      • 4.22路由传参 (/detail/1000) 动态路由
  • 5.路由拦截
  • 6.路由模式
  • 7.withRouter/类组件(自己封装)跳转方法
  • 8.路由懒加载
  • 9.useRoutes钩子配置路由

1.Router(V6)简介

React RouterV6_第1张图片React RouterV6_第2张图片

2.对比V5

React RouterV6_第3张图片

3.用法详解

3.1一级路由和多级路由

React RouterV6_第4张图片
安装
在这里插入图片描述

3.2指定默认路由

React RouterV6_第5张图片
React RouterV6_第6张图片

3.3路由重定向

React RouterV6_第7张图片

3.4 404

React RouterV6_第8张图片

3.5代码

import React from 'react'
import {Navigate, Route,Routes,} from "react-router-dom"
import Film from "../views/Film"
import Cinema from "../views/Cinema"
import Center from "../views/Center"
import NotFound from "../views/NotFound"
export default function Mrouter() {
  return (
    <div>
      <Routes>
        {/* 默认路由 */}
        {/* }> */}
        <Route path="/film" element={<Film />}></Route>
        <Route path="/cinema" element={<Cinema />}></Route>
        <Route path="/center" element={<Center />}></Route>
        {/* 匹配到/ 重定向到film页面 */}
        <Route path="/" element={<Navigate to="/film"></Navigate>}></Route>
        {/* 都没匹配上 跳转到404页面  *符号是全部的意思,所以将这个放在最后,以上路由都没匹配上才执行到这一步的路由匹配*/}
        <Route path="*" element={<NotFound/>}></Route>
      </Routes>
    </div>
  );
}

3.6路由嵌套(maizuo.com为例)

路由嵌套有两种形式
二级路由不代表路由嵌套,如果组件整个被替换,只是路径上嵌套比如/cinema/search,搜索组件完全替换了cinema组件,而film组件下的两个二级路由是Nowplaying和Comingsoon,这两个组件共用film组件的轮播图
比如说这两个组件,上面部分的轮播图是共用的,只有下面的内容是替换的
React RouterV6_第9张图片React RouterV6_第10张图片那么会将这两个组件放在film组件的哪个位置呢,怎么让它们在轮播图的下面而不是上面,就需要路由容器占位了
React RouterV6_第11张图片相对路径的写法:
React RouterV6_第12张图片

React RouterV6_第13张图片

像这种组件完全替换,实际上是平级的组件,但是路径是二级路径的代码需要这样写:
React RouterV6_第14张图片React RouterV6_第15张图片React RouterV6_第16张图片

import React from "react";
import { Navigate, Route, Routes } from "react-router-dom";
import Film from "../views/Film";
import Cinema from "../views/Cinema";
import Search from "../views/Search";
import Center from "../views/Center";
import NotFound from "../views/NotFound";
import Nowplaying from "../views/films/Nowplaying";
import Comingsoon from "../views/films/Comingsoon";
export default function Mrouter() {
  return (
    <div>
      <Routes>
        {/* 默认路由 */}
        {/* }> */}
        <Route path="/film" element={<Film />}>
          {/* 默认路由 没有匹配子组件时,只匹配到父组件默认为Nowplaying路由 */}
          {/* }/> */}
          {/* 默认路由写法2 重定向路径必须写绝对路径*/}
          {/* } /> */}
          {/* 默认路由写法3 */}
          <Route index element={<Navigate to="/film/nowplaying"></Navigate>} />
          {/* 这样的写法是将这两个组件加载到film组件中来 */}
          <Route path="nowplaying" element={<Nowplaying />} />
          <Route path="comingsoon" element={<Comingsoon />} />
        </Route>

        <Route path="/cinema" element={<Cinema />}></Route>
        <Route path="/cinema/search" element={<Search />}></Route>
        <Route path="/center" element={<Center />}></Route>
        {/* 匹配到/ 重定向到film页面 */}
        <Route path="/" element={<Navigate to="/film"></Navigate>}></Route>
        {/* 都没匹配上 跳转到404页面  *符号是全部的意思,所以将这个放在最后,以上路由都没匹配上才执行到这一步的路由匹配*/}
        <Route path="*" element={<NotFound />}></Route>
      </Routes>
    </div>
  );
}

4.声明式导航和编程式导航

a标签相当于于声明式导航
location相当编程式导航
在这里插入图片描述

4.1声明式导航

使用NavLink选项卡,选中哪个哪个会自动有class=active,可以给active设置高亮颜色,但是由于很多人一起开发,别人的acive也会影响到我们的颜色,所以要取另外的名,className给NavLink传了个属性,支持回调函数的写法,回调函数接收到一个形参,解构出来isActive
React RouterV6_第17张图片
React RouterV6_第18张图片Tabbar.js

import React from 'react'
import {NavLink} from "react-router-dom"
export default function Tabbar() {
  return (
    <div>
      <ul>
        <li>
          {" "}
          <NavLink
            to="/film"
            className={({ isActive }) => (isActive ? "xxactive" : "")}
          >
            电影
          </NavLink>
        </li>
        <li>
          {" "}
          <NavLink
            to="/cinema"
            className={({ isActive }) => (isActive ? "xxactive" : "")}
          >
            影院
          </NavLink>
        </li>
        <li>
          <NavLink
            to="/center"
            className={({ isActive }) => (isActive ? "xxactive" : "")}
          >
            我的
          </NavLink>
        </li>
      </ul>
    </div>
  );
}

React RouterV6_第19张图片

4.2编程式导航

更适合携带参数跳转到另一个页面
使用useNavigate跳转,还有一种方法是this.props.history,但是这种方法有的组件不一定能拿到this.props的值

4.21query(URL Search传参 /detail?id=1000)

React RouterV6_第20张图片
React RouterV6_第21张图片
Nowplaying.js

import React, { useEffect, useState} from "react";
import {useNavigate} from "react-router-dom"
import axios from "axios";
export default function Nowplaying() {
  const [list, setList] = useState([]);
  const navigate = useNavigate();
  useEffect(() => {
    axios(
      "https://m.maizuo.com/gateway?cityId=110100&pageNum=1&pageSize=10&type=1&k=4238375",
      {
        headers: {
          "X-Client-Info":
            '{"a":"3000","ch":"1002","v":"5.2.0","e":"16460383564094280654127105","bc":"110100"}',
          "X-Host": "mall.film-ticket.film.list",
        },
      }
    ).then((res) => {
      // console.log(res.data.data.films)
      //setState函数是异步的,这样写是同步写法,可以及时获取到List的值
      // setList((list)=>{
      //   list = res.data.data.films;
      //   console.log(list);
      //   debugger;
      //   return list;
      // });
      // 这样是异步写法,打印的list的值还为空
      setList(res.data.data.films);
      //  console.log(list);
    });
  }, []);
  function handleChangePage(id) {
    navigate(`/detail/?id=${id}`);
  }
  return (
    <div>
      <ul>
        {list.map((item) => (
          <li
            key={item.filmId}
            onClick={() => {
              handleChangePage(item.filmId)
            }}
          >
            {item.name}
          </li>
        ))}
      </ul>
    </div>
  );
}

Detail.js

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

export default function Detail() {
  const [searchParams, setsearchParams] = useSearchParams();
  // console.log(window.location.href);
  //拿到上个页面跳转过来传过来的id
  console.log(searchParams.get("id"));
  return <div>Detail
    <button onClick={()=>setsearchParams({'id':1000})}>猜你喜欢</button>
  </div>;
}

4.22路由传参 (/detail/1000) 动态路由

React RouterV6_第22张图片Nowplaying.js

import React, { useEffect, useState} from "react";
import {useNavigate} from "react-router-dom"
import axios from "axios";
export default function Nowplaying() {
  const [list, setList] = useState([]);
  const navigate = useNavigate();
  useEffect(() => {
    axios(
      "https://m.maizuo.com/gateway?cityId=110100&pageNum=1&pageSize=10&type=1&k=4238375",
      {
        headers: {
          "X-Client-Info":
            '{"a":"3000","ch":"1002","v":"5.2.0","e":"16460383564094280654127105","bc":"110100"}',
          "X-Host": "mall.film-ticket.film.list",
        },
      }
    ).then((res) => {
      // console.log(res.data.data.films)
      //setState函数是异步的,这样写是同步写法,可以及时获取到List的值
      // setList((list)=>{
      //   list = res.data.data.films;
      //   console.log(list);
      //   debugger;
      //   return list;
      // });
      // 这样是异步写法,打印的list的值还为空
      setList(res.data.data.films);
      //  console.log(list);
    });
  }, []);
  function handleChangePage(id) {
    //query传参
    // navigate(`/detail/?id=${id}`);
    //路由传参
    navigate(`/detail/${id}`)

  }
  return (
    <div>
      <ul>
        {list.map((item) => (
          <li
            key={item.filmId}
            onClick={() => {
              handleChangePage(item.filmId)
            }}
          >
            {item.name}
          </li>
        ))}
      </ul>
    </div>
  );
}

Detail.js

import React from "react";
import { useSearchParams, useParams, useNavigate } from "react-router-dom";

export default function Detail() {
  const navigate =useNavigate()
  // console.log(window.location.href);
  // query传参
  // const [searchParams, setsearchParams] = useSearchParams();
  //拿到上个页面跳转过来传过来的id
  //console.log(searchParams.get("id"));

  // 路由传参拿到上个页面跳转过来传来的id的方法
  const params=useParams()
  // 看看params里是什么
  // console.log(params);
  console.log(params.myid);
  return (
    <div>
      Detail
      {/* query传参 */}
      {/*  */}
      {/* 路由传参的跳转到另一个路由地址就得用navigate了 */}
      <button onClick={() => navigate("/detail/1000")}>猜你喜欢</button>
    </div>
  );
}

5.路由拦截

React RouterV6_第23张图片

自己封装的Redirect组件
React RouterV6_第24张图片
Login.js

import React from 'react'
import { useNavigate } from 'react-router-dom';
import Redirect from '../components/Redirect'

export default function Login() {
  const navigate=useNavigate()
  return (
    <div>
      <input type="text"></input>
      
      <button type="button" onClick={()=>{localStorage.setItem("token","ss");
        navigate("/center");
    }}>登录</button>
      
    </div>
  )
}

index.js

import React from "react";
import { Navigate, Route, Routes } from "react-router-dom";
import Redirect from "../components/Redirect"
import Film from "../views/Film";
import Cinema from "../views/Cinema";
import Search from "../views/Search";
import Center from "../views/Center";
import NotFound from "../views/NotFound";
import Detail from "../views/Detail"
import Login from "../views/Login"
import Nowplaying from "../views/films/Nowplaying";
import Comingsoon from "../views/films/Comingsoon";
export default function Mrouter() {
  return (
    <div>
      <Routes>
        {/* 默认路由 */}
        {/* }> */}
        <Route path="/film" element={<Film />}>
          {/* 默认路由 没有匹配子组件时,只匹配到父组件默认为Nowplaying路由 */}
          {/* }/> */}
          {/* 默认路由写法2 重定向路径必须写绝对路径*/}
          {/* } /> */}
          {/* 默认路由写法3 */}
          <Route index element={<Navigate to="/film/nowplaying"></Navigate>} />
          {/* 这样的写法是将这两个组件加载到film组件中来 */}
          <Route path="nowplaying" element={<Nowplaying />} />
          <Route path="comingsoon" element={<Comingsoon />} />
        </Route>

        <Route path="/cinema" element={<Cinema />}></Route>
        <Route path="/cinema/search" element={<Search />}></Route>
        {/* 判断有无token值,有的话isAuth则为真显示Center组件,否则重定向到登录页面,
        但是这样写如果没有token,在登录页面输入登录之后,并不能跳转到center组件来,因为element里的是组件只渲染了一次,
        之前的render写法里面是回调函数,匹配一次就会执行一次,所以不能这么写 */}
        {/*  : }
        > */}
        <Route
          path="/center"
          element={<AuthComponent>
            <Center></Center>
          </AuthComponent>}></Route>
        <Route path="/login" element={<Login />}></Route>
        {/* }> */}
        {/* 路由传参 */}
        <Route path="/detail/:myid" element={<Detail />}></Route>
        {/* 匹配到/ 重定向到film页面 */}
        <Route path="/" element={<Navigate to="/film"></Navigate>}></Route>
        {/* 都没匹配上 跳转到404页面  *符号是全部的意思,所以将这个放在最后,以上路由都没匹配上才执行到这一步的路由匹配*/}
        <Route path="*" element={<NotFound />}></Route>
      </Routes>
    </div>
  );
}
// function isAuth(){
//   return localStorage.getItem("token")
// }
// 一旦匹配到center路径了,就要渲染AuthComponent组件,判断里面有token,就把内部插槽组件return出去,为假就重定向
// 如果没有token值,当登录页面重新设置token后匹配center路径,组件又会重新渲染,判断token值,有就渲染
//路由拦截组件的封装
// props解构赋值出children
function AuthComponent({children}){
  const isLogin=localStorage.getItem("token")
  return isLogin?children:<Redirect to="/login"/>
}

6.路由模式

BrowserRouter更像平时我们访问的路径,但是需要后端同步配置一下,不然会返回404
React RouterV6_第25张图片HashRouter会让路径多一个#符号
React RouterV6_第26张图片React RouterV6_第27张图片

7.withRouter/类组件(自己封装)跳转方法

被Route包裹的组件会被赋予this.props.history,match等方法,但是被它包裹的组件的子组件没有这个方法,用withRouter组件可以实现,但是V6中没有这个方法了,需要自己封装
函数组件中如果要跳转网页,其实也用不到,可以用navigate(任何组件都可用),但是类组件没有hooks,需要封装withRouter方法

函数组件:将每个li变成一个新的组件FilmItem
Nowplaying.js

import React, { useEffect, useState} from "react";
import axios from "axios";
import FilmItem from "./FilmItem";
export default function Nowplaying() {
  const [list, setList] = useState([]);
  useEffect(() => {
    axios(
      "https://m.maizuo.com/gateway?cityId=110100&pageNum=1&pageSize=10&type=1&k=4238375",
      {
        headers: {
          "X-Client-Info":
            '{"a":"3000","ch":"1002","v":"5.2.0","e":"16460383564094280654127105","bc":"110100"}',
          "X-Host": "mall.film-ticket.film.list",
        },
      }
    ).then((res) => {
      // console.log(res.data.data.films)
      //setState函数是异步的,这样写是同步写法,可以及时获取到List的值
      // setList((list)=>{
      //   list = res.data.data.films;
      //   console.log(list);
      //   debugger;
      //   return list;
      // });
      // 这样是异步写法,打印的list的值还为空
      setList(res.data.data.films);
      //  console.log(list);
    });
  }, []);
 
  return (
    <div>
      <ul>
        {list.map((item) => (
          // 将item传给FilmItem组件
         <FilmItem key={item.filmId} {...item}></FilmItem>
        ))}
      </ul>
    </div>
  );
}

FilmItem.js

import React from "react";
import { useNavigate } from "react-router-dom";
export default function FilmItem(item) {
  const navigate = useNavigate();
  function handleChangePage(id) {
    //query传参
    // navigate(`/detail/?id=${id}`);
    //路由传参
    navigate(`/detail/${id}`);
  }
  return (
    <div>
      <li
        onClick={() => {
          handleChangePage(item.filmId);
        }}
      >
        {item.name}
      </li>
    </div>
  );
}

类组件封装withrouter
React RouterV6_第28张图片
withRouter.js

import React from 'react'
import { useNavigate,useParams,useLocation } from 'react-router-dom'
export default function withRouter(Component) {
  // 高阶函数高阶组件  接收一个组件,return出去一个组件,这里return了一个函数组件function(props){}
  // Nowplaying所有属性传到这了
  return function (props){
    const push = useNavigate();
    const params = useParams();
    const location = useLocation()
    // history里传个对象{push:push,params:params}
    return <Component {...props} history={{ push, params,location }} />;
  }
}
//withRouter(FilmItem)

FilmItem.js

// import React from "react";
// import { useNavigate } from "react-router-dom";
// export default function FilmItem(item) {
//   const navigate = useNavigate();
//   function handleChangePage(id) {
//     //query传参
//     // navigate(`/detail/?id=${id}`);
//     //路由传参
//     navigate(`/detail/${id}`);
//   }
//   return (
//     
// // onClick={() => { // handleChangePage(item.filmId); // }} // > // {item.name} // //
// ); // } // 类组件 import React, { Component } from "react"; import withRouter from "../../components/withRouter"; class FilmItem extends Component { handleChangePage(id){ // console.log(id); this.props.history.push(`/detail/${id}`); // this.props.history.push跳转页面 // this.props.history.match获取参数 // this.props.history.location获取当前路由 console.log(this.props.history); } render() { console.log(this.props); return ( <div> <li onClick={() => { this.handleChangePage(this.props.filmId); }} > {this.props.name} </li> </div> ); } } export default withRouter(FilmItem)

8.路由懒加载

由于路由过多,导致首页加载慢,渲染慢,我们使用路由懒加载后是按需加载且只加载一次
React RouterV6_第29张图片可以看到再次进入cinema不再加载,只加载了第一次
React RouterV6_第30张图片写成路由懒加载的写法后:
router文件夹下的index.js

import React from "react";
import { Navigate, Route, Routes } from "react-router-dom";
import Redirect from "../components/Redirect";
// import Film from "../views/Film";
// import Cinema from "../views/Cinema";
// import Search from "../views/Search";
// import Center from "../views/Center";
// import NotFound from "../views/NotFound";
// import Detail from "../views/Detail";
// import Login from "../views/Login";
// import Nowplaying from "../views/films/Nowplaying";
// import Comingsoon from "../views/films/Comingsoon";
export default function Mrouter() {
  return (
    <div>
      <Routes>
        {/* 默认路由 */}
        {/* }> */}
        <Route path="/film" element={LazyLoad("Film")}>
          {/* 默认路由 没有匹配子组件时,只匹配到父组件默认为Nowplaying路由 */}
          {/* }/> */}
          {/* 默认路由写法2 重定向路径必须写绝对路径*/}
          {/* } /> */}
          {/* 默认路由写法3 */}
          <Route index element={<Navigate to="/film/nowplaying"></Navigate>} />
          {/* 这样的写法是将这两个组件加载到film组件中来 */}
          {/* 路由懒加载写法:Nowplaying是films文件夹下的 */}
          {/* } /> */}
          <Route path="nowplaying" element={LazyLoad("films/Nowplaying")} />
          <Route path="comingsoon" element={LazyLoad("films/Comingsoon")} />
        </Route>
        {/* 传进去的值是字符串,函数需要字符串,而不是变量,且找不到这个变量,如果有a=1,这里传变量a,函数就会找到1 */}
        <Route path="/cinema" element={LazyLoad("Cinema")}></Route>
        <Route path="/cinema/search" element={LazyLoad("Search")}></Route>
        {/* 判断有无token值,有的话isAuth则为真显示Center组件,否则重定向到登录页面,
        但是这样写如果没有token,在登录页面输入登录之后,并不能跳转到center组件来,因为element里的是组件只渲染了一次,
        之前的render写法里面是回调函数,匹配一次就会执行一次,所以不能这么写 */}
        {/*  : }
        > */}
        <Route
          path="/center"
          element={<AuthComponent>{LazyLoad("Center")}</AuthComponent>}
        ></Route>
        <Route path="/login" element={LazyLoad("Login")}></Route>
        {/* }> */}
        {/* 路由传参 */}
        <Route path="/detail/:myid" element={LazyLoad("Detail")}></Route>
        {/* 匹配到/ 重定向到film页面 */}
        <Route path="/" element={<Navigate to="/film"></Navigate>}></Route>
        {/* 都没匹配上 跳转到404页面  *符号是全部的意思,所以将这个放在最后,以上路由都没匹配上才执行到这一步的路由匹配*/}
        <Route path="*" element={LazyLoad("NotFound")}></Route>
      </Routes>
    </div>
  );
}
// function isAuth(){
//   return localStorage.getItem("token")
// }
// 一旦匹配到center路径了,就要渲染AuthComponent组件,判断里面有token,就把内部插槽组件return出去,为假就重定向
// 如果没有token值,当登录页面重新设置token后匹配center路径,组件又会重新渲染,判断token值,有就渲染
//路由拦截组件的封装
// props解构赋值出children
function AuthComponent({ children }) {
  const isLogin = localStorage.getItem("token");
  return isLogin ? children : <Redirect to="/login" />;
}
// 路由懒加载的封装
const LazyLoad = (path) => {
  const Comp = React.lazy(() => import(`../views/${path}`));
  return (
    <React.Suspense fallback={<>加载中...</>}>
      <Comp />
    </React.Suspense>
  );
};

9.useRoutes钩子配置路由

路由改成钩子配置路由
React RouterV6_第31张图片router下的index.js

import React from "react";
import { useRoutes, Navigate } from "react-router-dom";
import Redirect from "../components/Redirect";
export default function Mrouter() {
  const element = useRoutes([
    {
      path: "/film",
      element: LazyLoad("Film"),
      // 子路径
      children: [
        // 重定向
        {
          path: "",
          element: <Redirect to="/film/nowplaying"></Redirect>,
        },
        {
          path: "nowplaying",
          // 文件夹films下的Nowplaying  路径
          element: LazyLoad("films/Nowplaying"),
        },
        {
          path: "comingsoon",
          element: LazyLoad("films/Comingsoon"),
        },
      ],
    },
    {
      path: "/cinema",
      element: LazyLoad("Cinema"),
    },
    {
      path: "/cinema/search",
      element: LazyLoad("Search"),
    },
    {
      path: "/login",
      element: LazyLoad("Login"),
    },
    {
      path: "/center",
      element: <AuthComponent>{LazyLoad("Center")}</AuthComponent>,
    },
    {
      path: "/detail/:myid",
      element: LazyLoad("Detail"),
    },
    {
      path: "/",
      element: <Navigate to="/film"></Navigate>,
    },
    {
      path: "*",
      element: LazyLoad("NotFound"),
    },
  ]);
  return element;
}
function AuthComponent({ children }) {
  const isLogin = localStorage.getItem("token");
  return isLogin ? children : <Redirect to="/login" />;
}
// 路由懒加载的封装
const LazyLoad = (path) => {
  const Comp = React.lazy(() => import(`../views/${path}`));
  return (
    <React.Suspense fallback={<>加载中...</>}>
      <Comp />
    </React.Suspense>
  );
};

你可能感兴趣的:(React,react.js)