react-路由

前端路由

前端根据不同的地址,对应渲染不同的组件或页面。

前端路由一般分为两种:

  • hash:以#代表hash值,这个给浏览器识别,根据这个hash值来切换显示不同的内容,这个hash值不会发送给后端,能够兼容低版本的浏览器。

    http://localhost:8000/#/login
    
  • history:相比于hash路由,history路由更好看,它会发送给服务端,如果前端的路由和后端服务器的路由一样的话,那么会访问服务器路由,上线后需要后端配合才能使用。

    http://localhost:8000/login
    

react路由的安装

react本身没有提供路由,我们需要安装第三方插件react-router-dom

react路由v6版本地址:https://reactrouter.com/en/6.8.1/start/overview

v5版本地址:https://v5.reactrouter.com/

v6版本更偏向于函数式组件开发

v5版本兼容类组件和函数组件开发。

1)安装路由

yarn add react-router-dom@5.3.4
# or
npm i react-router-dom@5.3.4

2)配置一级路由

App.jsx

import React from "react";
import {
  BrowserRouter as Router,
  Switch,
  Route,
  Link
} from "react-router-dom";
import Login from "./pages/Login";
import Home from "./pages/Home";

export default function App() {
  return (
    <Router>
      <div>
        <nav>
          <ul>
            <li>
              <Link to="/">首页</Link>
            </li>
            <li>
              <Link to="/login">登录页面</Link>
            </li>
          </ul>
        </nav>

        {/* A <Switch> looks through its children <Route>s and
            renders the first one that matches the current URL. */}
        <Switch>
          <Route path="/login">
            <Login />
          </Route>
          <Route path="/">
            <Home />
          </Route>
        </Switch>
      </div>
    </Router>
  );
}

Home.jsx

import React, { Component } from 'react'

export default class Home extends Component {
  render() {
    return (
      <div>Home</div>
    )
  }
}

Login.jsx

import React, { Component } from 'react'

export default class Login extends Component {
  render() {
    return (
      <div>Login</div>
    )
  }
}

react路由匹配信息

路由器

BrowserRouter:代表history路由模式,

HashRouter:代表hash路由模式

路由器应该放在最外层,其他的路由组件都应该包裹在内。否则会报错如下:

image-20230222171541782

路径匹配器

Switch

类似于js中switch条件判断,它匹配到路径后只渲染匹配到的哪个Route组件,如果没有匹配到,就不会渲染页面

路由匹配是模糊匹配,如果路径是/,那么下面的路由路径都匹配不到,导致其他页面渲染不出来。

解决方案:

1)将/路径放到最下面

<Switch>
    <Route path="/login">
      <Login />
    </Route>
    <Route path="/register">
      <Register />
    </Route>
    <Route path="/">
      <Home />
    </Route>
</Switch>

2)使用exact精确匹配

<Switch>
    <Route path="/" exact>
      <Home />
    </Route>
    <Route path="/login">
      <Login />
    </Route>
    <Route path="/register">
      <Register />
    </Route>
</Switch>
Route

将路由路径与组件进行映射,当路径匹配成功后,则渲染对应的页面组件,否则就不会渲染。

  • path
    设置路由路径

  • exact:代表精确匹配,只能路由地址恰好与path的地址匹配上才算匹配成功。

    <Switch>
        <Route path="/" exact>
          <Home />
        </Route>
    </Switch>
    
  • component:配置要渲染的页面组件(推荐使用)

    <Switch>
    	<Route path="/login" component={Login}></Route>
    </Switch>
    
  • render:是一个函数,也可以用于配置要渲染的页面组件

    <Switch>
        <Route path="/register" render={() => <Register />}></Route>
    </Switch>
    
  • 嵌套的方式配置页面

    <Switch>
        <Route path="/" exact>
          <Home />
        </Route>
    </Switch>
    

没什么特殊情况,建议使用component属性去配置页面组件。

导航

Link组件

用于跳转页面的组件,类似于vue中的router-link

  • to:配置跳转页面的路径
    可以传递字符串,也可以是一个对象
    字符串方式:

    <Link to="/login">登录页面</Link>
    

    对象方式:

    <Link to={{pathname: '/register'}}>注册页面</Link>
    
  • replace:路由路径替换,当跳转页面后,那么回退页面时不会回退到刚刚的页面,而是回退到了上上个页面。

    <Link to={{pathname: '/register'}} replace>注册页面</Link>
    
Redirect组件

路由重定向。

  • from:匹配的路径来源,从哪里来
  • to:重定向到哪里去
  • exact:精确匹配
<Switch>
    <Redirect from="/register" to={'/login'} exact />
    <Route path="/" exact>
      <Home />
    </Route>
    <Route path="/login" component={Login}></Route>
    <Route path="/register" render={() => <Register />}></Route>
