React Route5 路由

💻 React Route5 路由🏠专栏:React

👀个人主页:繁星学编程🍁

🧑个人简介:一个不断提高自我的平凡人🚀

🔊分享方向:目前主攻前端,其他知识也会阶段性分享🍀

👊格言:☀️没有走不通的路,只有不敢走的人!☀️

👉让我们一起进步,一起成为更好的自己!!!🎁

文章目录

React Route5 路由

一切皆组件,默认是history模式

安装

shell 复制代码
yarn add react-router-dom@5

起步

(1) 从react-router-dom中引入BrowserRouter

js 复制代码
import { BrowserRouter } from 'react-router-dom'

如果想要使用路由,那么所有和路由相关的内容都要放在BrowserRouter的里面

html 复制代码
<BrowserRouter>
    <h2>Hello Router</h2>  
</BrowserRouter>

注:如果整个项目都要使用路由,可以将BrowserRouter组件写到index.js项目入口文件中,并包裹组件。

(2) 从react-router-dom中引入Link

html 复制代码
import { Link } from 'react-router-dom'
<BrowserRouter>
  <ul>
    <li>
      <Link to="home">首页</Link>
    </li>
    <li>
      <Link to="about">关于页</Link>
    </li>
  </ul>
</BrowserRouter>

Link组件相当于Vue中的router-link

Link的to属性用于改变url

(3) 从react-router-dom中引入Route

js 复制代码
import { Route } from 'react-router-dom'

Route组件是用来切换内容,相当于Vue中的router-view

js 复制代码
<Route path="/home" component={Home}></Route>
<Route path="/about" component={About}></Route>

Route组件中的所有属性:

  • path:指定 URL 路径匹配规则的字符串,可包含变量和正则表达式。
  • exact:当为 true 时,只有 URL 路径与 path 属性完全匹配时才渲染当前组件。
  • strict:当为 true 时,要求 URL 路径结尾必须为斜线 /
  • sensitive:当为 true 时,表示 URL 匹配时,将大小写视为敏感。
  • component:指定路由匹配成功后要渲染的 React 组件。
  • render:一个返回 React 组件的函数,可以根据需要动态渲染组件。
  • children:一个返回 React 元素的函数,无论 URL 是否匹配,都将被调用并渲染为子元素。

react的路由是:包容性路由(无论匹没匹配到想要的路由,都会匹配一遍所有的路由)

vue的路由是:排他性路由(从路由表上向下进行匹配,找到就不会向下匹配)

react的路由想要成为排他性路由方式

js 复制代码
// 1. 从react-router-dom中引入Switch
import { Switch } from 'react-router-dom'
// 2. 将Switch组件包裹在Route组件外层
<Switch>
  <Route path="/home" component={Home}></Route>
  <Route path="/about" component={About}></Route>  
</Switch>

Switch组件 是用来包裹Route组件的,用于路由匹配的时候,只匹配一个,也就是将包容性路由转换成排他性路由

存在的问题:当首页的路由是的url路径是:/ 时,会出现无论你点击切换到哪个组件时,Home组件的内容都会显示。

解决方式

  1. 将url为 / 的Route放到最下面
  2. 在Route中添加 exact 属性,进行精准匹配

路由嵌套

路由嵌套就是在其中的一个子路由里面继续使用Link和Route组件

js 复制代码
import React from 'react'
import { Link, Route } from 'react-router-dom';
import Detail from './Detail'
const Home = () => {
    return (
        <>
            <p>欢迎进入首页</p>
            <ul>
                <li>
                    <Link to='/home/detail'>详情页</Link>
                </li>
            </ul>
            <hr />
            <Route path='/home/detail' component={Detail}></Route>
        </>
    );
}

export default Home;

注意:如果一个路由有下一级的路由,那么这个路由就不要加exact属性,会使得当前路由及其子路由都显示不出来。

当一个组件变成路由组件后,那么props里面会自动有路由信息。

此时可以通过:

js 复制代码
// 类组件
const {
    match: { path },
} = this.props
// 函数组件
const { match: { path } } = props

