React ajax请求 及axios、pubsub、fetch使用

React ajax

前置说明

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

常用ajax请求库

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

axios

axios:是对ajax进行了封装,可以实现异步请求
1、前后端分离:在前端中引入axios.js文件
2、模块开发:在项目中安装axios模块

axios中常用的方法:

  • axios.request(config)
  • axios.get(url[, config])
  • axios.delete(url[, config])
  • axios.post(url[, data[, config]])
  • axios.put(url[, data[, config]])

GET请求:

  • 方式一:

    axios({
    		method: 'get',  // 默认请求方式为get
    		url: 'api',
    		params: {  // 传递参数
    			key: value
    		},
    		headers: {  // 设置请求头信息
    			key: value
    		}
    		responseType: 'json'
    	}).then(response => { // 请求成功
    		let res = response.data;
    		console.log(res);
    	}).catch(error => {  // 请求失败,
    		console.log(error);
    });
    
  • 方式二:

    axios.get("api", {
    		params: {   // 传递参数
    			key: value
    		},
    		headers: {  // 设置请求头信息,可以传递空值
    			key: value
    		}
    	}).then(response => {
    		let res = response.data; // 请求成功
    		console.log(res);
    	}).catch(error => {  // 请求失败,
    		console.log(error);
    });
    

POST请求

  • 方式一:

    // 注:post请求方法有的要求参数格式为formdata格式,此时需要借助 Qs.stringify()方法将对象转换为字符串
    let obj = qs.stringify({
    	key: value
    });
    axios({
    	method: 'post',
    	url: 'api',
    	data: obj, // 传递参数
    	headers: {  // 设置请求头信息
    		key: value
    	}
    	responseType: 'json'
    }).then(response => { // 请求成功
    	let res = response.data;
    	console.log(res);
    }).catch(error => { // 请求失败,
    	console.log(error);
    });
    
  • 方式二:

    let data = {key: value},
    let headers = {
    				USERID: "",
    				TOKEN: ""
    			};
    // 若无headers信息时,可传空对象占用参数位置
    axios.post("api", qs.stringify(data), {
    		headers
    }).then(response => {// 请求成功
    	let res = response.data;
    	console.log(res);
    }).catch(error => { // 请求失败
    	console.log(error);
    });
    

Qs的使用:

  • 引用cdn或者使用npm、cnpm或者yarn进行插件安装

  • 使用cdn时,默认全局变量为Qs

  • Qs基本方法使用:

    • qs.stringify() 方法:将目标数据转换为 string字符串
    • qs.parse() 方法:将对象字符串格式的数据转换为 对象格式

消息订阅-发布机制

  1. 工具库: PubSubJS

  2. 下载: npm install pubsub-js --save

  3. 使用:

    import PubSub from 'pubsub-js' //引入
    	
    PubSub.subscribe('delete', function(data){ }); //订阅
    
    PubSub.publish('delete', data) //发布消息
    

Fetch

  • fetch: 原生函数,不再使用XmlHttpRequest对象提交ajax请求

  • 老版本浏览器可能不支持

  • GET请求:

    fetch(url).then(function(response) {
        return response.json()
      }).then(function(data) {
        console.log(data)
      }).catch(function(e) {
        console.log(e)
    });
    
  • POST请求:

    fetch(url, {
        method: "POST",
        body: JSON.stringify(data),
      }).then(function(data) {
        console.log(data)
      }).catch(function(e) {
        console.log(e)
    })
    

案例——github用户搜索

在这里插入图片描述

目录格式:
在这里插入图片描述
代码文件:

1、使用axios方法:

index.html:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    <link rel="stylesheet" href="./css/bootstrap.css">
    <title>React App</title>
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>

index.js:

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

App.js:

import React, { Component } from 'react'
import Search from './components/Search'
import List from './components/List'

export default class App extends Component {

