react路由的学习

2021/7/1 6:23:40

本文主要是介绍react路由的学习,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

react-router

单页面富应用(SPA):整个web应用实际上只有一个页面,当URL发生改变时,并不会从服务器去请求新的静态资源,而是通过js监听URL的改变,并且根据URL的 不同去渲染新的页面;而前端路由便维护着URL和具体页面组件的映射关系,最终我们在页面上看到的实际就是渲染的一个个组件页面。

前端路由原理:监听URL发生改变,同时不引起页面的刷新有两个方法:

​ (1)通过URL的hash改变URL。

​ (2)通过HTML5的history模式修改URL。

hash模式的原理

本质上是监听window的hashchange事件,然后根据具体的hash值来判断所要显示的内容;注意:hash模式的优势就是兼容性更好,在老版本的IE都可以运行。但是缺陷就是路径后有一个#,显得不像一个真实的路径。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=, initial-scale=1.0" />
    <title>秃头的科比</title>
  </head>
  <body>
    <div>
      <a href="#/home">首页</a>
      <a href="#/about">关于</a>

      <div class="router-view"></div>
    </div>
    <script>
      var routerViewEl = document.getElementsByClassName("router-view")[0];

      window.addEventListener("hashchange", function () {
        //根据具体的hash值来判断显示的内容
        switch (location.hash) {
          case "#/home":
            routerViewEl.innerHTML = "首页";
            break;
          case "#/about":
            routerViewEl.innerHTML = "关于";
            break;
          default:
            routerViewEl.innerHTML = "";
        }
      });
    </script>
  </body>
</html>

history模式

history模式是HTML5新增的,它有6种模式修改URL而不刷新页面:

​ (1)replaceState:替换原来的路径;

​ (2)pushState:使用新的路径;

​ (3)popState:路径的回退;

​ (4)go:向前或向后改变路径;

​ (5)back:向后改变路径;

​ (6)forward:向前改变路径。

history模式实现原理:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>history原理</title>
  </head>
  <body>
    <div id="app">
      <a href="/home">首页</a>
      <a href="/about">关于</a>

      <div class="router-view"></div>
    </div>

    <script>
      // 1.获取router-view的DOM
      const routerViewEl = document.getElementsByClassName("router-view")[0];

      // 获取所有的a元素, 自己来监听a元素的改变
      const aEls = document.getElementsByTagName("a");
      for (let el of aEls) {
        el.addEventListener("click", (e) => {
          e.preventDefault();
          const href = el.getAttribute("href");
          history.pushState({}, "", href);
          urlChange();
        });
      }

      // 执行回退操作时, 依然来到urlChange
      window.addEventListener("popstate", urlChange);

      // 监听URL的改变
      function urlChange() {
        switch (location.pathname) {
          case "/home":
            routerViewEl.innerHTML = "首页";
            break;
          case "/about":
            routerViewEl.innerHTML = "关于";
            break;
          default:
            routerViewEl.innerHTML = "";
        }
      }
    </script>
  </body>
</html>

react-router从版本4开始,路由不再集中在一个包中进行管理了:

(1)react-router是router的核心部分代码;

(2)react-router-dom是用于浏览器的;

(3)react-router-native是用于原生应用的;

目前我们使用最新的react-router版本是5版本:实际上4和5版本的区别并不大。

react-router主要的一些API

(1)BrowserRouter和HashRouter:

​ BrowserRouter使用history模式。

​ HashRouter使用hash模式。

(2)Link和Navlink:

​ 通常路径的跳转使用Link组件,其最终会被渲染成a元素。

​ NavLink是在Link的基础上可以添加一些样式属性。

​ to属性用于设置将要跳转到的路径。

(3)Route:用于路径的匹配;

​ path属性:用于设置匹配到的路径;

​ component属性:设置匹配到的页面组件;

​ exat:精准匹配,只要匹配到完全一致的路径,才会渲染对应的页面组件。

NavLink的使用

需求:路径选中时,对应的a元素变为红色。

这个时候,我们要使用NavLink组件来替代Link组件:

(1)activeStyle:活跃时匹配的样式;

<NavLink exact to="/" activeStyle={{color: "red", fontSize: "30px"}}>首页</NavLink>
<NavLink to="/about" activeStyle={{color: "red", fontSize: "30px"}}>关于</NavLink>
<NavLink to="/profile" activeStyle={{color: "red", fontSize: "30px"}}>我的</NavLink>

(2)activeClassName:活跃时添加的class,事实上在默认匹配成功时,NavLink就会自动给a标签添加上.active属性,但是为了避免样式重叠,推荐自定义class。

<NavLink exact to="/" activeClassName="link-active">首页</NavLink>
<NavLink to="/about" activeClassName="link-active">关于</NavLink>
<NavLink to="/profile" activeClassName="link-active">我的</NavLink>