拿到一级路由的url,就可以在编写二级路由的url的时候,使用变量代替一级路由的url。

这样写的好处:只要直接修改一级路由的url所有的二级路由中的url就可以不需要修改,直接使用。

js 复制代码
const {
    match: { path },
} = this.props
// 原来写法:
<Route path="/home/sale" component={Sale}></Route>
<Route path="/home/about" component={About}></Route>
// 使用变量:
<Route path={`${path}/sale`} component={Sale}></Route>
<Route path={`${path}/about`} component={About}></Route>

动态路由

params传参(/detail/:id)

react中动态路由的写法和vue是完全一样的,也是例:/detail/:id

html 复制代码
<ul>
  <li>
    <Link to="/home/detail/234">详情页</Link>
  </li>
  <li>
    <Link to="/home/detail/567">详情页</Link>
  </li>
</ul>
<Route path="detail/:id" component={Detail}></Route>
js 复制代码
const Detail = (props) => {
    // ? 可选链操作符,表示可有可无
    const id = props.match?.params?.id;
}

query传参(/detail?id=123&name='zs')

传递/detail?id=123&name='zs'

  • 方式一:字符串格式
html 复制代码
// 方式一:
<Link to="/detail?id=123&name=zs">详情页</Link>
<Route path="detail" component={Detail}></Route>
  • 方式二:对象格式
html 复制代码
// 方式二:
<Link to={{
          pathname: '/detail',
          search: '?id=123&name=zs',
          hash: '#abz',
          // 在url上面不可见的传参方式
          state:{
            x: 10,
            y: 20
            }
         }}></Link>
<Route path="detail" component={Detail}></Route>

获取/detail?id=123&name='zs'中的id

  • 方式一:查询字符串转对象
js 复制代码
// 要转换的查询字符串
var str = "name=zs&age=18";
// 封装一个函数
function parseQueryString(str) {
    // 准备一个接收的空对象
    var obj = {};
    // 将每一个数值分隔开
    var s1 = str.split('&');
    // console.log(s1); // ['name=zs', 'age=18']
    // 将数组中的值依次放入对象中
    s1.forEach(function(item) {
        // 将每个数组里面的字符串按照等号分割开,得到若干个数组
        var t = item.split('=');
        var key = t[0];
        var value = t[1];
        obj[key] = value;
    });
    return obj;
}
// 调用函数
var result = parseQueryString(str);
console.log(result); // {name: 'zs', age: '18'}
  • 方式二:通过 new URL()
js 复制代码
const Detail = (props) => {
  // 2.通过 new URL():必须有一个基准地址才能解析
  const urlObj = new URL('http://www.a.com' + props.location.search)
  const id = obj.searchParams.get("id")
}
  • 方式三:通过new URLSearchParams()
js 复制代码
  // 3.通过new URLSearchParams()
  const urlObj = new URLSearchParams(props.location.search);
  const id = urlObj.get("id")

路由组件渲染方式

使用component属性来渲染路由组件(很多情况下使用)

js 复制代码
<Route path="/home" component={Home}></Route>

使用render属性来渲染路由组件

js 复制代码
<Route path="/about" render={About}></Route>
  • 与component属性方式的区别:render属性不能渲染类组件

    如果想要渲染类组件可以通过:在类组件的外面套一层函数(() => )

    因此,通过render属性里面的函数形式,就可以进行条件判断,进行路由鉴权操作