    state = { //初始化状态
        users:[], //users初始值为数组
        isFirst:true, //是否为第一次打开页面
        isLoading:false,//标识是否处于加载中
        err:'',//存储请求相关的错误信息
    }

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

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

setupProxy.js(配置代理):

const proxy = require('http-proxy-middleware')

module.exports = function(app){
	app.use(
		proxy('/api1',{ //遇见/api1前缀的请求,就会触发该代理配置
			target:'http://localhost:5000', //请求转发给谁
			changeOrigin:true,//控制服务器收到的请求头中Host的值
			pathRewrite:{'^/api1':''} //重写请求路径(必须)
		})
	)
}

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%;
}

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>Loading......</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="head_portrait" src={userObj.avatar_url} style={{width:'100px'}}/>
                                            </a>
                                            <p className="card-text">{userObj.login}</p>
                                        </div>
                                    )
                                })
                }
            </div>
        )
    }
}

Search目录下的index.jsx:

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

export default class Search extends Component{
    search=()=>{
        //获取用户的输入  连续解构赋值+重命名
        const {keyWordElement:{value:keyWord}}=this;
        //发送请求前通知App更新状态
        this.props.updateAppState({isFirst: false,isLoading: true})
        //发送网络请求
        axios.get(`/api1/search/users?q=${keyWord}`).then(
            response=>{
                this.props.updateAppState({isLoading: false,users: response.data.items})
            },
            error=>{
                this.props.updateAppState({isLoading: false,err:error.message})
            }
        )
    }
    render() {
        return(
            <section className="jumbotron">
                <h3 className="jumbotron-heading">搜索github用户</h3>
                <div>
                    <input ref={c => this.keyWordElement = c} type="text" placeholder="输入关键词点击搜索"/>&nbsp;
                    <button onClick={this.search}>搜索</button>
                </div>
             </section>
        )
    }
}

服务器 server.js: 需要先启动服务器

const express = require("express")
const axios = require("axios")
const app = express()


/*
  请求地址: http://localhost:3000/search/users?q=aa

  后台路由
    key: /search/users
    value: function () {}
*/
app.get("/search/users", function (req, res) {
  const {q} = req.query
  axios({
    url: 'https://api.github.com/search/users',
    params: {q}
  }).then(response => {
    res.json(response.data)
  })
})

app.listen(5000, "localhost", (err) => {
  if (!err){
  	console.log("服务器启动成功")
  	console.log("请求github真实数据请访问:http://localhost:5000/search/users")
  } 
  else console.log(err);
})

2、使用pubsub方法:

其余文件代码均不变

App.js:

import React, { Component } from 'react'
import Search from './components/Search'
import List from './components/List'

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

List目录下的index.jsx:

import React,{Component} from "react";
import PubSub from 'pubsub-js';
import './index.css'

export default class List extends Component{
    state = { //初始化状态
        users:[], //users初始值为数组
        isFirst:true, //是否为第一次打开页面
        isLoading:false,//标识是否处于加载中
        err:'',//存储请求相关的错误信息
    }
    componentDidMount() {
        this.token=PubSub.subscribe('msg',(_,stateObj)=>{
            this.setState(stateObj);
        })
    }
    componentWillUnmount() {
        PubSub.unsubscribe(this.token);
    }

    render() {
        const {users,isFirst,isLoading,err} = this.state
        return (
            <div className="row">
                {
                    isFirst ? <h2>欢迎使用,输入关键字,随后点击搜索</h2> :
                        isLoading ? <h2>Loading......</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="head_portrait" src={userObj.avatar_url} style={{width:'100px'}}/>
                                            </a>
                                            <p className="card-text">{userObj.login}</p>
                                        </div>
                                    )
                                })
                }
            </div>
        )
    }
}

Search目录下的index.jsx:

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

