当前位置: 首页 > news >正文

react(3)

七、react ajax

7.1理解

7.1.1前置说明

  1. React本身只关注于界面, 并不包含发送ajax请求的代码

  2. 前端应用需要通过ajax请求与后台进行交互(json数据)

  3. react应用中需要集成第三方ajax库(或自己封装)

7.1.2常用的ajax请求库

  1. jQuery: 比较重, 如果需要另外引入不建议使用

  2. axios: 轻量级, 建议使用

    1. 封装XmlHttpRequest对象的ajax

    2. promise风格

    3. 可以用在浏览器端和node服务器端

7.2脚手架配置代理

在终端分别打开server1.js,server2.js

(在演示时需要重新启动脚手架npm start)

方法一

在package.json中追加如下配置

"proxy":"http://localhost:5000"

说明:

  1. 优点:配置简单,前端请求资源时可以不加任何前缀。

  2. 缺点:不能配置多个代理。

  3. 工作方式:上述方式配置代理,当请求了3000不存在的资源时,那么该请求会转发给5000 (优先匹配前端资源)

方法二

  1. 第一步:创建代理配置文件

    在src下创建配置文件:src/setupProxy.js
  2. 编写setupProxy.js配置具体代理规则:

    const { createProxyMiddleware } = require('http-proxy-middleware')
    ​
    module.exports = function(app) {app.use(createProxyMiddleware('/api1', {  //api1是需要转发的请求(所有带有/api1前缀的请求都会转发给5000)target: 'http://localhost:5000', //配置转发目标地址(能返回数据的服务器地址)changeOrigin: true, //控制服务器接收到的请求头中host字段的值/*changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:5000changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:3000changeOrigin默认值为false,但我们一般将changeOrigin值设为true*/pathRewrite: {'^/api1': ''} //去除请求前缀,保证交给后台服务器的是正常请求地址(必须配置)}),createProxyMiddleware('/api2', { target: 'http://localhost:5001',changeOrigin: true,pathRewrite: {'^/api2': ''}}))
    }

说明:

  1. 优点:可以配置多个代理,可以灵活的控制请求是否走代理。

  2. 缺点:配置繁琐,前端请求资源时必须加前缀。

7.3案例(含消息订阅)

image-20240928113944598

描述:输入关键字,能够搜索出用户并展示(演示前打开服务器)

1.首先分析一下如何拆分组件,我们将这个案例分为List和Search组件,分别创建两个文件,并引入App.js,页面渲染用上组件标签

2.再分析一下网络请求,我们希望建立一个代理服务器,当多次发送请求时,Github可能禁止访问,这时我们可以在代理服务器上虚拟一些数据。

代理此时的端口号是5000,而我们所处的端口号为3000,所以我们采用方法二中的脚手架配置代理

参考上一部分内容即可

4.分析一下这两个组件的逻辑:

首先我们从Search组件中获得关键字,并发送请求,拿到返回值。

然后将返回值传递给List组件,再渲染到页面上。此时我们可以通过消息订阅来完成兄弟组件的通信。

消息订阅

a.首先全局安装消息订阅的包

npm add pubsub-js

b.再在需要使用的两个组件分别引入这两个包

import PubSub from 'pubsub-js'

c.在接收消息的组件中订阅消息

 this.token = PubSub.subscribe('消息名', (msg, data) => {console.log(data)})

d.在发送消息的组件中发送消息

 PubSub.publish('消息名', data)

e.取消消息订阅

PubSub.unsubscribe(this.token)

5.现在我们所需知识都已经掌握,现在写Search组件

首先引入axios库,引入PubSub包,给按钮绑定点击事件,利用ref打标识获取input元素的value值

在点击事件中,首先获取关键字,发送请求前通知List更新消息,发送axios请求。

在axios请求中:

a.域名是我们站在自己的端口号上,并且加上代理所需要的‘/api1’字段

b.利用promise语法,可获得返回数据。通过消息订阅将消息发布出去

import React, { Component } from 'react'
import axios from 'axios'
import PubSub from 'pubsub-js'
export default class Search extends Component {search = () => {// 连续解构赋值 + 重命名const { keyword: { value: keyword } } = this// 发送请求前通知List更新消息PubSub.publish('xixi', { isFirst: false, isLoading: true })// console.log(keyword)axios.get(`http://localhost:3000/api1/search/users?q=${keyword}`).then(response => {// console.log(response.data)PubSub.publish('xixi', { isLoading: false, users: response.data.items })})}render() {return (<div><section className="jumbotron"><h3 className="jumbotron-heading">搜索Github用户</h3><div><input ref={c => this.keyword = c} type="text" placeholder="输入关键字" />&nbsp;<button onClick={this.search}>Search</button></div></section></div>)}
}

6.List组件

a.引入PubSub包

b.初始化state,在页面渲染时将state的数据渲染上去

c.在页面挂载完毕时,订阅消息,将state更新

d.在页面将要被卸载时,取消消息订阅

import React, { Component } from 'react'
import './index.css'
import PubSub from 'pubsub-js'
export default class List extends Component {state = {users: [],//isFirst: true,//是否为第一次打开页面isLoding: false,//标识是否处于加载之中err: ''//存储请求相关的信息错误}componentDidMount() {this.token = PubSub.subscribe('xixi', (msg, data) => {// console.log(data)this.setState(data)
​})}componentWillUnmount() {PubSub.unsubscribe(this.token)}render() {const { users, isFirst, isLoding, err } = this.statereturn (<div><div className="row">{isFirst ? <h2>欢迎使用,输入关键字</h2> :isLoding ? <h2>Loading.....</h2> :err ? <h2>{err}</h2> :users.map((userObj) => {return (<div className="card" key={userObj.id}><a href={userObj.html_url} target="_blank" rel="noreferrer"><img alt='头像' src={userObj.avatar_url} style={{ width: '100px' }} /></a><p className="card-text">{userObj.login}</p></div>)})
​}</div></div>)}
}

八、React路由

8.1相关理解

8.1.1SPA的理解

  1. 单页Web应用(single page web application,SPA)。

  2. 整个应用只有一个完整的页面

  3. 点击页面中的链接不会刷新页面,只会做页面的局部更新。

  4. 数据都需要通过ajax请求获取, 并在前端异步展现。

8.1.2路由的理解

1. 什么是路由?

  1. 一个路由就是一个映射关系(key:value)

点击导航栏,它就改路径,改路径后被浏览器检测,检测到后就展示组件

  1. key为路径, value可能是function或component

1. 路由分类

  1. 后端路由:

    1. 理解: value是function, 用来处理客户端提交的请求。

    2. 注册路由: router.get(path, function(req, res))

    3. 工作过程:当node接收到一个请求时, 根据请求路径找到匹配的路由, 调用路由中的函数来处理请求, 返回响应数据

  2. 前端路由:

    1. 浏览器端路由,value是component,用于展示页面内容。

    2. 注册路由: <Route path="/test" component={Test}>

    3. 工作过程:当浏览器的path变为/test时, 当前路由组件就会变为Test组件

8.2react-router-dom相关API

全局安装react-router-dom

 npm add react-router-dom@5

ps:对react-router-dom的理解:

  1. react的一个插件库。

  2. 专门用来实现一个SPA应用。

  3. 基于react的项目基本都会用到此库。

8.2.1内置组件

1.  <BrowserRouter>
2.  <HashRouter>
3.  <Route>
4.  <Redirect>
5.  <Link>
6.  <NavLink>
7.  <Switch>

8.2.2其他

1.  history对象
2.  match对象
3.  withRouter函数

8.3路由的基本使用

8.3.1路由基本使用的案例

案例演示:通过点击旁边的导航栏,利用路由切换右侧展示区的内容

image-20240928155848277

1.首先分析一下组件划分,标题和导航栏时公共用的,展示区由不同的组件显示,我们分为Home组件和About组件

2.在Home组件和About组件中简单写内容

3.主要是对App组件的编写:

引入react-router-dom库,引入两个所需组件。

Link是路由链接,通过编写路由链接来实现切换组件,to属性是修改路径

Route是注册路由,path属性是匹配对应的路径,component属性用来指定哪一个组件。

// 创建外壳组件App
import React, { Component } from "react"
import { Link, Route } from 'react-router-dom'
import Home from './components/Home'
import About from "./components/About"
export default class App extends Component {render() {return (<div><div className="row"><div className="col-xs-offset-2 col-xs-8"><div className="page-header"><h2>React Router Demo</h2></div></div></div><div className="row"><div className="col-xs-2 col-xs-offset-2"><div className="list-group">{/* <a className="list-group-item" href="./about.html">About</a><a className="list-group-item active" href="./home.html">Home</a> */}<Link className="list-group-item" to="/about">About</Link><Link className="list-group-item" to="/home">Home</Link></div></div><div className="col-xs-6"><div className="panel"><div className="panel-body"><Route path="/about" component={About} /><Route path="/home" component={Home} /></div></div></div></div></div>)}
}

4.由于Link,Route标签都需要包裹在BrowserRouter标签里,并且只需要一个路由器进行管理,所以直接在index.js文件中将App标签包裹起来(记得引入react-router-dom库)

// 引入
import React from "react"
import ReactDOM from "react-dom"
import App from "./App"
import { BrowserRouter } from "react-router-dom"
// 渲染
ReactDOM.render(<BrowserRouter><App /></BrowserRouter>,document.querySelector('#root'))

总结:路由器的基本使用

1.明确导航区和展示区

2.导航区的a标签改为Link标签

3.展示区写Route标签进行路径匹配

4.在App标签外侧包裹BrowserRouter标签

8.3.2路由组件与一般组件

1.写法不同:

一般组件:<Demo/>
路由组件:<Route path="/Demo" component={Demo} />

2.存放地点不同:

一般组件:放在文件夹components
路由组件:放在文件夹pages

3.接收到的props不同:

一般组件:传递的什么就能收到什么
路由组件:history,location,match接收到固定三个属性

8.3.3NavLink

可以追加被点击后的高亮样式的类名

<NavLink activeClassName="active" className="list-group-item" to="/about">About</NavLink>
<NavLink activeClassName="active" className="list-group-item" to="/home">Home</NavLink>
封装NavLink

由于NavLink写起来属性很多,大量编写时不方便,自己定义一个NavLink组件

1.首先在组件的文件夹里创建一个组件,取名MyNavLink。

在MyNavLink组件里,引入react-router-dom库,在返回值时,利用props传递参数

import React, { Component } from 'react'
import { NavLink } from 'react-router-dom'
export default class MyNavLink extends Component {render() {return (<NavLink activeClassName="active" className="list-group-item" {...this.props}></NavLink>)}
}
​

2.在原来使用NavLink的文件里(这里是App.js),使用MyNavLink组件

先引入MyNavLink组件,在导航区使用MyNavLink组件,并传递props值,这里需要注意的是,原来我们使用组件时,用的组件标签是子封闭,但是因为导航栏我们需要传递导航标题,所以我们利用下面这种形式。

<MyNavLink to="/home">Home</MyNavLink>
<MyNavLink to="/about">About</MyNavLink>

标签体内容其实也是特殊的props(可以打开控制台查看),属性名是children

8.3.4Switch

当路径匹配上一个之后,就展示组件,并不再向下进行匹配

备注:如果一个路径对应多个组件,不用Switch的情况下,React将多个组件一起展示,但是建议一个路径对应一个组件

使用方法如下:

1.引入

import { Switch, Route } from 'react-router-dom'

2.用Switch标签将注册路由包裹起来

<Switch><Route path="/about" component={About} /><Route path="/home" component={Home} />
</Switch>

总结:1.通常情况下,path和component是一一对应的关系

2.Switch可以提高路由匹配效率(单一匹配)

8.3.5Redirect重定向

1.一般写在所有路由注册的最下方,当所有路由都无法匹配时,跳转到Redirect指定的路由

2.具体编码

<Redirect to="/指定的路径" />

8.4嵌套路由

案例:在Home组件中还有路由组件

image-20240928204337063

1.首先分析组件构成,大体框架与上一个案例相似,我们主要是改变Home组件的内容。

将Home组件的内容同样分为导航区和展示区,在展示区由News组价和Message组件构成。

所以在Home文件里创建两个子组件的文件夹,并将对应内容渲染在页面上

在Home/News/index.js中

import React, { Component } from 'react'
​
export default class News extends Component {render() {return (<ul><li>news001</li><li>news002</li><li>news003</li></ul>)}
}
​

在Home/Message/index.js中

import React, { Component } from 'react'
export default class Message extends Component {render() {return (<div><ul><li><a href="/message1">message001</a>&nbsp;&nbsp;</li><li><a href="/message2">message002</a>&nbsp;&nbsp;</li><li><a href="/message/3">message003</a>&nbsp;&nbsp;</li></ul></div>)}
}

2.在Home组件中,首先引入两个子组件,再引入自定义的MyNavLink组件,再引入react-router-dom库

在返回虚拟DOM时,编写路由链接,并注册路由

import React, { Component } from 'react'
import News from './News'
import Message from './Message'
import MyNavLink from '../../components/MyNavLink'
import { Route, Switch, Redirect } from 'react-router-dom'
export default class Home extends Component {render() {return (<div><h2>Home组件内容</h2><div><ul className="nav nav-tabs"><li><MyNavLink to="/home/news">News</MyNavLink></li><li><MyNavLink to="/home/message">Message</MyNavLink></li></ul><Switch><Route path="/home/news" component={News} /><Route path="/home/message" component={Message} /><Redirect to="/home/news" /></Switch></div></div>
​)}
}

注意:1.注册子路由时要写上父路由的path值,当React遇到Link标签时,是重新进行路径匹配,所以必须加上父路由的path值

2.路由的匹配是按照注册路由的顺序进行的

解决样式丢失问题

当多级路由后,用户强制刷新时,样式会丢失

解决方案:1.在public/index.html中引入样式时,不写 ./而是写 /

2.在public/index.html中引入样式时,不写 ./而是写%PUBLIC_URL%

3.不使用BrowserRouter标签包裹App标签,使用HashRouter

8.5向路由组件传递参数数据

案例效果:

image-20240929213047750

8.5.1向路由组件传递params参数

1.首先分析组件构成,显然,在Message组件中,又分为导航区和展示区。由于展示区的大体结构相同,所以我们就只写一个路由组件即可,其余信息通过传递参数来获取。

2.在Message文件夹中建立Details组件。

在Detials组件中,我们的消息内容是通过传递的参数来进行匹配的,所以我们先定义一个对象数组。然后返回虚拟DOM,这里结构先简单看一下。

import React, { Component } from 'react'
​
const details = [{ id: '01', content: '你' },{ id: '02', content: '好' },{ id: '03', content: '你好' },
​
]
export default class Details extends Component {
​render() {console.log(this.props)const { id, title } = this.props.match.paramsconst contents = details.find((detailObj) => {return detailObj.id === id})
​return (<ul><li>id:{id}</li><li>title:{title}</li><li>content:{contents.content}</li></ul>)}
}

3.在Message组件中,我们先引入Details组件,引入react-router-dom库

我们先初始化state,创建一个数组。然后在render函数里,首先解构赋值messageArr,用遍历数组的方式渲染导航栏的部分。

在导航栏,我们用Link路由链接编写:由于要传递参数,要用模板字符串。

在展示区,我们用Route注册路由,通过/:的语法传递参数。

import React, { Component } from 'react'
import Details from './Details'
import { Route, Link } from 'react-router-dom'
export default class Message extends Component {state = {messageArr: [{ id: '01', title: '消息1' },{ id: '02', title: '消息2' },{ id: '03', title: '消息3' }]}render() {const { messageArr } = this.statereturn (<div><ul>{messageArr.map((msgObj) => {return (<li key={msgObj.id}>{/* 向路由传递params参数 */}<Link to={`/home/message/details/${msgObj.id}/${msgObj.title}`}>{msgObj.title}</Link></li>)})}</ul><hr />{/* 声明接收params参数 */}<Route path="/home/message/details/:id/:title" component={Details} /></div>
​)}
}

4.路由链接里的参数传递到哪里了?

打印Details组件的props我们发现,props.match.params里

image-20240929214340754

所以我们返回Details组件,将接收到的id,title进行解构赋值,并通过find方法匹配出内容,渲染在页面上。

总结:

1.路由链接(携带参数):

<Link to={`/home/message/details/${msgObj.id}/${msgObj.title}`}>{msgObj.title}</Link>

2.注册路由(声明接收):

<Route path="/home/message/details/:id/:title" component={Details} />

3.子路由组件接收参数:

 const { id, title } = this.props.match.params

8.5.2向路由组件传递search参数

大体结构类似,只是在上面总结的三步有差别

总结:

1.路由链接(携带参数):

<Link to={`/home/message/details/?id=${msgObj.id}&title=${msgObj.title}`}>{msgObj.title}</Link>

2.注册路由(声明接收):

<Route path="/home/message/details" component={Details} />

3.子路由组件接收参数:(引入qs库)

在引入的部分:import qs from 'qs'
在render函数里:const { search } = this.props.locationconst { id, title } = qs.parse(search.slice(1))

备注:获取到的search是urlencoded编码字符串,需要借助querystring解析

8.5.3向路由组件传递state参数

总结:

1.路由链接(携带参数):

<Link to={{ pathname: '/home/message/details', id: msgObj.id, title: msgObj.title }}>{msgObj.title}</Link>

2.注册路由(声明接收):

<Route path="/home/message/details" component={Details} />

3.子路由组件接收参数:(引入qs库)

const { id, title } = this.props.location
const contents = details.find((detailObj) => {return detailObj.id === id
}) || {}

备注:如果强制刷新,state会变成undefined,contents这个部分会报错,所以加上||{},如果是undefined,则赋值为空对象

8.6push与replace

一般默认push模式,是以压栈的形式,但是当你开启replace模式后,是替换栈顶。

开启replace模式:

<Link replace={true} to={{ pathname: '/home/message/details', id: msgObj.id, title: msgObj.title }}>{msgObj.title}</Link>

8.7编程式路由导航

不通过Link或者NavLink标签,使用路由跳转。我们利用this.props.history里的push和replace方法

案例:给按钮绑定事件,可以通过点击按钮选择查看消息的方式。

这个时候不能用Link标签写死了,所以我们给按钮绑定事件,在回调函数里调用this.props.history里的push和replace方法。其他步骤不改变。

回调函数:pushShow = (id, title) => {this.props.history.push(`/home/message/details/${id}/${title}`)}replaceShow = (id, title) => {this.props.history.replace(`/home/message/details/${id}/${title}`)}
绑定事件:
<button onClick={() => { this.pushShow(msgObj.id, msgObj.title) }}>push</button>
<button onClick={() => { this.replaceShow(msgObj.id, msgObj.title) }}>replace</button>

此案例用的params传递参数的形式,其他传递参数的方式可以自己尝试一下

补充:this.props.history.goBack() 回退

this.props.history.goForward() 前进

this.props.history.go(num) 前进或后退num页

8.8withRouter

withRouter可以加工一般组件,让一般组件具有路由组件所特有的API

withRouter的返回值是一个新的组件

使用方法:

import { withRouter } from 'react-router-dom'
export default withRouter(Headers)

相关文章:

  • 以Flask为基础的虾皮Shopee“曲线滑块验证码”识别系统部署
  • pdf怎么编辑修改内容?详细介绍6款pdf编辑器功能
  • Java对象访问机制:句柄访问与直接指针访问
  • 自动化办公-Python中的for循环
  • excel 填充内容的公式
  • react 状态管理
  • 基于51单片机的温湿度上下限监测预警proteus仿真
  • TDD(时分双工 Time Division Duplexing)和FDD(频分双工 Frequency Division Duplexing)
  • Ruby基础语法
  • mTLS(Mutual TLS)即双向传输层安全,是一种安全通信协议,用于在客户端和服务器之间建立双向的身份验证和加密通道。
  • 网络编程自学(4)——异步服务器设计
  • cheese安卓版纯本地离线文字识别插件
  • Python批量处理客户明细表格数据,挖掘更大价值
  • DDL 超时,应该如何解决 | OceanBase 用户问题集萃
  • 指令个人记录
  • “寒冬”下的金三银四跳槽季来了,帮你客观分析一下局面
  • 【跃迁之路】【641天】程序员高效学习方法论探索系列(实验阶段398-2018.11.14)...
  • 11111111
  • C语言笔记(第一章:C语言编程)
  • DataBase in Android
  • E-HPC支持多队列管理和自动伸缩
  • iBatis和MyBatis在使用ResultMap对应关系时的区别
  • iOS | NSProxy
  • Java 内存分配及垃圾回收机制初探
  • js对象的深浅拷贝
  • JS专题之继承
  • KMP算法及优化
  • ReactNative开发常用的三方模块
  • yii2中session跨域名的问题
  • 不上全站https的网站你们就等着被恶心死吧
  • - 概述 - 《设计模式(极简c++版)》
  • 干货 | 以太坊Mist负责人教你建立无服务器应用
  • 构建工具 - 收藏集 - 掘金
  • 精益 React 学习指南 (Lean React)- 1.5 React 与 DOM
  • 配置 PM2 实现代码自动发布
  • 线上 python http server profile 实践
  • k8s使用glusterfs实现动态持久化存储
  • 东超科技获得千万级Pre-A轮融资,投资方为中科创星 ...
  • 如何在 Intellij IDEA 更高效地将应用部署到容器服务 Kubernetes ...
  • 树莓派用上kodexplorer也能玩成私有网盘
  • ​DB-Engines 12月数据库排名: PostgreSQL有望获得「2020年度数据库」荣誉?
  • ​经​纬​恒​润​二​面​​三​七​互​娱​一​面​​元​象​二​面​
  • # 深度解析 Socket 与 WebSocket:原理、区别与应用
  • #NOIP 2014# day.1 T2 联合权值
  • #我与Java虚拟机的故事#连载08:书读百遍其义自见
  • ()、[]、{}、(())、[[]]等各种括号的使用
  • (7)svelte 教程: Props(属性)
  • (Java实习生)每日10道面试题打卡——JavaWeb篇
  • (zt)最盛行的警世狂言(爆笑)
  • (补充):java各种进制、原码、反码、补码和文本、图像、音频在计算机中的存储方式
  • (附源码)计算机毕业设计ssm基于Internet快递柜管理系统
  • (六)vue-router+UI组件库
  • (三)centos7案例实战—vmware虚拟机硬盘挂载与卸载
  • (十五)Flask覆写wsgi_app函数实现自定义中间件
  • (四十一)大数据实战——spark的yarn模式生产环境部署