IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> 8、React路由 -> 正文阅读

[JavaScript知识库]8、React路由

1、介绍

SPA概念:单页Web应用(single page web application),就是只有一张Web页面的应用,由一个主页面和多个页面片段组成,由JS控制页面片段的展示与否。整个页面不会刷新,只会做局部页面的更新操作,与服务端通过ajax进行异步交互。

React路由作用:React开发的是一个SPA应用,整个应用由一系列组件构成,所有的组件并非直接全部挂载,而是页面根据页面的操作,挂载相应的组件。组件的挂载与否的控制逻辑便是React路由完成。

2、实现原理

前端路由的实现,根本上是监控浏览地址的变化及历史记录,前端路由地址栏发生变更,不发起网路请求,但是会被监控到,根据不同的path,控制挂载不同的组件。

实现手段有两种,一种是基于history实现的BrowserRouter,一种是基于hash实现的HashRouter。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>前端路由</title>
</head>
<body>
	<a href="http://www.atguigu.com" onclick="return push('/test1') ">push test1</a><br><br>
	<button onClick="push('/test2')">push test2</button><br><br>
	<button onClick="replace('/test3')">replace test3</button><br><br>
	<button onClick="back()">&lt;= 回退</button>
	<button onClick="forword()">前进 =&gt;</button>

	<script type="text/javascript" src="https://cdn.bootcss.com/history/4.7.2/history.js"></script>
	<script type="text/javascript">
		// let history = History.createBrowserHistory() //方法一,直接使用H5推出的history身上的API
		let history = History.createHashHistory() //方法二,hash值(锚点)

		function push (path) {
			history.push(path)
			return false
		}

		function replace (path) {
			history.replace(path)
		}

		function back() {
			history.goBack()
		}

		function forword() {
			history.goForward()
		}
		//监控请求路径变化
		history.listen((location) => {
			console.log('请求路由路径变化了', location)
		})
	</script>
</body>
</html>

3、安装引入

reactor路由的官方实现有3种

  • web:使用react做web端开发。
  • native:使用react做原生应用开发(移动端)。
  • anywhere:兼容web与native开发。

开发web端应用,一般采用web实现,这里使用react-router-dom的版本5。

安装命令:npm install react-router-dom@5

4、入门使用

import React, { Component } from 'react';
import {Link,Route,BrowserRouter} from 'react-router-dom';
import './App.css';

class ComponentA extends Component {
  render() {
    return (
      <div>组件A</div>
    )
  }
}

class ComponentB extends Component {
  render() {
    return (
      <div>组件B</div>
    )
  }
}

export default class App extends Component {
  render() {
    return (
      <div>
        {/* 导航区和对应的展示区,需要使用BrowserRouter或HashRouter组件包裹起来 */}
        <BrowserRouter> 
          <div style={{width:'300px',height:'100px',border:'1px solid'}}>
            <h3>导航区域</h3>
            {/* 
                Link组件,路由的导航组件。点击此组件时,页面上对应的组件将会被挂载。
              */}
            <Link to="/a">展示组件A</Link> 
            &nbsp;&nbsp;&nbsp;
            <Link to="/b">展示组件B</Link>
          </div>
          <br />
          <div style={{width:'300px',height:'100px',border:'1px solid'}}>
            <h3>内容展示区域</h3>
            {/* 
                Route组件,路由的展示组件。path属性与Link组件的to属性对应。
            */}
            <Route path="/a" component={ComponentA}/>
            <Route path="/b" component={ComponentB}/>
          </div>
        </BrowserRouter>
      </div>
    )
  }
}

效果:地址栏虽然发生了改变,但是浏览器并未发起网络请求,React路由只是根据地址栏变化,挂载了不同的组件。

在这里插入图片描述

使用BrowserRouter或HashRouter组件时,一劳永逸的办法是写在src\index.js里面。

import React from 'react';
import ReactDOM from 'react-dom/client';
import {BrowserRouter} from 'react-router-dom'
import App from './App';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
    <BrowserRouter>
        <App />
    </BrowserRouter>
);

BrowserRouter与HashRouter组件

二者浏览器地址栏展示方式不同,BrowserRouter地址栏域名与path间使用/分割;HashRouter地址栏域名与path间使用/#/分割,与锚点链接一致。

