React学习04-React_Ajax

news2024/12/28 9:46:54

写在前面

  1. React本身只关注于界面,并不包含发送ajax请求的代码
  2. 前端应用需要通过ajax请求与后台进行交互(json数据)
  3. React应用中需要集成第三方ajax库(或自己封装)

常用的ajax请求库

  • jQuery:比较重,如果需要另外引入不建议使用
  • axios:轻量级,建议使用
    • npm i axios
    • 封装XmlHttpRequest对象的ajax
    • promise风格
    • 可以用在浏览器端和node服务器端

启动server.js服务

server1.js

const express = require('express')
const app = express()

app.use((request,response,next)=>{
	console.log('有人请求服务器1了');
	console.log('请求来自于',request.get('Host'));
	console.log('请求的地址',request.url);
	next()
})

app.get('/students',(request,response)=>{
	const students = [
		{id:'001',name:'tom',age:18},
		{id:'002',name:'jerry',age:19},
		{id:'003',name:'tony',age:120},
	]
	response.send(students)
})

app.listen(5000,(err)=>{
	if(!err) console.log('服务器1启动成功了,请求学生信息地址为:http://localhost:5000/students');
})

server2.js

const express = require('express')
const app = express()

app.use((request,response,next)=>{
	console.log('有人请求服务器2了');
	next()
})

app.get('/cars',(request,response)=>{
	const cars = [
		{id:'001',name:'奔驰',price:199},
		{id:'002',name:'马自达',price:109},
		{id:'003',name:'捷达',price:120},
	]
	response.send(cars)
})

app.listen(5001,(err)=>{
	if(!err) console.log('服务器2启动成功了,请求汽车信息地址为:http://localhost:5001/cars');
})

脚手架配置代理

直接发送请求时会跨域,需要为脚手架配置代理(跨域时请求可发出,服务器可接收,但客户端无法接收返回的信息)

方法一

package.json中追加配置:"proxy": "http://localhost:5000",,请求时直接请求项目地址。

  1. 优点:配置简单,前端请求资源时可以不加任何前缀。
  2. 缺点:不能配置多个代理。
  3. 工作方式:上述方式配置代理,当请求了3000不存在的资源时,那么该请求会转发给5000 (优先匹配前端资源)。

方法二

src目录下创建配置文件:src/setupProxy.js

  1. 优点:可以配置多个代理,可以灵活的控制请求是否走代理。
  2. 缺点:配置繁琐,前端请求资源时必须加前缀。
// commonJS写法,不能用ES6写法

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:5000
      	changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:3000
      	changeOrigin默认值为false,但我们一般将changeOrigin值设为true
      */
      pathRewrite: {'^/api1': ''} //去除请求前缀,保证交给后台服务器的是正常请求地址(必须配置)
    }),
    createProxyMiddleware('/api2', { 
      target: 'http://localhost:5001',
      changeOrigin: true,
      pathRewrite: {'^/api2': ''}
    })
  )
}

gitee搜索用户案例

image.png

静态组件

App.jsx

import React, { Component } from 'react'

import List from './components/List'
import Search from './components/Search'

export default class App extends Component {
    render() {
        return (
            <div className="container">
                <Search />
                <List />
            </div>
        )
    }
}

components/Search/index.jsx

import React, { Component } from 'react'

export default class Search extends Component {
    render() {
        return (
            <section className="jumbotron">
            <h3 className="jumbotron-heading">搜索Gitee用户</h3>
            <div>
                <input type="text" placeholder="键入想要搜索的内容"/>&nbsp;<button>搜索</button>
            </div>
            </section>
        )
    }
}

components/List/index.jsx

import React, { Component } from 'react'
import './index.css'

export default class List extends Component {
    render() {
        return (
            <div className="row">
                <div className="card">
                    <a rel="noreferrer" href="https://github.com/reactjs" target="_blank">
                    <img alt="name" src="https://avatars.githubusercontent.com/u/6412038?v=3" style={{width: '100px'}}/>
                    </a>
                    <p className="card-text">reactjs</p>
                </div>
            </div>
        )
    }
}

