React系统学习笔记--超基础--超详细--超简洁--React Ajax(四)

此部分需要预备技术栈:ajax、Axios

1 React中配置代理(proxy)

跨域是请求发送了,但是响应被拦截了

产生跨域的本质是浏览器ajax引擎把响应拦截了,而代理服务器(中间人)没有ajax引擎;代理服务器是通过请求转发的方式

方式一简单代理:在package.json中追加如下配置:"proxy":http://localhost:5000

PS:发送get的时候去找http://localhost:3000,再写5000的话还是会产生跨域。配置了如上代理,并不是所有请求都转发给5000,而是你请求时用http://localhost:3000进行请求,当其在3000端口中找不到资源时将会自动转发至5000端口进行请求,不产生跨域问题(先在3000找,找不到了转发给5000)

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

缺点:不能配置多个代理

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

方法二: 在src下创建配置文件:src/setupProxy.js

PS:必须是这个文件名,react项目运行的时候会自动查找这个文件,并将其加入webpack的配置中,所以当你修改此文件后,你需要重新启动项目

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

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

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

2 GitHub搜索案例

在这里插入图片描述

ES6小知识点:连续赋值解构 + 重命名

let obj = {
    
    a:{
    
    b:1}}
const {
    
    a} = obj; //传统解构赋值
const {
    
    a:{
    
    b}} = obj; //连续解构赋值
const {
    
    a:{
    
    b:value}} = obj; //连续解构赋值+重命名
console.log(value)

注意:新的React中setupProxy.js文件里要这样写:

//不是const proxy = require('http-proxy-middleware')
const {
    
     createProxyMiddleware } = require('http-proxy-middleware')

module.exports = function (app) {
    
    
    app.use(
        createProxyMiddleware('/api1', {
    
    
            target: 'http://localhost:5000',
            changeOrigin: true,
            pathRewrite: {
    
     '^/api1': '' }
        })
    )
}

Search获取数据,List需要用数据,但是他们俩是兄弟关系,所以search先把数据给App(这就需要App当年给search一个函数),然后App再把数据给List

App.js

export default class App extends Component {
    
    

  //初始化状态
  state = {
    
     users: [] }

  //状态在哪,操作状态的方法就在哪
  saveUsers = (users) => {
    
    
    this.setState({
    
     users: users })
  }
  render() {
    
    
    return (
      <div className="container">
        {
    
    /* 父给子提前传个函数 saveUsers*/}
        <Search saveUsers={
    
    this.saveUsers} />
        {
    
    /* 自己拿着没用,转手交给儿子 */}
        <List users={
    
    this.state.users} />
      </div>
    )
  }
}

Search.js

export default class Search extends Component {
    
    

    search = () => {
    
    
        //获取用户的输入
        //连续解构赋值写法+重命名
        const {
    
     keyWordNode: {
    
     value: keyWord } } = this
        //发送网络请求
        axios.get(`http://localhost:3000/api1/search/users?q=${
      
      keyWord}`).then(
            response => {
    
    
                this.props.saveUsers(response.data.items)
            },
            error => {
    
     console.log('成功了', error); }
        )
    }
    render() {
    
    
        return (
            <section className="jumbotron">
                <h3 className="jumbotron-heading">搜索Github用户</h3>
                <div>
                    <input ref={
    
    c => {
    
     this.keyWordNode = c }} type="text" placeholder="输入关键词点击搜索" />&nbsp;					  <button onClick={
    
    this.search} >搜索</button>
                </div>
            </section>
        )
    }
}

List.js

export default class List extends Component {
    
    
    render() {
    
    
        return (
            <div className="row">
                {
    
    
                    this.props.users.map((userObj) => {
    
    
                        return (
                            <div className="card" key={
    
    userObj.id}>
                                <a href={
    
    userObj.html_url} target="_blank" rel="noreferrer">
                                    <img alt='head_portrait' src={
    
    userObj.avatar_url} style={
    
    {
    
     width: '100px' }} />
                                </a>
                                <p className="card-text">{
    
    userObj.login}</p>
                            </div>
                        )
                    })
                }
            </div>
        )
    }
}

List除了要展示Users,还要展示:欢迎页面,加载页面,错误信息(状态驱动页面展示,所以提前准备)

完善版本的代码展示:

//App.js
export default class App extends Component {
    
    

  //初始化状态
  state = {
    
    
    //初始化数组
    users: [],
    //是否第一次打开页面
    isFirst: true,
    //是否加载中,发送请求前调为true
    isLoading: false,
    //错误休息
    err: ''
  }

  //状态在哪,操作状态的方法就在哪,更新App的state
  updateAppState = (stateObj) => {
    
    
    this.setState(stateObj)
  }
  render() {
    
    
    return (
      <div className="container">
        <Search updateAppState={
    
    this.updateAppState} />
        <List {
    
    ...this.state} />
      </div>
    )
  }
}

//Search.js
search = () => {
    
    
    //获取用户的输入
    //连续解构赋值写法+重命名
    const {
    
     keyWordNode: {
    
     value: keyWord } } = this
    //发请求前通知App更新状态
    this.props.updateAppState({
    
     isFirst: false, isLoading: true })
    //发送网络请求
    axios.get(`http://localhost:3000/api1/search/users?q=${
      
      keyWord}`).then(
        response => {
    
    
            //请求成功后通知App更新状态
            this.props.updateAppState({
    
     isLoading: false, users: response.data.items })
        },
        error => {
    
    
            //请求失败后通知App更新状态
            this.props.updateAppState({
    
     isLoading: false, err: error.message })
        }
    )
}