效果:BrowserRouter(左),HashRouter(右)

BrowserRouter
HashRouter

5、路由组件&一般组件

一般组件:直接挂载在页面的组件。

<ComponentA/>

路由组件:借助路由方式挂载到页面的组件。

<Route path="/a" component={ComponentA}/>

区别:

  • 挂载到页面的写法不同,如上。
  • 按照约定,在项目中存放的位置不同,一般组件通常存放在components目录,路由组件通常存放在pages目录(不绝对)。
  • 默认接收到的props属性值不同。一般组件传什么就接收到什么,路由组件的props属性固定接收到historylocationmatch三个值。
history:
	goBack: ? goBack() //后退到上一页面
	goForward: ? goForward() //前进到下一页面
	go: ? go(n) //后退或前进n个页面,负数后退,正数前进
	push: ? push(path, state) //浏览记录入栈新页面,且浏览器地址栏改为新页面地址,state可选
	replace: ? replace(path, state) //浏览末位记录替换为新页面,且浏览器地址栏改为新页面地址,state可选
location:
	pathname: "" //路径
	search: "" //路由组件search参数
	state: undefined //路由组件state参数
match:
	params: {} //路由组件params参数
	path: "" //路径
	url: "" //url

6、NavLink组件

NavLink导航组件被点击时,会自动为当前组件的html标签添加class='active'属性,只需要给类名active编写css样式,页面就能友好展示当前正被点击的导航组件。

使用NavLink组件替换Link组件

{/* 当此组件被点击时,会自动为其添加class=active */}
<NavLink to="/a">展示组件A</NavLink> 
<NavLink to="/b">展示组件B</NavLink>

为class=active编写样式

.active{
    font-size: 14px;
    color: #fff;
    height: 44px;
    padding: 0 15px;
    background-color: #07c160;
    border: 1px solid #07c160;
    line-height: 1.2;
    text-align: center;
    border-radius: 2px;
    cursor: pointer;
    transition: opacity 0.2s;
    outline: none;
    position: relative;
}

效果

在这里插入图片描述

NavLink自定义点击事件类名

{/* 当此组件被点击时,会自动为其添加class=haha */}
<NavLink activeClassName="haha" to="/a">展示组件A</NavLink> 

7、Switch组件

当浏览器地址发生变化,路由组件会被全部遍历一遍,满足匹配的路由组件均会被挂载。

路由组件

<Route path="/a" component={ComponentA}/>
<Route path="/b" component={ComponentB}/>
<Route path="/b" component={ComponentB}/>

效果

在这里插入图片描述

使用Switch将路由组件包裹起来,在当前Switch中,当匹配到一个满足条件的路由组件,便停止遍历,也可提高匹配效率。

路由组件

<Switch>
    <Route path="/a" component={ComponentA}/>
    <Route path="/b" component={ComponentB}/>
    <Route path="/b" component={ComponentB}/>
</Switch>

效果

在这里插入图片描述

如果存在多个Switch,每个Switch中的路由组件都会被遍历匹配一次

路由组件

<Switch>
    <Route path="/a" component={ComponentA}/>
    <Route path="/b" component={ComponentB}/>
</Switch>

<Switch>
    <Route path="/a" component={ComponentA}/>
</Switch>
{/* 两个Switch都会被匹配一次,所以ComponentA被匹配到时会被挂载2次 */}

效果

在这里插入图片描述

8、路由匹配模式

模糊匹配:React路由默认使用模糊匹配。

{/* 导航组件 */}
<NavLink to="/a/a-1">展示组件A</NavLink> 

<Switch>
    {/* 路由组件,因为默认模糊匹配,所以此组件能匹配上面的导航组件 */}
    <Route path="/a" component={ComponentA}/>
</Switch>

精准匹配:需要手动开启(精准匹配开启需要慎重,可能会导致二级路由失效)。

{/* 导航组件 */}
<NavLink to="/a/a-1">展示组件A</NavLink> 

<Switch>
    {/* 路由组件,使用exact属性开启精准匹配,此时此组件不能匹配上面的导航组件 */}
    <Route exact={true} path="/a" component={ComponentA}/>
    {/* 开启精准匹配简写方式 */}
    <Route exact path="/a" component={ComponentA}/>