components/List/index.css

.album {
    min-height: 50rem;
    /* Can be removed; just added for demo purposes */
    padding-top: 3rem;
    padding-bottom: 3rem;
    background-color: #f7f7f7;
}

.card {
    float: left;
    width: 33.333%;
    padding: .75rem;
    margin-bottom: 2rem;
    border: 1px solid #efefef;
    text-align: center;
}

.card>img {
    margin-bottom: .75rem;
    border-radius: 100px;
}

.card-text {
    font-size: 85%;
}

axios发送请求

请求地址:https://gitee.com/api/v5/search/users?q=xxxxx

据天禹老师所讲,请求过于频繁可能会401,因此提供了一个本地的服务器用于存放一部分数据以保证项目的顺利展示,这里就不写了

export default class Search extends Component {

    searchUser = () => {
        // 获取用户的输入
        const {keyWordElement: {value: keyWord}} = this // 解构赋值的连续写法 + 重命名
        // 发送网络请求
        axios.get(`https://gitee.com/api/v5/search/users?q=${keyWord}`).then(
            value => {
                console.log('success', value.data)
            },
            reason => {
                console.log('fail', reason)
            }
        )
    }

    render() {
        return (
            <section className="jumbotron">
            <h3 className="jumbotron-heading">搜索Gitee用户</h3>
            <div>
                <input ref={c => this.keyWordElement = c} type="text" placeholder="键入想要搜索的内容"/>&nbsp;
                <button onClick={this.searchUser}>搜索</button>
            </div>
            </section>
        )
    }
}

解构赋值与连续解构赋值:

let obj = {a: {b: {c: 1}}}
let obj2 = {a: {b: 1}}

console.log('@', obj.a.b.c)

const {a: {b: {c}}} = obj
console.log('#', c)

const {a: {b: data}} = obj2
console.log('$', data)

image.png

展示数据

App组件接收用户数据并传递给List组件

export default class App extends Component {

    state = {users: []} // 初始化状态,users初始值为数组

    saveUsers = (users) => {
        this.setState({users})
    }

    render() {
        return (
            <div className="container">
                <Search saveUsers={this.saveUsers}/>
                <List users={this.state.users}/>
            </div>
        )
    }
}

List组件展示数据

export default class List extends Component {
    render() {
        return (
            <div className="row">
                {
                    this.props.users.map((userObj) => {
                        return (
                            <div key={userObj.id} className="card">
                                <a rel="noreferrer" href={userObj.html_url} target="_blank">
                                <img alt={userObj.name} src={userObj.avatar_url==='https://gitee.com/assets/no_portrait.png' ? 'https://avatars.githubusercontent.com/u/6412038?v=3' : userObj.avatar_url + '!avatar200'} style={{width: '100px'}}/>
                                </a>
                                <p className="card-text">{userObj.login}</p>
                            </div>
                        )
                    })
                }
            </div>
        )
    }
}

GIF 2022-4-13 18-09-24.gif

优化展示

App.jsx

import React, { Component } from 'react'

import List from './components/List'
import Search from './components/Search'

export default class App extends Component {

    state = { // 初始化状态
        users: [], // users初始值为数组
        isFirst: true, // 是否首次打开页面
        isLoading: false, // 是否加载中
        err: '' // 存储请求错误
    }

    // 更新App的state
    updateAppState = (stateObj) => {
        this.setState(stateObj)
    }

    /*saveUsers = (users) => {
        this.setState({users})
    }

    changeIsFirst = (users) => {
        this.setState({users})
    }

    changeIsLoading = (users) => {
        this.setState({users})
    }

    saveErr = (users) => {
        this.setState({users})
    }*/

    render() {
        return (
            <div className="container">
                <Search updateAppState={this.updateAppState}/>
                <List {...this.state}/>
            </div>
        )
    }
}

components/Search/index.jsx

import React, { Component } from 'react'
import axios from 'axios'

export default class Search extends Component {

