React-路由进阶

一、路由的使用

1.声明式导航

  • src/index.js文件中定义一个路由模式(可选,也可以在具体的某个组件中使用Router)
javascript 复制代码
import React from "react";
import ReactDOM from "react-dom";

// 设置路由模式
import {HashRouter as Router} from 'react-router-dom'

// 定义 provider
import { Provider } from "react-redux";
import store from "./Store/index";

import App from "./App";

ReactDOM.render(
    <Provider store={store}>
        {/* 使用Router包裹根组件 */}
        <Router>
            <App></App>
        </Router>
    </Provider>,
    document.getElementById("root")
);
  • 在根组件src/App.js中引入路由相关组件(根据自身需要选择路由模式),并使用

    • 后续除了特殊的路由规则(嵌套路由)以外,其它普通的路由规则都需要在根组件中运用
javascript 复制代码
import React, { Component } from "react";
import { HashRouter as Router, Route, Link } from "react-router-dom";

import Cmp10 from "./Components/Cmp10";
import Cmp11 from "./Components/Cmp11";

class App extends Component {
    render() {
        return (
            <Router>
                <div>
                    <h1>导航区域</h1>
                    <div>
                        <ul>
                            <li>
                                <Link to="/home">首页</Link>
                            </li>
                            <li>
                                <Link to="/news">新闻</Link>
                            </li>
                        </ul>
                    </div>
                </div>
                <Route path="/home" component={Cmp10}></Route>
                <Route path="/news" component={Cmp11}></Route>
            </Router>
        );
    }
}

export default App;

在写上述代码时注意,路由自带组件的顺序嵌套关系,组件<Link></Link>和组件<Route></Route>必须被组件<Router></Router>给包裹着。

需要注意:

刨除样式的影响,Route组件在HTML代码中的位置决定了渲染后其在页面中显示的位置。如果Route放在最后,则其显示的时候也在最后;若其放在渲染内容的最前面,相应的显示也会在最开始。

2.编程式导航

react-router-dom中通过history对象中的push/go等方法实现编程式导航功能,这一点与之前的vue路由还是很相似的。

javascript 复制代码
this.props.history.push({
  pathname: "/home",
  search: "from=404",	// 表示传递查询字符串
  state: {				// 隐式传参,地址栏不体现
    username: "admin",
  },
});

// 给定给定的数字(正数或负数)决定去往历史栈中的哪个地址,正数往未来,负数往过去
this.props.history.go(-1)
this.props.history.goBack(-1)

请勿在根组件中写编程式导航,因为根组件默认是没有props对象,解决办法见后续。

案例:
javascript 复制代码
import React, { Component } from "react";
import { parseSearch } from "../utils/function";
export default class index extends Component {
  // 编程式导航
  render() {
    console.log(this.props);
    const { push } = this.props.history;
    // 接收query参数
    const { search } = this.props.location;
    // 获取路径中的参数转为对象格式 1. 使用new URL 拼接路径 + 需要转换的参数
    const urlObj = new URL("http://localhost" + search);
    // 2.使用Object.fromEntries 将urlObj.searchParams 转换 为一个新的对象
    console.log(Object.fromEntries(urlObj.searchParams));
    // 精简写法
    console.log(
      Object.fromEntries(new URL("http://localhost" + search).searchParams)
    );
    // 封装写法
    console.log(parseSearch(search));
    return (
      <div>
        {/* <button onClick={() => push("/news")}>去新闻页</button> */}
        <button
          onClick={() =>
            push({
              pathname: "/news",
              search: "id=100",
              state: { login: true },
            })
          }
        >
          去新闻页
        </button>
      </div>
    );
  }
}

二、路由参数

路由参数:在Route定义渲染组件时给定动态绑定的参数。

React路由传参方式有三种:

  • ==动态路由参数(param)==

    • 以"/film/detail/:id"形式传递的数据

    • 在目标页面路由中传递

    • 在落地组件中通过this.props.match.params得到

    • 一般用于restful规范下的开发

  • 查询字符串(query)

    • 通过地址栏中的 ?key=value&key=value传递

    • 在落地组件中通过this.props.location.search得到

    • 由于得到的数据是带"?"的,还需要进一步加工处理之后才能使用,因此建议少用或者不用

  • 式传参(state),通过地址栏是观察不到的

    • 不合适写在声明式导航中,写在编程式导航中更加合适

    • 一般数用于埋点数据

      • 简单的讲,埋点是将部分标记隐藏起来,等待用户去触发,因为这个事情不想让用户看到(需要做一些数据的收集,后续做分析),因此会使用隐式传参的方式(大数据分析)
    • 在落地组件中通过this.props.location.state得到