export default class Search extends Component{
    search=()=>{
        //获取用户的输入  连续解构赋值+重命名
        const {keyWordElement:{value:keyWord}}=this;
        //发送请求前通知List更新状态
        // this.props.updateAppState({isFirst: false,isLoading: true})
        PubSub.publish('msg',{isFirst: false,isLoading: true})
        //发送网络请求
        axios.get(`/api1/search/users?q=${keyWord}`).then(
            response=>{
                // this.props.updateAppState({isLoading: false,users: response.data.items})
                PubSub.publish('msg',{isLoading: false,users: response.data.items})
            },
            error=>{
                // this.props.updateAppState({isLoading: false,err:error.message})
                PubSub.publish('msg',{isLoading: false,err:error.message})
            }
        )

    }
    render() {
        return(
            <section className="jumbotron">
                <h3 className="jumbotron-heading">搜索github用户</h3>
                <div>
                    <input ref={c => this.keyWordElement = c} type="text" placeholder="输入关键词点击搜索"/>&nbsp;
                    <button onClick={this.search}>搜索</button>
                </div>
             </section>
        )
    }
}

3、使用fetch方法:

App.js:

import React, { Component } from 'react'
import Search from './components/Search'
import List from './components/List'

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

List目录下的index.jsx:

import React,{Component} from "react";
import PubSub from 'pubsub-js';
import './index.css'

export default class List extends Component{
    state = { //初始化状态
        users:[], //users初始值为数组
        isFirst:true, //是否为第一次打开页面
        isLoading:false,//标识是否处于加载中
        err:'',//存储请求相关的错误信息
    }
    componentDidMount() {
        this.token=PubSub.subscribe('msg',(_,stateObj)=>{
            this.setState(stateObj);
        })
    }
    componentWillUnmount() {
        PubSub.unsubscribe(this.token);
    }

    render() {
        const {users,isFirst,isLoading,err} = this.state
        return (
            <div className="row">
                {
                    isFirst ? <h2>欢迎使用,输入关键字,随后点击搜索</h2> :
                        isLoading ? <h2>Loading......</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="head_portrait" src={userObj.avatar_url} style={{width:'100px'}}/>
                                            </a>
                                            <p className="card-text">{userObj.login}</p>
                                        </div>
                                    )
                                })
                }
            </div>
        )
    }
}

Search目录下的index.js:

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

export default class Search extends Component{
    search=async ()=> {
        //获取用户的输入  连续解构赋值+重命名
        const {keyWordElement: {value: keyWord}} = this;
        //发送请求前通知List更新状态
        // this.props.updateAppState({isFirst: false,isLoading: true})
        PubSub.publish('msg', {isFirst: false, isLoading: true})

        //发送网络请求
        // axios.get(`/api1/search/users?q=${keyWord}`).then(
        //     response=>{
        //         // this.props.updateAppState({isLoading: false,users: response.data.items})
        //         PubSub.publish('msg',{isLoading: false,users: response.data.items})
        //     },
        //     error=>{
        //         // this.props.updateAppState({isLoading: false,err:error.message})
        //         PubSub.publish('msg',{isLoading: false,err:error.message})
        //     }
        // )
        try{
            const response = await fetch(`/api1/search/users?q=${keyWord}`)
            const data = await response.json()
            PubSub.publish('msg', {isFirst: false, users:data.items})
        }catch (error){
            PubSub.publish('msg', {isLoading: false,err:error.message})
        }

    }
    render() {
        return(
            <section className="jumbotron">
                <h3 className="jumbotron-heading">搜索github用户</h3>
                <div>
                    <input ref={c => this.keyWordElement = c} type="text" placeholder="输入关键词点击搜索"/>&nbsp;
                    <button onClick={this.search}>搜索</button>
                </div>
             </section>
        )
    }
}

案例相关知识点:

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

  2. ES6小知识点:解构赋值+重命名

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

    1. 先订阅,再发布(理解:有一种隔空对话的感觉)
    2. 适用于任意组件间通信
    3. 要在组件的componentWillUnmount中取消订阅
  4. fetch发送请求(关注分离的设计思想)

    try {
        const response= await fetch(`/api1/search/users2?q=${keyWord}`)
        const data = await response.json()
        console.log(data);
    } catch (error) {
       console.log('请求出错',error);
    }
    
  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值