    searchUser = () => {
        // 获取用户的输入
        const {keyWordElement: {value: keyWord}} = this // 解构赋值的连续写法 + 重命名
        // 发送请求前通知App更新状态
        this.props.updateAppState({isFirst: false, isLoading: true})
        // 发送网络请求
        axios.get(`https://gitee.com/api/v5/search/users?q=${keyWord}`).then(
            value => {
                // 请求成功后通知App更新状态
                this.props.updateAppState({isLoading: false, users: value.data})
            },
            reason => {
                // 请求失败后通知App更新状态
                this.props.updateAppState({isLoading: false, err: reason.message})
            }
        )
    }

    render() {
        return (
            <section className="jumbotron">
            <h3 className="jumbotron-heading">搜索Gitee用户</h3>
            <div>
                <input ref={c => this.keyWordElement = c} type="text" placeholder="键入想要搜索的内容"/>&nbsp;
                <button onClick={this.searchUser}>搜索</button>
            </div>
            </section>
        )
    }
}

components/List/index.jsx

import React, { Component } from 'react'
import './index.css'

export default class List extends Component {
    render() {
        const {users, isFirst, isLoading, err} = this.props
        return (
            <div className="row">
                {
                    isFirst ? <h2>欢迎使用</h2> : isLoading ? <h2>加载中...</h2> :
                    err ? <h2 style={{color: 'red'}}>{err}</h2> :
                    users.map((userObj) => {
                        return (
                            <div key={userObj.id} className="card">
                                <a rel="noreferrer" href={userObj.html_url} target="_blank">
                                <img alt={userObj.name} src={userObj.avatar_url==='https://gitee.com/assets/no_portrait.png' ? 'https://avatars.githubusercontent.com/u/6412038?v=3' : userObj.avatar_url + '!avatar200'} style={{width: '100px'}}/>
                                </a>
                                <p className="card-text">{userObj.login}</p>
                            </div>
                        )
                    })
                }
            </div>
        )
    }
}

消息订阅与发布

在需要接收数据的组件里订阅消息,当其他组件发布该消息时,就会执行其中的回调

使用PubSubJS库:npm install pubsub-js

引入PubSubJS库:import PubSub from 'pubsub-js'

App组件删除所有与state相关的操作,将state保存在List组件中

List组件中消息订阅;

componentDidMount() {
    this.token = PubSub.subscribe('state', (msg, data) => { // msg适用于多个订阅时,区分订阅来源
        this.setState(data)
    })
}

componentWillUnmount() {
    PubSub.unsubscribe(this.token) // 在组件销毁前取消订阅,防止状态泄露
}

Search组件中消息发布:

searchUser = () => {
    // 获取用户的输入
    const {keyWordElement: {value: keyWord}} = this // 解构赋值的连续写法 + 重命名
    // 发送请求前通知List更新状态
    PubSub.publish('state', {isFirst: false, isLoading: true})
    // 发送网络请求
    axios.get(`https://gitee.com/api/v5/search/users?q=${keyWord}`).then(
        value => {
            // 请求成功后通知List更新状态
            PubSub.publish('state', {isLoading: false, users: value.data})
        },
        reason => {
            // 请求失败后通知List更新状态
            PubSub.publish('state', {isLoading: false, err: reason.message})
        }
    )
}

fetch发送请求

fetch文档地址

segmentfault上的fetch介绍

通过console.log()感受一下fetch的“关注分离”:

fetch(`https://gitee.com/api/v5/search/users?q=${keyWord}`).then(
    response => {
        console.log('联系服务器成功')
        return response.json()
    },
    reason => {console.log('联系服务器失败', reason)}
).then(
    response => {
        console.log('获取数据成功', response);
    },
    reason => {console.log('获取数据失败', reason)}
)

image.png

如果联系服务器失败了(比如断网时发送请求)会输出什么?

联系服务器失败 reason
获取数据成功 undefined
  1. fetch: 原生函数,不再使用XmlHttpRequest对象提交ajax请求
  2. 老版本浏览器可能不支持