</Switch>

Redirect组件(默认匹配):当Switch中所有路由组件均无法匹配时,被默认挂载的组件。

<Switch>
    <Route exact path="/a" component={ComponentA}/>
    <Route path="/b" component={ComponentB}/>
    {/* 在本文档中,浏览器默认打开 http://localhost:3000/ ,React路由会默认开始匹配 / ,所有路由组件均无法匹配,便使用Redirect中匹配的路由组件 */}
    <Redirect to="/a"/>
</Switch>

在这里插入图片描述

9、路由嵌套

通过路由的方式挂载的组件,其内部又通过路由挂载组件,就形成路由嵌套。

import React, { Component } from 'react';
import {NavLink,Route,Switch,Redirect} from 'react-router-dom';
import './index.css';

export default class Levels extends Component {
  render() {
    return (
      <div>
        <div className='container'>
          <h3>导航区域</h3>
          <NavLink to="/a">展示组件A</NavLink> 
          &nbsp;&nbsp;&nbsp;
          <NavLink to="/b">展示组件B</NavLink>
        </div>
        <br />

        <div className='container'>
          <h3>1级路由展示区域</h3>
          <Switch>
            <Route path="/a" component={ComponentA}/>
            <Route path="/b" component={ComponentB}/>
          </Switch>
        </div>
      </div>
    )
  }
}

class ComponentA extends Component {
  render() {
    return (
      <div>
        {/* 
          1.注册子路由时要写上父路由的path值
		  2.路由的匹配是按照注册路由的顺序进行的(先匹配挂载父路由组件,再匹配挂载子路由组件,父路由匹配依赖模糊匹配)
        */}
        <NavLink to="/a/msg">消息</NavLink> 
          &nbsp;&nbsp;&nbsp;
        <NavLink to="/a/myself">个人中心</NavLink>
        <br />
        <div className='container2'>
          <h3>2级路由展示区域</h3>
          <Switch>
            <Route path="/a/msg" component={Msg}/>
            <Route path="/a/myself" component={Myself}/>
          </Switch>
        </div>
      </div>
    )
  }
}

class ComponentB extends Component {
  render() {
    return (
      <div>组件B</div>
    )
  }
}

class Msg extends Component {
  render() {
    return (
      <div>消息组件</div>
    )
  }
}

class Myself extends Component {
  render() {
    return (
      <div>个人中心组件</div>
    )
  }
}

效果

在这里插入图片描述

10、向路由组件传参

params参数

import React, { Component } from 'react';
import {NavLink,Route,Switch,Redirect} from 'react-router-dom';
import './index.css';

export default class Params extends Component {
  render() {
    const person = {
        name:"张三",
        age:18
    }
    return (
      <div>
        <div className='container'>
          <h3>导航区域</h3>
          {/* params参数:直接在导航组件to属性里面传参 */}
          <NavLink to={`/a/${person.name}/${person.age}`}>展示组件A</NavLink> 
        </div>
        <br />

        <div className='container'>
          <h3>展示区域</h3>
          <Switch>
            {/* params参数:路由组件path后面,使用:符号接收参数 */}
            <Route path="/a/:name/:age" component={ComponentA}/>
          </Switch>
        </div>
      </div>
    )
  }
}

class ComponentA extends Component{
 render(){
    // params参数:存放在 this.props.match.params 里面,已经被自动封装成对象。
    const {name,age} = this.props.match.params;
    return(
        <div>
            <p>组件A</p>
            <p>接收到的params数据:{name},{age}</p>
        </div>
    )
 }
}

效果

在这里插入图片描述

search参数

{/* search参数:导航组件在?符号后面传参 */}
<NavLink to="/a?name=张三&age=18">展示组件A</NavLink> 

{/* search参数:路由组件无需修改,正常引入即可 */}
<Route path="/a" component={ComponentA}/>

class ComponentA extends Component{
 render(){
    /**
     * search参数:存放在 this.props.location.search 里面
     * 接收到的是urlencoded编码字符串,可以借助第三方库解析结果。如querystring库
     * 因为本参数存在中文,接收到的结果
     *      可能为:?name=张三&age=18
     *      也可能为:?name=%E5%BC%A0%E4%B8%89&age=18
     */
    const p = this.props.location.search;
    return(
        <div>
            <p>组件A</p>
            <p>接收到的params数据:{p}</p>
        </div>
    )
 }
}