//List.js注意写表达式!
export default class List extends Component {
    
    
    render() {
    
    
        const {
    
     isFirst, isLoading, err } = this.props
        return (
            <div className="row">
                {
    
    
                    isFirst ? <h2 className='welcome'>欢迎进入鱼仔的GitHub搜索小案例,请输入关键字点击搜索</h2> :
                        isLoading ? <h2 className='loading'>鱼仔拼命地帮您加载中!!!请稍后!!!</h2> :
                            err ? <h2 className='error'>{
    
    err}</h2> :
                                this.props.users.map((userObj) => {
    
    
                                    return (
                                        <div className="card" key={
    
    userObj.id}>
                                            <a href={
    
    userObj.html_url} target="_blank" rel="noreferrer">
                                                <img alt='head_portrait' src={
    
    userObj.avatar_url} style={
    
    {
    
     width: 											  '100px' }} />
                                            </a>
                                            <p className="card-text">{
    
    userObj.login}</p>
                                        </div>
                                    )
                                })
                }
            </div >
        )
    }
}

3 消息订阅与发布机制

适用于任意组件间通信

使用的工具库: PubSubJS

安装:npm i pubsub-js

GitHub搜索案例的消息订阅与发布版本

//App.js
export default class App extends Component {
    
    
  render() {
    
    
    return (
      <div className="container">
        <Search />
        <List />
      </div>
    )
  }
}
//Search组件发布消息,把数据带给List
search = () => {
    
    
    //获取用户的输入
    //连续解构赋值写法+重命名
    const {
    
     keyWordNode: {
    
     value: keyWord } } = this
    //发请求前通知List更新状态
    PubSub.publish('getData', {
    
     isFirst: false, isLoading: true })
    //发送网络请求
    axios.get(`http://localhost:3000/api1/search/users?q=${
      
      keyWord}`).then(
        response => {
    
    
            //请求成功后通知List更新状态
            PubSub.publish('getData', {
    
     isLoading: false, users: response.data.items })
        },
        error => {
    
    
            //请求失败后通知List更新状态
            PubSub.publish('getData', {
    
     isLoading: false, err: error.message })
        }
    )
}
//List订阅消息,收到数据
//初始化状态--放在List中
state = {
    
    
    //初始化数组
    users: [],
    //是否第一次打开页面
    isFirst: true,
    //是否加载中,发送请求前调为true
    isLoading: false,
    //错误休息
    err: ''
}

componentDidMount() {
    
    
    this.token = PubSub.subscribe('getData', (msg, stateObj) => {
    
    
        this.setState(stateObj)
    })
}

componentWillUnmount() {
    
    
    PubSub.unsubscribe(this.token)
}
//注意这两处不是在propd中取了,是在state
const {
    
     isFirst, isLoading, err } = this.state
this.state.users.map

4 fetch发送请求

复习Ajax

xhr:最原始的,没有封装(内置)

jQuery:封装好的api,可能会产生回调地狱(对xhr的封装)

axios:没有回调地狱(对xhr的封装)

fetch:关注分离的设计思想(就是不一下把数据给你),浏览器原生 AJAX 接口,老版本浏览器可能不支持

fetch发送请求:(联系服务器成功和服务器响应不响应数据是两件事)

  • 第一步,先联系服务器,联系上了再拿数据,此时数据还没取出来(可以直接浏览器断网联系服务器失败),建立联系
  • 第二步,response的原型对象身上有json()函数,response.json()返回的是一个Promise实例,想拿的数据就在这个Promise实例对象身上,联系服务器成功及获取数据也成功,那么这个promise的状态就是成功而且保存你想要的数据,如果联系服务器成功了但获取数据失败了,那promise就是失败的状态,里面保存失败的原因

知识:第一个.then只会返回成功或失败其中一个,.then成功或失败的返回值是一个非promise值,那么.then返回的promise实例状态就是成功的,值就是这个非promise值;如果成功或失败的返回值是一个promise值,那么就作为.then返回的promise实例的值

总结就是如果直接使用fetch,返回的并不是直接的结果它只是一个HTTP响应,而不是真的数据

想要获取数据,方法有二:

  • 使用promise的链式调用,再第一个then中将其返回,再下个then中在使用
  • 使用async+await获取

代码示例:

----------------------------- 未优化:使用then链式调用 ---------------------------------------------------------
fetch(`/api1/search/users2?q=${
      
      keyWord}`).then(
			response => {
    
    
				console.log('联系服务器成功了');
				return response.json()
			},
			error => {
    
    
				console.log('联系服务器失败了',error);
				return new Promise(()=>{
    
    })//返回一个初始化的实例就不会往下走了
			}
		).then(
			response => {
    
    console.log('获取数据成功了',response);},
			error => {
    
    console.log('获取数据失败了',error);}
) 

----------------------------- 第一次优化:最后统一处理错误 ------------------------------------------------------
fetch(`/api1/search/users2?q=${
      
      keyWord}`).then(
    response => {
    
    
        console.log('联系服务器成功了');
        return response.json()
    },
).then(
    response => {
    
     console.log('获取数据成功了', response); },
).catch(
    error => {
    
     console.log('获取数据失败了', error); }
)
----------------------------- 再优化后:使用async+await ---------------------------------------------------------
try {
    
    
		const response= await fetch(`/api1/search/users2?q=${
      
      keyWord}`)
		const data = await response.json()
		console.log(data);
		} catch (error) {
    
    
		onsole.log('请求出错',error);
		}
}

猜你喜欢

转载自blog.csdn.net/m0_55644132/article/details/127774075