接收示例:

javascript 复制代码
constructor(props){
    super(props)
    this.state = {
        // 接收动态路由参数
        news_id: this.props.match.params.id,
        // 接收查询字符串并处理
        query: querystring.parse(this.props.location.search.slice(1)),
        // 接收state
        state: this.props.location.state
    };
}
案例:

首页:

javascript 复制代码
import React, { Component } from "react";
import { NavLink } from "react-router-dom";
import { Route } from "react-router-dom/cjs/react-router-dom.min";

import index from "./pages/index";
import news from "./pages/news";
// 样式
import './assets/css/App.css'
export default class App extends Component {
  render() {
    return (
      <div>
        <ul>
          {/* 声明式导航  标签式导航 */}
          <li>
            {/* 字符串传参 */}
            <NavLink to="/index?name=index">首页</NavLink>
          </li>
          <li>
            {/* 路径传参 */}
            <NavLink to="/news/100">新闻页</NavLink>
          </li>
        </ul>
        <hr />
        <Route path="/index" component={index}></Route>
        {/* 在匹配params 动态参数时 需要设置传递名称  ?表示可传可不传*/}
        <Route path="/news/:id?" component={news}></Route>
      </div>
    );
  }
}

index页面

javascript 复制代码
import React, { Component } from "react";
import { parseSearch } from "../utils/function";
export default class index extends Component {
  // 编程式导航
  render() {
    console.log(this.props);
    const { push } = this.props.history;
    // 接收query参数
    const { search } = this.props.location;
    // 获取路径中的参数转为对象格式 1. 使用new URL 拼接路径 + 需要转换的参数
    const urlObj = new URL("http://localhost" + search);
    // 2.使用Object.fromEntries 将urlObj.searchParams 转换 为一个新的对象
    console.log(Object.fromEntries(urlObj.searchParams));
    // 精简写法
    console.log(
      Object.fromEntries(new URL("http://localhost" + search).searchParams)
    );
    // 封装写法
    console.log(parseSearch(search));
    return (
      <div>
        {/* <button onClick={() => push("/news")}>去新闻页</button> */}
        <button
          onClick={() =>
            push({
              pathname: "/news",
              search: "id=100",
              state: { login: true },
            })
          }
        >
          去新闻页
        </button>
      </div>
    );
  }
}

news页面

javascript 复制代码
import React, { Component } from "react";
class news extends Component {
  render() {
    // 获取params 参数
    console.log(this.props.match.params);
    // 获取state 参数
    console.log(this.props.location.state);
    return <div>news</div>;
  }
}

export default news

三、重定向与404路由

1.重定向路由

React的重定向路由有以下写法:

在重定向的时候需要知道,从哪里来,到哪里去,因此该组件需要使用2个属性:

  • from:匹配需要重定向的路由

  • to:需要去往的路由

javascript 复制代码
import { Redirect } from "react-router-dom"

<Redirect from="/from" to="/to"></Redirect>

注意:

  • 建议使用Route书写路由规则时,使用Switch组件包裹Route组件,以便最多只能匹配一个规则

  • 建议给Redirect组件加上exact属性,来设置严格匹配模式

案例:

javascript 复制代码
import React, { Component } from "react";
import { NavLink, Redirect,Switch } from "react-router-dom";
import { Route } from "react-router-dom/cjs/react-router-dom.min";

import index from "./pages/index";
import news from "./pages/news";
// 样式
import "./assets/css/App.css";
export default class App extends Component {
  render() {
    return (
      <div>
        <ul>
          {/* 声明式导航  标签式导航 */}
          <li>
            {/* 字符串传参 */}
            <NavLink to="/index?name=index">首页</NavLink>
          </li>
          <li>
            {/* 路径传参 */}
            <NavLink to="/news/100">新闻页</NavLink>
          </li>
        </ul>
        <hr />
        <Switch>
          {/* 重定向 exact 严格匹配  配合switch 匹配一箱 */}
          <Redirect from="/" to="/index" exact></Redirect>
          <Route path="/index" component={index}></Route>
          {/* 在匹配params 动态参数时 需要设置传递名称  ?表示可传可不传*/}
          <Route path="/news/:id?" component={news}></Route>
        </Switch>
      </div>
    );
  }
}