js 复制代码
<Route path="/about" render={
 (props) => {
   // 做路由鉴权
   if(token){
       <Cart/>
   }else{
       <Login/>
   }
}>
</Route>

使用children属性来渲染路由组件

js 复制代码
<Route path="/mine" children={Mine}></Route>

特点

  • 当没有Switch组件的时候,children的效果是不管是否匹配都会直接渲染
  • 当有Switch组件的时候,children属性效果和render属性完全一样

使用Route插槽来渲染路由组件

js 复制代码
<Route path="/cart">
<Cart />
</Route>

特点:

  • 既可以渲染类组件,也可以渲染函数组件
  • 缺点:但是渲染的组件默认是没有路由信息的

解决没有路由信息方式:

可以通过withRouter

withRouter是一个高阶组件,作用是让那些没有路由信息的组件直接拥有路由信息

js 复制代码
// 1. 从react-router-dom解构withRouter高阶组件
import {withRouter} from "react-router-dom";

// 2. 使用withRouter
class Cart extends React.Component {
  render() {
    console.log(this.props);
    return <div>购物车页面</div>;
  }
}
const Cart2 = withRouter(Cart);

4个hooks来帮助获取

分别是:useHistory、useLocation、useParams、useRouteMatch

相比withRouter,使用hooks方式的好处:hooks不会使组件层级变深

js 复制代码
// 1. 从react-router-dom解构hooks
import {
  useHistory,
  useLocation,
  useParams,
  useRouteMatch,
} from "react-router-dom";
// 2. 函数组件使用
const Cart = () => {
  const location = useLocation();
  console.log(location);
  const histroy = useHistory();
  console.log(histroy);
  const match = useRouteMatch();
  console.log(match);
  const params = useParams();
  console.log(params);
  return <div>购物车页面</div>;
};

路由重定向

解构Redirect组件

js 复制代码
// 从react-router-dom中解构Redirect组件
import { Redirect } from 'react-router-dom';

使用Redirect组件

js 复制代码
<Redirect from="/" to="/home"></Redirect>  
  • from属性:指定源路径
  • to属性:指定目标路径
js 复制代码
// 解构Route和Switch组件
import { Switch, Route } from 'react-router-dom';
<Switch>
  <Redirect from="/" to="/home" exact></Redirect>
  <Route></Route>
</Switch>  

需要注意的点

在使用重定向时,为了防止重定向匹配到与路径相似的所有路径。可以采取以下两种方式:

  • 可以添加exact属性
  • 可以将Redirect组件放所有Route组件的最下面

路由鉴权

封装的路由鉴权的组件

js 复制代码
const Auth = (props) => {
  return (
    <Route
      path={props.path}
      render={() => {
        if (localStorage.getItem("token")) {
          return props.children;
        } else {
          return (
            <Redirect
              from={props.path}
              to={{
                pathname: "/login",
                state: {
                  from: props.path,
                },
              }}
            ></Redirect>
          );
        }
      }}
    ></Route>
  );
};

使用路由

js 复制代码
<Switch>
  <Redirect from="/" to="/home" exact></Redirect>
  <Route path="/home" component={Home}></Route>

  <Auth path="/about">
    <About />
  </Auth>
  <Auth path="/mine">
    <Mine />
  </Auth>
  <Route path="/login">
    <Login />
  </Route>
</Switch>

编程式导航

编程式导航:通过使用histroy对象下面的push/replace/go/goBack/goForward

js 复制代码
// 1. 从react-router-dom中解构useHistory 
import {useHistory} from "react-router-dom";
// 2. 使用编程式导航
const login = () => {
  localStorage.setItem("token", "12345");
  histroy.push(location.state.from);
};

react-router-dom的history对象中常见的方法有以下几种,它们分别是:

  • push(path, [state]): 将新的URL添加到历史记录中,从而进行页面导航。path为要跳转的页面路径,state为要传递给该页面的状态对象。
  • replace(path, [state]): 替换当前历史记录中的URL,并使用新的URL进行导航。path为要跳转的页面路径,state为要传递给该页面的状态对象。
  • go(n): 在浏览器历史记录中前进或后退指定的步数,例如go(1)代表前进一步,go(-1)代表后退一步。
  • goBack(): 后退一步。
  • goForward(): 前进一步。
  • listen(listener): 添加一个持续监听历史记录变化的回调函数,该函数会在历史记录发生变化时被调用,并返回一个用于卸载该回调函数的函数。

NavLink和Link一样,是用来做路由跳转的

Link的功能NavLink都有,并且多了一个高亮的效果

js 复制代码
// 1. 从react-router-dom中解构NavLink
import { Route, NavLink } from "react-router-dom";

<ul>
  <li>
    <NavLink to="/home" activeClassName="select">
      首页
    </NavLink>
  </li>
  <li>
    <NavLink to="/about" activeClassName="select">
      关于页
    </NavLink>
  </li>
</ul>

使用NavLink组件会在组件上自动添加名为active的类名,用于写高亮的样式

如果要修改active的类名,可以直接使用activeClassName类名

NavLink组件也可以添加exact进行高亮的精准匹配

Prompt

Prompt是React Router中的一种路由组件,它用于在用户离开当前页面之前显示一个确认对话框。

作用 :可以防止用户意外地离开当前页面,提示用户保存未提交的信息或确认他们要离开当前页面。

Prompt相当于Vue路由导航守卫中的beforeRouteLeave

Prompt 组件的两个属性

  • when 属性设置为 isDirty,这意味着只有当 isDirtytrue 时,确认对话框才会显示。
  • message 属性用来指定确认对话框上显示的消息。
js 复制代码
import { Prompt } from 'react-router-dom';

function MyForm() {
  const [isDirty, setIsDirty] = useState(false);

  const handleFormChange = () => {
    setIsDirty(true);
  };

  return (
    <form onChange={handleFormChange}>
      <label htmlFor="name">Name:</label>
      <input type="text" id="name" />

      <Prompt
        when={isDirty}
        message="Are you sure you want to leave without saving?"
      />
    </form>
  );
}

404页面

path="*"

js 复制代码
<Switch>
  <Redirect from="/" to="/home" exact></Redirect>
  <Route path="/home" component={Home}></Route>
  <Route path="/about" component={About}></Route>
  {/* 404路由 */}
  <Route path="*" component={NotFound}></Route>
</Switch>

注意点:

  • 要写在所有Route的最下面 不放在最下面,会跳转导致404 Route以下的Route时都跳转404
  • 必须要和Switch组件一起使用 不使用Switch组件时,无论要跳转到哪个页面404页面都会一起存在

路由表

在App.jsx同级目录下新建router文件夹并在其文件夹中新建Index.jsx文

引入根组件及其各级路由

js 复制代码
import React from 'react'
import { Route, Redirect } from 'react-router-dom'
// 引入根组件
import App from '../App';
// 引入一级路由
import Home from '../pages/home/Index'
import Detail from '../pages/detail/Index'
import Login from '../pages/login/Index'
import City from '../pages/city/Index'
import NotFound from '../pages/notfound/Index'
// 引入二级路由
import Movies from '../pages/home/movies/Index'
import Videos from '../pages/home/videos/Index'
import Mini from '../pages/home/mini/Index'
import Show from '../pages/home/show/Index'
import Mine from '../pages/home/mine/Index'
// 引入三级路由
import Hot from '../pages/home/movies/hot/Index'
import Wait from '../pages/home/movies/wait/Index'
import Cinema from '../pages/home/movies/cinema/Index'
import Classic from '../pages/home/movies/classic/Index'

将组件嵌套渲染

js 复制代码
const Index = () => {
  return (
      <App>
          <Redirect from="/" to='/home' exact></Redirect>
          <Route path='/home'>
              <Home>
                  <Redirect from='/home' to='/home/movies' exact></Redirect>
                  <Route path='/home/movies'>
                      <Movies>
                          <Redirect from='/home/movies' to='/home/movies/hot' exact></Redirect>
                          <Route path='/home/movies/hot'><Hot /></Route>
                          <Route path='/home/movies/wait'><Wait /></Route>
                          <Route path='/home/movies/cinema'><Cinema /></Route>
                          <Route path='/home/movies/classic'><Classic /></Route>
                      </Movies>
                  </Route>
                  <Route path='/home/videos'><Videos /></Route>
                  <Route path='/home/mini'><Mini /></Route>
                  <Route path='/home/show'><Show /></Route>
                  <Route path='/home/mine'><Mine /></Route>
              </Home>
          </Route>
          <Route path='/detail/:id'><Detail /></Route>
          <Route path='/login'><Login /></Route>
          <Route path='/city'><City /></Route>
          <Route path='*'><NotFound /></Route>
      </App>
  );
}

导出Index组件

js 复制代码
export default Index;

在入口文件index.js中,将原本引入App组件改为引入路由组件Index

在原先放置各级路由的组件中使用props.children接收路由表中的路由

js 复制代码
// eg:
const App = (props) => {
  return (
      <>
          <Switch>
              {props.children}
          </Switch>
      </>
  );
}

路由懒加载

从react中解构lazy方法,然后进行按需引入

js 复制代码
// 1.从react中解构lazy方法
import { lazy } from 'react'

原来写法

js 复制代码
import Home from '../pages/home/Index'

改为按需引入

js 复制代码
const Home = lazy(() => import('../pages/home/Index'))

封装懒加载组件

fallback={}中可以是标签或组件<Child/>

js 复制代码
import React, { Suspense } from 'react'

const Lazys = (props) => {
  return (
      <Suspense fallback={<div>loading...</div>}>{props.component}</Suspense>
  );
}

export default Lazys;

进行路由懒加载

js 复制代码
// 引入封装的组件
import Lazys from './Lazys';
// 懒加载处理后的路由表
const Index = () => {
  return (
      <App>
          <Redirect from="/" to='/home' exact></Redirect>
          <Route path='/home'>
              <Lazys component={
                  <Home>
                      <Redirect from='/home' to='/home/movies' exact></Redirect>
                      <Route path='/home/movies'>
                          <Lazys component={
                              <Movies>
                                  <Redirect from='/home/movies' to='/home/movies/hot' exact></Redirect>
                                  <Route path='/home/movies/hot'>
                                      <Lazys component={<Hot />} />
                                  </Route>
                                  <Route path='/home/movies/wait'>
                                      <Lazys component={<Wait />} />
                                  </Route>
                                  <Route path='/home/movies/cinema'>
                                      <Lazys component={<Cinema />} />
                                  </Route>
                                  <Route path='/home/movies/classic'>
                                      <Lazys component={<Classic />} />
                                  </Route>
                              </Movies>
                          } />
                      </Route>
                      <Route path='/home/videos'>
                          <Lazys component={<Videos />} />
                      </Route>
                      <Route path='/home/mini'>
                          <Lazys component={<Mini />} />
                      </Route>
                      <Route path='/home/show'>
                          <Lazys component={<Show />} />
                      </Route>
                      <Route path='/home/mine'>
                          <Lazys component={<Mine />} />
                      </Route>
                  </Home>
              } />
          </Route>
          <Route path='/detail/:id'>
              <Lazys component={<Detail />} />
          </Route>
          <Route path='/login'>
              <Lazys component={<Login />} />
          </Route>
          <Route path='/city'>
              <Lazys component={<City />} />
          </Route>
          <Route path='*'>
              <Lazys component={<NotFound />} />
          </Route>
      </App >
  );
}

结束语

希望对您有一点点帮助,如有错误欢迎小伙伴指正。

👍点赞:您的赞赏是我前进的动力!

⭐收藏:您的支持我是创作的源泉!

✍评论:您的建议是我改进的良药!

一起加油!!!💪💪💪

相关推荐
腾讯TNTWeb前端团队2 小时前
helux v5 发布了,像pinia一样优雅地管理你的react状态吧
前端·javascript·react.js
范文杰5 小时前
AI 时代如何更高效开发前端组件?21st.dev 给了一种答案
前端·ai编程
拉不动的猪5 小时前
刷刷题50(常见的js数据通信与渲染问题)
前端·javascript·面试
拉不动的猪6 小时前
JS多线程Webworks中的几种实战场景演示
前端·javascript·面试
FreeCultureBoy6 小时前
macOS 命令行 原生挂载 webdav 方法
前端
uhakadotcom7 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom7 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom7 小时前
React与Next.js:基础知识及应用场景
前端·面试·github
uhakadotcom7 小时前
Remix 框架:性能与易用性的完美结合
前端·javascript·面试
uhakadotcom7 小时前
Node.js 包管理器:npm vs pnpm
前端·javascript·面试