</Switch>

注意:Redirect建议写在最前面,如果写在后面可能匹配不到,无法生效。

二级路由

vue中嵌套路由配置:

{
    path: '/home',
    compoennt: Home,
    children: [
        {
            path: 'user',
            component: User
        }
    ]
}

二级路由配置

  1. 嵌套路由配置不需要嵌套加BrowserRouter和HashRouter。
  2. 嵌套路由配置需要加入父级路由,后面才是子路由路径父级路由/子路由
import React, { Component } from 'react'
import { Link, Route, Switch, Redirect } from 'react-router-dom'
import Students from './Students'
import Teacher from './Teacher'
import User from './User'

export default class Home extends Component {
  render() {
    return (
      <div>
        <ul>
          <li>
            <Link to={'/home/teacher'}>教师管理</Link>
          </li>
          <li>
            <Link to={'/home/students'}>学生管理</Link>
          </li>
          <li>
            <Link to={'/home/user'}>用户管理</Link>
          </li>
        </ul>
        {/* 这里渲染对应页面 */}

        <Switch>
          <Redirect from='/home' to={'/home/teacher'} exact />
          <Route path={'/home/teacher'} component={Teacher}></Route>
          <Route path={'/home/students'} component={Students}></Route>
          <Route path={'/home/user'} component={User}></Route>
        </Switch>
      </div>
    )
  }
}

路由懒加载

前端目前的项目基本都是工程化项目单页面应用。

单页面应用一开始加载时就会将所有的文件加载出来,如果项目很大,会导致在首屏加载时时间过长,白屏时间长,用户体验不太好。

vue中解决:

{
    path: 'xxx',
    compoennt: () => import('./xxx.vue')
}

在react中,有两种方式去完成路由懒加载:

  1. 使用第三方插件
  2. 使用react官方的方式

使用了路由懒加载后,会在使用这个页面时才会去加载对应的页面,而不是一开始就全部加载页面。

首屏渲染速度得到了提升。

使用react-loadable插件

1)安装插件

npm i react-loadable
# or
yarn add react-loadable

2)引入插件

import Loadable from "react-loadable";

3)使用插件加载页面

const Login = Loadable({
  loader: () => import('./pages/Login'),
  loading: () => <div>正在加载中。。。。。。</div>
});

const Home = Loadable({
  loader: () => import('./pages/Home'),
  loading: () => <div>正在加载中。。。。。。</div>
});

const Register = Loadable({
  loader: () => import('./pages/Register'),
  loading: () => <div>正在加载中。。。。。。</div>
});

loader:加载的页面

loading:加载页面组件完成之前要渲染的加载组件。

使用react官方推荐的方式

  1. 使用React.Suspense包裹路由

    <React.Suspense fallback={<div>正在加载中。。。。。</div>}>
        <Switch>
          {/* <Redirect from="/register" to={'/login'} exact /> */}
    
          <Route path="/home" component={React.lazy(() => import('./pages/Home'))}></Route>
          <Route path="/login" component={React.lazy(() => import('./pages/Login'))}></Route>
          <Route path="/register" component={React.lazy(() => import('./pages/Register'))}></Route>
    
        </Switch>
      </React.Suspense>
    

    fallback:加载页面完成之前要渲染的一个loading组件

  2. 搭配React.lazy完成路由懒加载

     <Route path="/home" component={React.lazy(() => import('./pages/Home'))}></Route>
    

    路由参数

路由传递参数,在页面之间传递参数。

在react路由中,有四种参数传递方式:

prams动态路由

动态路由需要配置路由参数:

  1. 配置路由参数

    <Route path="/register/:id" render={() => <Register />}></Route>
    
  2. 跳转路由
    Link方式:

    <Link to={'/register/10'}>到注册页面</Link>
    

    js方式:

    this.props.history.push('/register/100');
    
  3. 参数获取
    动态路由参数是在this.props.match.params

    this.props.match.params
    

query的方式传参(不推荐)

通过对象的方式传递参数

  1. 跳转传递参数

    <Link to={{pathname: '/register', query: {id: '10'}}}>query跳转到注册页面</Link>
    

    js跳转

    this.props.history.push({
      pathname: '/register',
      query: {id: '100'}
    });
    
  2. 获取参数

    this.props.location.query
    

注意:有坑

当刷新页面后,参数丢失。

state的方式传递参数(不推荐)

通过对象的方式传递参数

  1. 跳转传递参数

    <Link to={{pathname: '/register', state: {id: '1000'}}}>state跳转到注册页面</Link>
    

    js方式:

    this.props.history.push({
      pathname: '/register',
      state: {id: '100'}
    });
    
  2. 获取参数

    this.props.location.state
    

注意:有坑

在history路由模式下刷新页面参数state还在,但是hash路由模式下刷新页面state参数丢失了。