state参数

params、search方式传参使用方便,但是参数会显示在地址栏,若传递的参数不想被用户看到,可使用state方式传参。

{/* state参数:导航组件to属性中使用state传参 */}
<NavLink to={{pathname:'/a',state:{name:'tom',age:18}}}>展示组件A</NavLink>

{/* state参数:路由组件无需修改,正常引入即可 */}
<Route path="/a" component={ComponentA}/>

class ComponentA extends Component{
 render(){
    /**
     * state参数:存放在 this.props.location.state 里面
     * 并且刷新也可以保留住参数(仅针对BrowserRouter)
     */
    const {name,age} = this.props.location.state;
    return(
        <div>
            <p>组件A</p>
            <p>接收到的params数据:{name},{age}</p>
        </div>
    )
 }
}

演示

在这里插入图片描述

11、push&replace

浏览器历史记录存储方式为栈结构(先进后出),React路由仅改变地址栏,未发起网络请求,但也会生成保留历史记录。

push:入栈一条页面记录,浏览器地址栏改为新入栈的地址(默认),会留下历史记录,可以回退。

replace:直接替换浏览器当前展示的页面记录,不会留下历史记录,不能回退(应用场景:登录成功不能回退到登录页面、支付成功不能返回待支付页面等)。

演示push导航组件

{/* 为导航组件添加push属性,标识为push操作 */}
<NavLink push to="/a">展示组件A</NavLink> 
&nbsp;&nbsp;&nbsp;
{/* 导航组件不添加push属性,默认为push操作 */}
<NavLink to="/b">展示组件B</NavLink>

在这里插入图片描述

演示replace导航组件

{/* 为导航组件添加replace属性,标识为replace操作 */}
<NavLink replace to="/a">展示组件A</NavLink> 
&nbsp;&nbsp;&nbsp;
<NavLink replace to="/b">展示组件B</NavLink>

在这里插入图片描述

12、编程式路由导航

借助导航组件组件(Link、NavLink等)可以控制路由组件的挂载逻辑,也能控制页面回退、前进访问历史记录,这些操作都需要点击触发导航组件,在某些情况下,页面没有导航组件可以点击,也需要实现这些功能。如点击网站logo图标跳转网站首页、支付成功3秒后自动挂载订单详情组件等。

借助路由组件提供的属性中的API,也能动态实现导航组件的功能。

history:
	goBack: ? goBack() //后退到上一页面
	goForward: ? goForward() //前进到下一页面
	go: ? go(n) //后退或前进n个页面,负数后退,正数前进
	push: ? push(path, state) //浏览记录入栈新页面,且浏览器地址栏改为新页面地址,state可选
	replace: ? replace(path, state) //浏览末位记录替换为新页面,且浏览器地址栏改为新页面地址,state可选
location:
	pathname: "" //路径
	search: "" //路由组件search参数
	state: undefined //路由组件state参数
match:
	params: {} //路由组件params参数
	path: "" //路径
	url: "" //url

withRouter:当一般组件中想要利用路由组件的history、location、match属性动态实现导航组件功能时,需要借助withRouter。

//导入withRouter
import {withRouter} from 'react-router-dom'

class MyComponent extends Component {
	//省略组件内容,使用路由组件的属性
}

//导出组件时,使用withRouter修饰组件,此组件就能使用路由组件所特有的API
export default withRouter(MyComponent)

13、BrowserRouter与HashRouter的区别

1.底层原理不一样:
			BrowserRouter使用的是H5的history API,不兼容IE9及以下版本。
			HashRouter使用的是URL的哈希值,兼容性好。
2.path表现形式不一样
			BrowserRouter的路径中没有#,例如:localhost:3000/demo/test
			HashRouter的路径包含#,例如:localhost:3000/#/demo/test
3.刷新后对路由state参数的影响
			(1).BrowserRouter没有任何影响,因为state保存在history对象中。
			(2).HashRouter刷新后会导致路由state参数的丢失。
4.备注:HashRouter可以用于解决一些路径错误相关的问题。
  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2022-09-30 00:43:36  更:2022-09-30 00:45:08 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/17 19:05:12-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码