全部代码:

import React, { Component } from 'react'
import PubSub from 'pubsub-js'
// import axios from 'axios'

export default class Search extends Component {

    searchUser = async() => {
        // 获取用户的输入
        const {keyWordElement: {value: keyWord}} = this // 解构赋值的连续写法 + 重命名
        // 发送请求前通知List更新状态
        // this.props.updateAppState({isFirst: false, isLoading: true})
        PubSub.publish('state', {isFirst: false, isLoading: true})
        //#region 发送网络请求 --- 使用axios发送
        /* axios.get(`https://gitee.com/api/v5/search/users?q=${keyWord}`).then(
            value => {
                // 请求成功后通知List更新状态
                // this.props.updateAppState({isLoading: false, users: value.data})
                PubSub.publish('state', { isLoading: false, users: value.data })
            },
            reason => {
                // 请求失败后通知List更新状态
                // this.props.updateAppState({isLoading: false, err: reason.message})
                PubSub.publish('state', { isLoading: false, err: reason.message })
            }
        ) */
        //#endregion
    
        //#region 发送网络请求 --- 使用fetch发送 -- 未优化
        /* fetch(`https://gitee.com/api/v5/search/users?q=${keyWord}`).then(
            response => {
                console.log('联系服务器成功')
                return response.json()
            },
            reason => {
                console.log('联系服务器失败', reason)
                return new Promise() // 中断Promise链
            }
        ).then(
            response => {
                console.log('获取数据成功', response);
            },
            reason => {console.log('获取数据失败', reason)}
        ) */
        //#endregion

        //#region 发送网络请求 --- 使用fetch发送 -- 优化版本
        /* fetch(`https://gitee.com/api/v5/search/users?q=${keyWord}`).then(
            response => {
                console.log('联系服务器成功')
                return response.json()
            }
        ).then(
            response => {
                console.log('获取数据成功', response);
            }
        ).catch(
            error => {console.log('请求出错', error)}
        ) */
        //#endregion

        try {
            const response = await fetch(`https://gitee.com/api/v5/search/users?q=${keyWord}`)
            const data = await response.json() // await要在async里用
            // console.log(data)
            PubSub.publish('state', { isLoading: false, users: data })
        } catch (error) {
            // console.log('请求出错', error)
            PubSub.publish('state', { isLoading: false, err: error.message })
        }

    }

    render() {
        return (
            <section className="jumbotron">
            <h3 className="jumbotron-heading">搜索Gitee用户</h3>
            <div>
                <input ref={c => this.keyWordElement = c} type="text" placeholder="键入想要搜索的内容"/>&nbsp;
                <button onClick={this.searchUser}>搜索</button>
            </div>
            </section>
        )
    }
}

总结

  1. 设计状态时要考虑全面,例如带有网络请求的组件,要考虑请求失败怎么办

  2. ES6解构赋值+重命名:

    let obj = {a: {b: 1}}
    const {a} = obj // 传统解构赋值
    const {a: {b}} = obj // 连续解构赋值
    const {a: {b: value}} = obj // 连续解构赋值 + 重命名
    
  3. 消息订阅与发布机制

    • 先订阅,再发布
    • 适用于任意组件间通信
    • 要在组件的componentWillUnmount中取消订阅
  4. fetch发送请求(关注分离的设计思想)

    try {
        const response = await fetch(`https://gitee.com/api/v5/search/users?q=${keyWord}`)
        const data = await response.json() // await要在async里用
        // console.log(data)
        PubSub.publish('state', { isLoading: false, users: data })
    } catch (error) {
        // console.log('请求出错', error)
        PubSub.publish('state', { isLoading: false, err: error.message })
    }
    // 外面需要包一个 async function
    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/128017.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C|%输出各种类型的值总结

参考&#xff1a; C语音输出各种类型数据的方式 c语言中%s的用法 %d&#xff1a;十进制有符号整数 输出的数据范围是 signed int 的范围&#xff0c;即 0x80000000 ~ 0x7FFFFFFF&#xff08;-2147483648~2147483647&#xff09; %u&#xff1a;十进制无符号整数 输出范围0~0xF…