2.404路由

项目中少不了404页面的配置,在React里面配置404页面需要注意:

  • 需要用到Switch组件,让其去包裹路由的Route组件(Switch组件保证只渲染其中一个子路由)
javascript 复制代码
import NotFound from "./Components/404";

<Route>
    <NotFound></NotFound>
</Route>
// 或
 <Route component={NotFound}></Route>

注意:在404路由的位置,不需要给定具体的路由匹配规则,不给path表示匹配*,即所有的路由都会匹配,因此用404路由一定要加Switch匹配一个路由。

注意:并不会因为当前是404路由/重定向路由而改变状态码,因为当前写的是前端的内容,状态码是后端提供的,只有等后期上线以后才能有状态码。

案例:

javascript 复制代码
import React, { Component } from "react";
import { NavLink,Switch } from "react-router-dom";
import { Route } from "react-router-dom/cjs/react-router-dom.min";

import index from "./pages/index";
import news from "./pages/news";
import NotFound from "./components/NotFound";
// 样式
import "./assets/css/App.css";
export default class App extends Component {
  render() {
    return (
      <div>
        <ul>
          {/* 声明式导航  标签式导航 */}
          <li>
            {/* 字符串传参 */}
            <NavLink to="/index?name=index">首页</NavLink>
          </li>
          <li>
            {/* 路径传参 */}
            <NavLink to="/news/100">新闻页</NavLink>
          </li>
        </ul>
        <hr />
        <Switch>
          <Route path="/index" component={index}></Route>
          {/* 在匹配params 动态参数时 需要设置传递名称  ?表示可传可不传*/}
          <Route path="/news/:id?" component={news}></Route>
          {/* 404路由必须加switch  在版本留替换为  Routes*/}
          <Route path="*" component={NotFound}></Route>
        </Switch>
      </div>
    );
  }
}

404页面

javascript 复制代码
import React, { Component } from "react";

export default class NotFound extends Component {
  componentDidMount() {
    const {goBack} = this.props.history
    // 执行副作用
    setTimeout(() => {
        goBack()
    },3000)
  }
  render() {
    return <div>404 NotFound</div>;
  }
}

四、路由渲染方式和withRouter

v6中采用了新的属性对组件进行渲染,属性名:element