search的方式传参

在地址栏中通过?传递参数

  1. 跳转传递参数
    字符串传递参数:

    <Link to={'/register?id=1'}>search跳转到注册页面</Link>
    
    this.props.history.push('/register?id=1');
    

    对象传递参数:

    <Link to={{pathname: '/register', search: '?id=10'}}>search跳转到注册页面</Link>
    
    this.props.history.push({
      pathname: '/register',
      search: '?id=100'
    });
    
  2. 获取参数

    this.props.location.search
    

    search参数获取到时一个字符串

search参数转换成对象

1)自己封装函数进行转换

export function parseSearch(str) {
    let arr = str.split("?")[1].split("&");   //先通过?分解得到?后面的所需字符串,再将其通过&分解开存放在数组里
    let obj = {};
    for (let i of arr) {
        console.log(i.split("="));
        obj[i.split("=")[0]] = i.split("=")[1];  //对数组每项用=分解开,=前为对象属性名,=后为属性值
    }
    return obj;
}

2)通过插件qs进行转换

  1. 安装插件

    npm i qs
    
    
  2. 引入

    import qs from 'qs'
    
    
  3. 使用
    将字符串参数转换成对象

    qs.parse(this.props.location.search, {
      ignoreQueryPrefix: true
    });
    
    

    ignoreQueryPrefix:去掉前缀?

    将对象转换成字符串参数

    qs.stringify(obj, {
      addQueryPrefix: true
    })
    
    

    addQueryPrefix:添加前缀?

路由跳转

通过Link组件跳转

 <Link to={'/home/teacher'}>教师管理</Link>

通过js跳转

window.location.href跳转
window.location.href = '/home/students'

这种可以在react项目中跳转路由页面,虽然可以完成页面的跳转,但是存在一些缺陷,会导致资源的重复加载,导致页面重新加载,用户体验不好。

props.push跳转路由

是路由插件提供了能力进行路由页面跳转,这种方式不会重复加载,页面也不会刷新,用户体验更好。

字符串参数:

this.props.history.push('/home/students');

对象参数:

this.props.history.push({
    pathname: '/home/students'
});
props.replace

替换路由路径,返回的是返回到了上上个页面

字符串参数:

this.props.history.replace('/home/students');

对象参数:

// 做了很多逻辑处理
this.props.history.replace({
    pathname: '/home/students'
});
props.goBack

回退到上一个页面路由

this.props.history.goBack();
props.goForward

前进一个页面

this.props.history.goForward();

withRouter高阶组件

非路由组件的跳转

在react组件中,通过Route组件配置的页面组件,被称为路由组件,但是直接引入使用的组件被称为非路由组件,非路路由组件内部没有路由相关信息,没法直接跳转路由。

针对非路由组件获取路由信息的解决方案:

1)将父组件的路由信息传入到非路由组件中(不推荐)

<Header {...this.props}></Header>

传入后,非路由组件内部就拥有了路由相关信息,然后就可以跳转路由了。

this.props.history.push('/login');

虽然解决了路由跳转问题,但是不推荐使用,因为这种方式嵌套了其他的非路由组件,会导致将路由信息层层传递下去,开发起来也非常麻烦。

2)withRouter高阶组件

通过高阶组件withRouter包裹非路由组件,将路由相关信息注入到组件的props中,非路由组件就拥有了路由相关信息。

  1. 引入

    import { withRouter } from 'react-router-dom';
    
  2. 使用

    export default withRouter(Header);
    

    运行withRouter函数包裹非路由组件,注入路由信息。

  3. 跳转

    this.props.history.push('/login');
    
  4. 全代码

    import React, { Component } from 'react'
    import { withRouter } from 'react-router-dom';
    class Header extends Component {
        gotoLogin = () => {
            console.log(this.props)
            this.props.history.push('/login');
        }
      render() {
        return (
          <div>
            这是header组件
            <button onClick={this.gotoLogin}>去登录</button>
          </div>
        )
      }
    }
    export default withRouter(Header);
    

高阶组件的案例

函数科里化:https://www.jianshu.com/p/2975c25e4d71

高阶组件本质就是一个函数,接收一个组件作为参数,经过处理返回一个组件。

简单解释withRouter

function withRouter(Comp) {
    return class WrapperdCompoennt extends React.Component {
        
        history = () => {
            
        }
        
        render() {
            return <Comp history={this.history} />
        }
    }
}

案例:

import React, { Component } from 'react'

function HOC(Comp) {
    return class WrapperedComp extends Component {

        state = {
            title: 'web前端'
        }

        render() {
            return <Comp title={this.state.title} />
        }
    }
}

class Test extends Component {
  render() {
    return (
      <div>
        test页面:
        <br />
        title: {this.props.title}
      </div>
    )
  }
}

export default HOC(Test);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值