JVM-垃圾回收

1、GC分类 &#xff08;1&#xff09;部分收集&#xff08;Partial GC&#xff09; 新生代收集&#xff08;Minor GC/Young GC&#xff09;&#xff1a;只对新生代进行垃圾收集。&#xff08;读音[ˈmaɪnə(r)]&#xff09; 老年代收集&#xff08;Major GC/Old GC&#xff09…

基于SpringBoot+mybatis+mysql+html家具城进销存管理系统

基于SpringBootmybatismysqlhtml家具城进销存管理系统一、系统介绍二、功能展示1.用户登陆2.进货管理(货物管理员)3.退货管理&#xff08;货物管理员&#xff09;4.商品销售、商品销售退货(商品管理员)5.管理商品、商品类别、供应商、客户&#xff08;商品管理员&#xff09;6.…

DirtyPipe(CVE-2022-0847)漏洞分析

前言 CVE-2022-0847 DirtyPipe脏管道漏洞是Linux内核中的一个漏洞&#xff0c;该漏洞允许写只读文件&#xff0c;从而导致提权。 调试环境 ubuntu 20.04Linux-5.16.10qemu-system-x86_64 4.2.1 漏洞验证 首先创建一个只读文件foo.txt&#xff0c;并且正常情况下是无法修改…

微信小程序集成three.js--6.利用raycastor选择对象

1.实例演示 小程序集成Three.js&#xff0c;利用raycaster实现对象的2.源码 &#xff08;1&#xff09;引入库并声明变量 import * as THREE from ../../libs/three.weapp.js import {OrbitControls } from ../../jsm/controls/OrbitControls const app getApp()// 声明一个…

知识付费系统源码搭建流程讲解、代码分析

知识付费系统是现在多数教培机构优先考虑的线上教学系统&#xff0c;而很多机构自身有技术人员常驻&#xff0c;所以不需要再花费资金去直接购买搭建好的成品系统&#xff0c;转而直接购买源码后&#xff0c;自行搭建配置。 目前&#xff0c;知识付费系统是许多教培机构转型的首…

BOSS直聘自动投简历聊天机器人的实现过程

这两年疫情&#xff0c;公司业务越来越差&#xff0c;必须得准备后路了&#xff0c;每天睡前都会在直聘上打一遍招呼&#xff0c;一直到打哈欠有睡意为止...,这样持续了一周&#xff0c;发现很难坚持&#xff0c;身为一名资深蜘蛛侠&#xff0c;怎么能这样下去呢&#xff1f;于…

Jmeter二次开发实现rsa加密

jmeter函数助手提供了大量的函数&#xff0c;像 counter、digest、random、split、strLen&#xff0c;这些函数在接口测试、性能测试中大量被使用&#xff0c;但是大家在实际工作&#xff0c;形形色色的测试需求不同&#xff0c;导致jmeter自带或者扩展插件给我们提供的函数无法…

LeetCode —— 二叉树