Route路由渲染组件是用于路由规则匹配成功后组件渲染容器,此组件提供了3种方式组件渲染方式:

  • component属性(组件对象/函数)

    javascript 复制代码
    <Route path="/home" component={Home} />
    javascript 复制代码
    <Route path="/home" component={(props) => <Home />} />
  • render属性(函数)

    javascript 复制代码
    <Route path="/home" render={(props) => <Home />} />
  • children属性(组件/函数

    javascript 复制代码
    <Route path="/about" children={(props) => {
        if(props.match){
            return <div>children渲染</div>
        }
    }} />
    javascript 复制代码
    <Route path="/about" children={<About />} />

注意点

  • 当children的值是一个函数时,无论当前地址和path路径匹不匹配,都将会执行children对应的函数,当children的值为一个组件时,当前地址和path不匹配时,路由组件不渲染

  • children函数方式渲染,会在形参中接受到一个对象,对象中match属性如果当前地址匹配成功返回对象,否则null

withRouter

作用:把不是通过路由切换过来的组件中,将react-router 的 history、location、match 三个对象传入props对象上

默认情况下,必须是经过路由匹配渲染的组件才存在this.props,才拥有路由参数,才能使用编程式导航的写法,才能执行this.props.history.push('/uri')跳转到对应路由的页面。然而不是所有组件都直接与路由相连的,当这些组件需要路由参数时,使用withRouter就可以给此组件传入路由参数,此时就可以使用this.props。

javascript 复制代码
// 引入withRouter
import { withRouter} from 'react-router-dom'
​
// 执行一下withRouter
export default withRouter(Cmp)

该高阶组件是路由包自带的东西,因此只需要引入+使用就可以了,不需要自己定义。

V5与V6的变化

  • Switch被废弃,由Routes替代,并且Routes是必须的

  • 渲染组件的方式被element属性替代,例如:

    javascript 复制代码
    <Route path="/dashboard" element={<Dashboard />} />
  • 重定向组件Redirect被废弃,使用以下语法替代:

    javascript 复制代码
    <Route path="/" element={<Navigate to="/dashboard/welcome" />} />
  • 嵌套路由被简化

    • 父路由:

      javascript 复制代码
      <Route path="/dashboard/*" element={<Dashboard />} />
    • 子路由(子路由规则中不再需要写父前缀):

      javascript 复制代码
      <Route path="welcome"  element={<Welcome/>}/>
  • 废弃了withRouter

案例:

主页面

javascript 复制代码
import React, { Component } from "react";
import { NavLink,Switch } from "react-router-dom";
import { Route } from "react-router-dom/cjs/react-router-dom.min";

import index from "./pages/index";
import News from "./pages/news";
import NotFound from "./components/NotFound";
// 样式
import "./assets/css/App.css";
export default class App extends Component {
  render() {
    return (
      <div>
        <ul>
          {/* 声明式导航  标签式导航 */}
          <li>
            {/* 字符串传参 */}
            <NavLink to="/index?name=index">首页</NavLink>
          </li>
          <li>
            {/* 路径传参 */}
            <NavLink to="/news/100">新闻页</NavLink>
          </li>
        </ul>
        <hr />
        <Switch>
          <Route path="/index" component={index}></Route>
          {/* 在匹配params 动态参数时 需要设置传递名称  ?表示可传可不传*/}
          {/* <Route path="/news/:id?" component={News}></Route> */}

          {/* 函数渲染方式  (不常用) 如果使用该方法传参 搭配 withRouter 包装使用*/}
          {/* <Route path="/news/:id?" component={(props)=><News/>}></Route> */}
          
          {/* render 函数渲染方式 (不常用) 如果使用该方法传参 搭配 withRouter 包装使用*/}
          {/* <Route path="/news/:id?" render={()=><News></News>}></Route> */}

          {/* children 函数渲染方式 (不常用) 如果使用该方法传参 搭配 withRouter 包装使用*/}
          <Route path="/news/:id?" children={<News/>}></Route>
          {/* children 组件渲染方式 (不常用) */}
          <Route path="/news/:id?" children={News}></Route>


          {/* 404路由必须加switch  在版本留替换为  Routes*/}
          <Route path="*" component={NotFound}></Route>
        </Switch>
      </div>
    );
  }
}

子页面

javascript 复制代码
import React, { Component } from "react";

// import { withRouter } from "react-router-dom";

// class news extends Component {
//   render() {
//     // 获取params 参数
//     console.log(this.props.match.params);
//     // 获取state 参数
//     console.log(this.props.location.state);
//     return <div>news</div>;
//   }
// }

// export default withRouter(news)

class news extends Component {
  render() {
    // 获取params 参数
    console.log(this.props.match.params);
    // 获取state 参数
    console.log(this.props.location.state);
    return <div>news</div>;
  }
}

export default news
相关推荐
吃杠碰小鸡24 分钟前
commitlint校验git提交信息
前端
虾球xz1 小时前
游戏引擎学习第20天
前端·学习·游戏引擎
我爱李星璇1 小时前
HTML常用表格与标签
前端·html
疯狂的沙粒1 小时前
如何在Vue项目中应用TypeScript?应该注意那些点?
前端·vue.js·typescript
小镇程序员1 小时前
vue2 src_Todolist全局总线事件版本
前端·javascript·vue.js
野槐1 小时前
前端图像处理(一)
前端
程序猿阿伟1 小时前
《智能指针频繁创建销毁:程序性能的“隐形杀手”》
java·开发语言·前端
疯狂的沙粒2 小时前
对 TypeScript 中函数如何更好的理解及使用?与 JavaScript 函数有哪些区别?
前端·javascript·typescript
瑞雨溪2 小时前
AJAX的基本使用
前端·javascript·ajax
力透键背2 小时前
display: none和visibility: hidden的区别
开发语言·前端·javascript