//样式
a.active, a.link-active {
  color: red;
  font-size: 30px;
}

switch的作用

当我们匹配到某一个路径时,我们会发现有一些问题:

比如/about路径匹配到的同时,/:userid也被匹配到了,并且最后的一个NoMatch组件也总是被匹配到;

原因是什么呢?默认情况下,react-router中只要是路径被匹配到的Route对应的组件都会被渲染;

但是实际开发中,我们往往希望有一种排他的思想,只要匹配到了第一个,那么后面的就不应该继续匹配了,

这个时候我们可以使用Switch来将所有的Route进行包裹即可;

<Switch>
    <Route exact path="/" component={Home} />
    <Route path="/about" component={About} />
    <Route path="/profile" component={Profile} />
    <Route path="/:id" component={User} />
    <Route path="/user" component={User} />
    <Route path="/login" component={Login} />
    <Route path="/product" component={Product} />
    <Route path="/detail/:id" component={Detail} />
    <Route path="/detail2" component={Detail2} />
    <Route path="/detail3" component={Detail3} />
    <Route component={NoMatch} />
</Switch>

redirect的使用

Redirect用于路由的重定向,当这个组件出现时,就会执行跳转到对应的to路径中:

案例: 用户跳转到User界面;但是在User界面有一个isLogin用于记录用户是否登录:

true:那么显示用户的名称;

false:直接重定向到登录界面;

render() {
    return this.state.isLogin ? (
      <div>
        <h2>User</h2>
        <h2>用户名: coderwhy</h2>
      </div>
    ): <Redirect to="/login"/>
 }

手动实现路由的跳转

目前我们实现的跳转主要是通过Link或者NavLink进行跳转的,实际上我们也可以通过JavaScript代码进行跳转。

但是通过JavaScript代码进行跳转有一个前提:必须获取到history对象。

如何可以获取到history的对象呢?

方式一:如果该组件是通过路由直接跳转过来的,那么可以直接获取history、location、match对象;

方式二:如果该组件是一个普通渲染的组件,那么不可以直接获取history、location、match对象;

那么如果普通的组件也希望获取对应的对象属性应该怎么做呢? 前面我们学习过高阶组件,可以在组件中添加想要的属性; react-router也是通过高阶组件为我们的组件添加相关的属性的;

如果我们希望在App组件中获取到history对象,必须满足一下两个条件:

(1)App组件必须包裹在Router组件之内;

(2)App组件使用withRouter高阶组件包裹;

import React, { PureComponent } from 'react';
import Product from './pages/product';

class App extends PureComponent {
  constructor(props) {
    super(props);
  }

  render() {
    return (
      <div>
        <button onClick={e => this.jumpToProduct()}>商品</button>
      </div>
    )
  }
  
  jumpToProduct() {
    this.props.history.push("/product");
  }
}

export default withRouter(App);

路由的参数传递

传递参数有三种方式:

(1)动态路由的方式;

动态路由的概念指的是路由中的路径并不会固定:比如/detail的path对应一个组件Detail,而如果我们将path在Route匹配时写成/detail/:id,那么 /detail/abc、/detail/123都可以匹配到该Route,并且进行显示; 这个匹配规则,我们就称之为动态路由,通常情况下,我们使用动态路由传递一些简单的参数。

<NavLink to={`/detail/${id}`} activeClassName="link-active">详情</NavLink>

<Switch>
	<Route path="/detail/:id" component={Detail} />
</Switch>

//那么如何在组件中拿到这个id呢
render() {
    const match = this.props.match;
    console.log(match.params);

    return (
      <div>
        <h2>Detail: {match.params.id}</h2>
      </div>
    )
}

(2)search传递参数;(这种方式不推荐使用)

<NavLink to={`/detail2?name=why&age=18`} activeClassName="link-active">详情2</NavLink>
 
 <Switch>
     <Route path="/detail2" component={Detail2} />
 </Switch>

(3)Link中to传入对象;

<NavLink to={{
        pathname: "/detail3",
        search: "name=abc",
        state: info
 }} 
        activeClassName="link-active">
          详情</NavLink>

<Switch>
	<Route path="/detail3" component={Detail3} />
</Switch>

react-router-config

目前我们所有的路由定义都是直接使用Route组件,并且添加属性来完成的。

但是这样的方式会让路由变得非常混乱,我们希望将所有的路由配置放到一个地方进行集中管理:这个时候可以使用react-router-config来完成; 安装react-router-config,配置路由映射的关系,使用renderRoutes函数完成配置。

{/* <Switch>
    <Route path="/detail3" component={Detail3} />
    <Route component={NoMatch} />
</Switch> */}

//一级路由 
{renderRoutes(routes)}

//子路由
{renderRoutes(this.props.route.routes)}


这篇关于react路由的学习的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程