持续更新中................ 二叉树的定义 public class TreeNode {int val;TreeNode left;TreeNode right;public TreeNode() {}public TreeNode(int val) {this.val val;}public TreeNode(int val, TreeNode left, TreeNode right) {this.val val;this.left left;this.r…

nginx看这一篇文章就够了

一、Nginx简介 Nginx是一款轻量级的Web 服务器/反向代理服务器及电子邮件&#xff08;IMAP/POP3&#xff09;代理服务器。是由伊戈尔赛索耶夫为俄罗斯访问量第二的Rambler.ru站点&#xff08;俄文&#xff1a;Рамблер&#xff09;开发的。其特点是占有内存少&#xff0c…

智能机柜PDU助力中小型数据中心机房末端配电系统建设

数据中心作为我国已确定“新基建”的七大领域之一&#xff0c;在国民经济和社会发展中扮演中越来越重要的角色&#xff0c;成为各行各业的关键基础设施&#xff0c;为各产业转型升级提供了重要支撑。据相关数据显示&#xff0c;2018年至今&#xff0c;我国的数据中心市场年均复…

项目管理工具dhtmlxGantt甘特图入门教程(四):可见性和布局视图大小设置

dhtmlxGantt是用于跨浏览器和跨平台应用程序的功能齐全的Gantt图表&#xff0c;可满足项目管理控件应用程序的所有需求&#xff0c;是完善的甘特图图表库。这篇文章给大家讲解 dhtmlxGantt的可见性组、布局大小和隐藏父布局视图。 DhtmlxGantt正版试用下载&#xff08;qun&…

【BP靶场portswigger-服务端3】目录遍历漏洞-6个实验(全)

目录 一、目录遍历 1、意义 二、通过目录遍历阅读任意文件 1、示例 实验1&#xff1a;文件路径遍历&#xff08;简单&#xff09; 三、利用文件路径遍历漏洞的常见障碍 1、对于../等的限制 实验2&#xff1a;文件路径遍历&#xff08;用绝对路径旁路阻止遍历序列&#xf…

相距两千多公里,仅仅数月,从相亲走到结婚 | 2022年复盘日记

&#x1f468;‍&#x1f393;作者&#xff1a;bug菌 &#x1f389;简介&#xff1a;CSDN、掘金等社区优质创作者&#xff0c;全网合计7w粉&#xff0c;对一切技术都感兴趣&#xff0c;重心偏Java方向&#xff0c;目前运营公众号「猿圈奇妙屋」&#xff0c;欢迎小伙伴们的加入&…

制造型企业如何利用WMS系统在大环境中取胜

如今数字化转型已经成为制造型企业高质量高发展的重要手段&#xff0c;越来越多的企业开始加大对数字化转型的投入来提升竞争力。例如&#xff1a;有些企业会上线WMS系统来搭建自己的数字化仓库管理平台&#xff0c;实现仓库业务的自动化和数字化升级。随着制造企业市场规模的不…

基于Xlinx的时序分析与约束(6)----如何读懂vivado下的时序报告?

写在前面 在《基于Xlinx的时序分析与约束&#xff08;3&#xff09;----基础概念&#xff08;下&#xff09;》文章中写了一些时序分析的基础概念&#xff0c;同时还说了文章中提到的公式根本就不需要记忆&#xff0c;因为综合工具vivado会帮你把所有时序路径都做详尽的分析&am…

Springboot延伸之自定义Banner

前言 前几周陆陆续续花了5万多字写了我读到的Springboot启动过程的源码&#xff0c;收获颇多&#xff0c;其中给自己留了扩展的作业。我们每次启动Springboot的时候&#xff0c;都有一个Spring的Banner&#xff0c;读源码的过程中可以得知我们可以自定义属于自己的Banner&…

领域驱动设计

DDD的代码结构_ronshi的博客-CSDN博客_ddd代码结构 DDD( 领域驱动设计)概念来源于2004年著名建模专家eric evans的书籍:《domain-driven design –tackling complexity in the heart of software》(中文译名&#xff1a;领域驱动设计—软件核心复杂性应对之道)。 DDD领域驱动设…

全流量安全分析为IT运维提供高效保障(二)

前言 某高校信息中心老师反应&#xff0c;用户反馈教务系统有访问慢的情况&#xff0c;需要通过流量分析系统来了解系统的运行情况&#xff0c;此报告专门针对系统的性能数据做了分析。 前一章做了系统功能模块分析和整体流量分析&#xff0c;本章将进行异常分析、其他信息发…

【Linux】C语言缓冲区、缓冲区的实现

目录 一、C语言缓冲区 1.1 什么是缓冲区 1.2 缓冲区刷新 1.3 问题解释 1.4 stdout与stderr 1.5 perror的实现 二、模拟实现缓冲区 2.1 接口展示 2.2 函数实现 2.3 测试与源代码 一、C语言缓冲区 1.1 什么是缓冲区 缓冲区本质是一块C语言提供的内存空间&#xff0c…