react学习笔记2——基于React脚手架与ajax

使用create-react-app创建react应用

react脚手架

  1. xxx脚手架: 用来帮助程序员快速创建一个基于xxx库的模板项目
    1. 包含了所有需要的配置(语法检查、jsx编译、devServer
    2. 下载好了所有相关的依赖
    3. 可以直接运行一个简单效果
  2. react提供了一个用于创建react项目的脚手架库: create-react-app
  3. 项目的整体技术架构为:  react + webpack + es6 + eslint
  4. 使用脚手架开发的项目的特点: 模块化, 组件化, 工程化

创建项目并启动

第一步,全局安装:npm i -g create-react-app

第二步,切换到想创项目的目录,使用命令:create-react-app hello-react

第三步,进入项目文件夹:cd hello-react

第四步,启动项目:npm start

react脚手架项目结构

public ---- 静态资源文件夹

        favicon.icon ------ 网站页签图标

        index.html -------- 主页面

        logo192.png ------- logo图

        logo512.png ------- logo图

        manifest.json ----- 应用加壳的配置文件

        robots.txt -------- 爬虫协议文件

src ---- 源码文件夹

        App.css -------- App组件的样式

        App.js --------- App组件

        App.test.js ---- 用于给App做测试

        index.css ------ 样式

        index.js ------- 入口文件

        logo.svg ------- logo图

        reportWebVitals.js

                --- 页面性能分析文件(需要web-vitals库的支持)

        setupTests.js

                        ---- 组件单元测试的文件(需要jest-dom库的支持)

功能界面的组件化编码流程(通用)

1. 拆分组件: 拆分界面,抽取组件

2. 实现静态组件: 使用组件实现静态页面效果

3. 实现动态组件

3.1 动态显示初始化数据

3.1.1 数据类型

3.1.2 数据名称

3.1.2 保存在哪个组件?

3.2 交互(从绑定事件监听开始)

组件的组合使用-TodoList

功能: 组件化实现此功能

  1. 显示所有todo列表

  2. 输入文本, 点击按钮显示到列表的首位, 并清除输入的文本

相关代码:

App.jsx

import React, { Component } from "react";
import Header from "./components/Header";
import List from "./components/List";
import Footer from "./components/Footer";
import "./App.css";
export default class App extends Component {
  //状态在哪里,操作状态的方法就在哪里
  //初始化数据
  state = {todos:[
    {id:'001',name:'吃饭',done:true},
    {id:'002',name:'睡觉',done:true},
    {id:'003',name:'打代码',done:false},
    {id:'004',name:'逛街',done:true},
  ]}
  //addTodo用于添加一个todo,接收的参数是一个todo对象
  addTodo = (todoObj) =>{
    // console.log('App',todoObj);
    //获取原todos
    const {todos} = this.state
    //追加一个todo
    const newTodos = [todoObj,...todos]
    //更新状态
    this.setState({
      todos:newTodos
    })
  }
  //updateTodo用于更新一个todo,接收的参数是todo对象
  updateTodo = (id,done) =>{
    //获取状态中的todos
    const {todos} = this.state
    //加工数据,匹配处理数据
    const newTodos = todos.map((todoObj)=>{
      if(todoObj.id === id) return {...todoObj,done}
      else return todoObj
    })
    this.setState({
      todos:newTodos
    })
  }
  //deleteTodo用于删除一个todo对象
  deleteTodo = (id) =>{
    //获取原来的todos
    const {todos} = this.state
    //删除指定的id的todo对象
    const newTodos = todos.filter((todoObj)=>{
      return todoObj.id !==id
    })
    this.setState({
      todos:newTodos
    })
  }
  //checkAllTodo用于全选
  checkAllTodo = (done) =>{
    //获取去原来的todos
    const {todos} = this.state
    //加工数据
    const newTodos = todos.map((todoObj)=>{
      return {...todoObj,done}
    })
    //更新状态
    this.setState({
      todos: newTodos
    })
  }
  //clearAllDone用于清除所有已完成的
  clearAllDone = () =>{
    //获取原来的todos
    const {todos} = this.state
    //过滤数据
    const newTodos = todos.filter((todoObj)=>{
      return !todoObj.done
    })
    //更新状态
    this.setState({
      todos:newTodos
    })
  }
  render() {
    const {todos} = this.state
    return (
      
{/* 通过props将函数传递给子组件,子组件在适当的时机调用这个函数,并将参数交给App,然后App将这个参数放入自己的状态里,就会引起App状态的更改,然后调用App里面的render,由于List是App的子组件,就会引发子组件的重新渲染 */}
); } }

App.css

/*base*/
body {
  background: #fff;
}

.btn {
  display: inline-block;
  padding: 4px 12px;
  margin-bottom: 0;
  font-size: 14px;
  line-height: 20px;
  text-align: center;
  vertical-align: middle;
  cursor: pointer;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
  border-radius: 4px;
}

.btn-danger {
  color: #fff;
  background-color: #da4f49;
  border: 1px solid #bd362f;
}

.btn-danger:hover {
  color: #fff;
  background-color: #bd362f;
}

.btn:focus {
  outline: none;
}

.todo-container {
  width: 600px;
  margin: 0 auto;
}
.todo-container .todo-wrap {
  padding: 10px;
  border: 1px solid #ddd;
  border-radius: 5px;
}

Header文件

index.jsx

import React, { Component } from 'react'
import PropTypes from 'prop-types'
import {nanoid} from 'nanoid'
import './index.css'
export default class index extends Component {
  //对接收的props进行类型和必要性的限制
  static propTypes = {
    addTodo: PropTypes.func.isRequired
  }
  //键盘事件的回调
  handleKeyUp = (event) =>{
    //解构赋值获取keyCode,target
    const {keyCode,target} = event
    //判断是否是回车按键
    if(keyCode!=13) return
    //添加的todo名字不能为空
    if(target.value.trim() === ''){
      alert('输入不能为空')
      return
    }
    console.log(target.value);
    //准备好一个todo对象
    const todoObj = {id:nanoid(),name:target.value,done:false}
    //将todoObj传递给App
    this.props.addTodo(todoObj)
    //清空输入
    target.value = ''
  }
  render() {
    return (
      
) } }

index.css

/*header*/
.todo-header input {
  width: 560px;
  height: 28px;
  font-size: 14px;
  border: 1px solid #ccc;
  border-radius: 4px;
  padding: 4px 7px;
}

.todo-header input:focus {
  outline: none;
  border-color: rgba(82, 168, 236, 0.8);
  box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
}

List文件

index.jsx

import React, { Component } from 'react'
import PropTypes from 'prop-types'
import Item from '../Item'
import './index.css'
export default class index extends Component {
  //对接收的props进行类型和必要性的限制
  static propTypes = {
    todos: PropTypes.array.isRequired,
    updateTodo: PropTypes.func.isRequired,
    deleteTodo: PropTypes.func.isRequired,
  }
  render() {
    const {todos,updateTodo,deleteTodo} = this.props
    return (
      
    { todos.map(todo=>{ // return return }) }
) } }

index.css

/*main*/
.todo-main {
  margin-left: 0px;
  border: 1px solid #ddd;
  border-radius: 2px;
  padding: 0px;
}

.todo-empty {
  height: 40px;
  line-height: 40px;
  border: 1px solid #ddd;
  border-radius: 2px;
  padding-left: 5px;
  margin-top: 10px;
}

Item文件

index.jsx

import React, { Component } from 'react'
import './index.css'
export default class index extends Component {
  state = {mouse:false} //标识鼠标移入、移出
  //标识鼠标移入、移出的回调
  handleMouse=(flag)=>{
    return ()=>{
      this.setState({
        mouse:flag
      })
    }
  }
  //勾选、取消勾选某一个todo的回调
  handleCheck = (id) =>{
    return (event)=>{
      console.log(id,event.target.checked);
      this.props.updateTodo(id,event.target.checked)
    }
  }
  //删除一个todo的回调
  handleDelete = (id) =>{
    console.log('通知App删除',id);
    if(window.confirm('确定删除吗?')){
      this.props.deleteTodo(id)
    }
  }
  render() {
    const {id,name,done} = this.props
    const {mouse} = this.state
    return (
      
  • ) } }

     index.css

    /*item*/
    li {
      list-style: none;
      height: 36px;
      line-height: 36px;
      padding: 0 5px;
      border-bottom: 1px solid #ddd;
    }
    
    li label {
      float: left;
      cursor: pointer;
    }
    
    li label li input {
      vertical-align: middle;
      margin-right: 6px;
      position: relative;
      top: -1px;
    }
    
    li button {
      float: right;
      display: none;
      margin-top: 3px;
    }
    
    li:before {
      content: initial;
    }
    
    li:last-child {
      border-bottom: none;
    }

     Footer文件

    index.jsx

    import React, { Component } from 'react'
    import './index.css'
    export default class index extends Component {
      //全选checkbox的回调
      handleCheckedAll=(event)=>{
        this.props.checkAllTodo(event.target.checked)
      }
      //清除已完成任务的回调
      handleClearAllDone = () => {
        this.props.clearAllDone()
      }
      render() {
        const {todos} = this.props
        //已完成的个数
        //pre 上一次的返回值,0 初始值,current当前的todo对象
        //第一次调用的时候没有上一次,所以pre初始是0
        const doneCount = todos.reduce((pre,todo)=>pre + (todo.done ? 1 : 0),0)
        console.log(doneCount);
        //总数
        const total = todos.length
        return (
          
    已完成{doneCount} / 全部{total}
    ) } }

    index.css

    /*footer*/
    .todo-footer {
      height: 40px;
      line-height: 40px;
      padding-left: 6px;
      margin-top: 5px;
    }
    
    .todo-footer label {
      display: inline-block;
      margin-right: 20px;
      cursor: pointer;
    }
    
    .todo-footer label input {
      position: relative;
      top: -1px;
      vertical-align: middle;
      margin-right: 5px;
    }
    
    .todo-footer button {
      float: right;
      margin-top: 5px;
    }
    

    React ajax

    理解

    前置说明

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

    常用的ajax请求库

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

    App.jsx

    import React, { Component } from 'react'
    import axios from 'axios'
    
    export default class App extends Component {
      getStudentData = () =>{
        axios.get('http://localhost:3000/api1/students').then(
          response=>{console.log('成功了',response.data);},
          error=>{console.log('失败了',error);}
        )
      }
      geCarData = () =>{
        axios.get('http://localhost:3000/api2/cars').then(
          response=>{console.log('成功了',response.data);},
          error=>{console.log('失败了',error);}
        )
      }
      render() {
        return (
          
    ) } }

     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": "" }, //重写请求路径(必须)
    //     }),
    //     proxy("/api2", {
    //       target: "http://localhost:5001",
    //       changeOrigin: true,
    //       pathRewrite: { "^/api2": "" },
    //     })
    //   );
    // };
    const { createProxyMiddleware } = require("http-proxy-middleware");
    
    module.exports = function (app) {
      app.use(
        "/api1",
        createProxyMiddleware({
          target: "http://localhost:5000",
          changeOrigin: true,
          pathRewrite: { "^/api1": "" },
        })
      );
      app.use(
        "/api2",
        createProxyMiddleware({
          target: "http://localhost:5001",
          changeOrigin: true,
          pathRewrite: { "^/api2": "" },
        })
      );
    };
    

     笔记:

    # react 脚手架配置代理总结
    
    ## 方法一
    
    > 在 package.json 中追加如下配置
    
    ```json
    "proxy":"http://localhost:5000"
    ```
    
    说明:
    
    1. 优点:配置简单,前端请求资源时可以不加任何前缀。
    2. 缺点:不能配置多个代理。
    3. 工作方式:上述方式配置代理,当请求了 3000 不存在的资源时(先去本地路径获取),那么该请求会转发给 5000 (优先匹配前端资源)
    
    ## 方法二
    
    1. 第一步:创建代理配置文件
    
       ```
       在src下创建配置文件:src/setupProxy.js
       ```
    
    2. 编写 setupProxy.js 配置具体代理规则:
    
       ```js
       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
             	changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:3000
             	changeOrigin默认值为false,但我们一般将changeOrigin值设为true
             */
             pathRewrite: { "^/api1": "" }, //去除请求前缀,保证交给后台服务器的是正常请求地址(必须配置)
           }),
           proxy("/api2", {
             target: "http://localhost:5001",
             changeOrigin: true,
             pathRewrite: { "^/api2": "" },
           })
         );
       };
       ```
    
    说明:
    
    1. 优点:可以配置多个代理,可以灵活的控制请求是否走代理。
    2. 缺点:配置繁琐,前端请求资源时必须加前缀。
    

    react学习笔记2——基于React脚手架与ajax_第1张图片

    axios

    文档

    GitHub - axios/axios: Promise based HTTP client for the browser and node.js

    相关API

    1.GET请求

    axios.get('/user?ID=12345')
      .then(function (response) {
        console.log(response.data);
      })
      .catch(function (error) {
        console.log(error);
      });
    
    axios.get('/user', {
        params: {
          ID: 12345
        }
      })
      .then(function (response) {
        console.log(response);
      })
      .catch(function (error) {
        console.log(error);
      });

    2.POST请求

    axios.post('/user', {
      firstName: 'Fred',
      lastName: 'Flintstone'
    })
    .then(function (response) {
    console.log(response);
    })
    .catch(function (error) {
    console.log(error);
    });

    案例—github用户搜索

    请求地址: https://api.github.com/search/users?q=xxxxxx

    代码:

    github搜索案例_axios:

    App.jsx

    import React, { Component } from 'react'
    import Search from './components/Search'
    import List from './components/List'
    import './App.css'
    export default class App extends Component {
      state = { //初始化状态
        users: [], //users初始值为数组
        isFirst: true, //是否为第一次打开页面
        isLoading: false, //标识是否处于加载中
        err: '',//存储请求相关的错误信息
      }
      //更新App的state
      updateAppState = (stateObj) =>{
        this.setState(stateObj)
      }
      render() {
        return (
          
    ) } }

     setupProxy.js

    //这种写法会导致localhost拒绝访问的问题
    // const proxy = require("http-proxy-middleware");
    // module.exports = function (app) {
    //   app.use(
    //     proxy("/api1", {
    //       //遇见/api1前缀的请求,就会触发该代理配置
    //       target: "http://localhost:5000", //请求转发给谁
    //       changeOrigin: true, //控制服务器收到的请求头中Host字段的值
    //       pathRewrite: { "^/api1": "" }, //重写请求路径(必须)
    //     })
    //   );
    // };
    const { createProxyMiddleware } = require("http-proxy-middleware");
    
    module.exports = function (app) {
      app.use(
        "/api1",
        createProxyMiddleware({
          target: "http://localhost:5000",
          changeOrigin: true,
          pathRewrite: { "^/api1": "" },
        })
      );
    };
    

     components

    Search/index.jsx:

    import React, { Component } from 'react'
    import axios from 'axios'
    
    export default class index extends Component {
      search = () =>{
        //获取用户输入(连续解构赋值+重命名)
        // console.log(this.keyWorldElement.value);
        //keyWorldElement没有被定义,{value:keyword}解构出来之后可以重新赋值
        const {keyWorldElement:{value:keyword}} = this
        console.log(keyword);
        //发送请求前通知App要更新状态
        this.props.updateAppState({isFirst:false,isLoading:true});
        //发送网络请求  后端解决跨域:cors
        // http://localhost:3000可以省略
        axios.get(`/api1/search/users?q=${keyword}`).then(
          response=>{
            // console.log('成功了', response.data);
            //请求成功后通知App更新状态
            this.props.updateAppState({isLoading:false,users:response.data.items})
          },
          error=>{
            console.log('失败了', error);
            //请求失败后通知App更新状态
            this.props.updateAppState({isLoading:false,err:error.message})
          }
        )
      }
      render() {
        return (
          

    搜索Github用户

    this.keyWorldElement = c} type="text" placeholder="输入关键词点击搜索"/> 
    ) } }

     List/index.jsx

    import React, { Component } from 'react'
    import './index.css'
    export default class index extends Component {
      render() {
        const {users,isFirst,isLoading,err} = this.props
        return (
          
    { isFirst ?

    欢迎使用,输入关键字,随后点击搜索

    : isLoading ?

    Loading...

    : err ?

    {err}

    : users.map((userObj)=>{ return (
    head_portrait

    {userObj.login}

    ) }) }
    ) } }

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

    消息订阅-发布机制

    1. 工具库: PubSubJS
    2. 下载: npm install pubsub-js --save
    3. 使用:

                    1.import PubSub from 'pubsub-js' //引入

                    2.PubSub.subscribe('delete', function(data){ }); //订阅

                    3.PubSub.publish('delete', data) //发布消息

    react学习笔记2——基于React脚手架与ajax_第2张图片

     github搜索案例_pubsub:

    App.jsx

    import React, { Component } from 'react'
    import Search from './components/Search'
    import List from './components/List'
    import './App.css'
    export default class App extends Component {
    
      //更新App的state
      updateAppState = (stateObj) =>{
        this.setState(stateObj)
      }
      render() {
        return (
          
    ) } }

    Search/index.jsx

    import React, { Component } from 'react'
    import axios from 'axios'
    import PubSub from 'pubsub-js'
    
    export default class index extends Component {
      search = () =>{
        //发布的消息,就像是经常需要改变的数据,而其他组件需要根据这个会动态改变的数据来进行动态展示
        console.log('Search组件发布消息了')
        //获取用户输入(连续解构赋值+}重命名)
        console.log(this.keyWorldElement.value);
        //keyWorldElement没有被定义,{value:keyword}解构出来之后可以重新赋值
        const {keyWorldElement:{value:keyword}} = this
        console.log(keyword);
        //发送请求前通知List要更新状态
        //谁发送数据就在什么地方发送消息
        PubSub.publish('atguigu',{isFirst:false,isLoading:true})
        //发送网络请求  后端解决跨域:cors
        axios.get(`/api1/search/users?q=${keyword}`).then(
          response=>{
            console.log('成功了', response.data);
            //请求成功后通知List更新状态
            PubSub.publish('atguigu',{isLoading:false,users:response.data.items})
          },
          error=>{
            console.log('失败了', error);
            //请求失败后通知App更新状态
            PubSub.publish('atguigu',{isLoading:false,err:error.message})
          }
        )
      }
      render() {
        return (
          

    搜索Github用户

    this.keyWorldElement = c} type="text" placeholder="输入关键词点击搜索"/> 
    ) } }

    List/index.jsx

    import React, { Component } from 'react'
    import PubSub from 'pubsub-js'
    import './index.css'
    export default class index extends Component {
      state = { //初始化状态
        users: [], //users初始值为数组
        isFirst: true, //是否为第一次打开页面
        isLoading: false, //标识是否处于加载中
        err: '',//存储请求相关的错误信息
      }
      componentDidMount(){
        //谁接收数据就在什么地方订阅消息
        //只要写了这个订阅消息之后,一旦有人发布这个消息就会收到数据
        this.token = PubSub.subscribe('atguigu',(_, stateObj)=>{
          // console.log('List组件收到数据了',stateObj);
          this.setState(stateObj)
        })
      }
      componentWillUnmount(){
        PubSub.unsubscribe(this.token)
      }
      render() {
        const {users,isFirst,isLoading,err} = this.state
        return (
          
    { isFirst ?

    欢迎使用,输入关键字,随后点击搜索

    : isLoading ?

    Loading...

    : err ?

    {err}

    : users.map((userObj)=>{ return (
    head_portrait

    {userObj.login}

    ) }) }
    ) } }

    扩展:Fetch 

    文档

    1. https://github.github.io/fetch/

         2.https://segmentfault.com/a/1190000003810652

     特点

    1. fetch: 原生函数,不再使用XmlHttpRequest对象提交ajax请求
    2. 老版本浏览器可能不支持

    相关API

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

     2.POST请求

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

    github搜索案例_fetch:

    App.jsx

    import React, { Component } from 'react'
    import Search from './components/Search'
    import List from './components/List'
    import './App.css'
    export default class App extends Component {
    
      //更新App的state
      updateAppState = (stateObj) =>{
        this.setState(stateObj)
      }
      render() {
        return (
          
    ) } }

    Search/index.jsx

    import React, { Component } from "react";
    // import axios from 'axios'
    import PubSub from "pubsub-js";
    
    export default class index extends Component {
      search = async () => {
        //发布的消息,就像是经常需要改变的数据,而其他组件需要根据这个会动态改变的数据来进行动态展示
        console.log("Search组件发布消息了");
        //获取用户输入(连续解构赋值+}重命名)
        console.log(this.keyWorldElement.value);
        //keyWorldElement没有被定义,{value:keyword}解构出来之后可以重新赋值
        const {
          keyWorldElement: { value: keyword },
        } = this;
        console.log(keyword);
        //发送请求前通知List要更新状态
        //谁发送数据就在什么地方发送消息
        PubSub.publish("atguigu", { isFirst: false, isLoading: true });
        // #region 发送网络请求————使用axios发送
        //发送网络请求  后端解决跨域:cors
        // axios.get(`/api1/search/users2?q=${keyword}`).then(
        //   response=>{
        //     console.log('成功了', response.data);
        //     //请求成功后通知List更新状态
        //     PubSub.publish('atguigu',{isLoading:false,users:response.data.items})
        //   },
        //   error=>{
        //     console.log('失败了', error);
        //     //请求失败后通知App更新状态
        //     PubSub.publish('atguigu',{isLoading:false,err:error.message})
        //   }
        // )
        //#endregion
        //发送网络请求————使用fetch发送(未优化)
        // fetch(`/api1/search/users2?q=${keyword}`).then(
        //   //能得到状态码是因为服务器告诉的,所以不管路径是否错误,只要服务器告诉状态码了,最后都是连接服务器成功了
        //   //成功请求后,获取数据成功后,这个调用response上面的json()方法,得到一个promise实例
        //   //如果联系服务器成功了,获取数据营业成功了,那么这个promise的状态也就变成了"fulfilled",而且里面保存着需要的数据
        //   //如果连接服务器成功了,但是获取数据失败了,那么这个promise的状态也就是失败的状态,里面保存着失败的原因
        //   response => {
        //     console.log('联系服务器成功了');
        //     return response.json()
        //   },
        //   //断网的时候,出现错误
        //   error=>{
        //     console.log('联系服务器失败了',error);
        //     如果服务器都没有连接上,最好不好展示获取数据是成功还是失败,而是直接中断promise链
        //     return一个初始化状态的promise
        //     return new Promise(()=>{})
        //   }
        // ).then(
        //   response=>{
        //     console.log('获取数据成功了', response);
        //   },
        //   error=>{
        //     console.log('获取数据失败了',error);
        //   }
        // )
        //发送网络请求————使用fetch发送(优化)
        // fetch(`/api1/search/users2?q=${keyword}`).then(
        //     response => {
        //     console.log('联系服务器成功了');
        //     return response.json()
        //   },
        // ).then(
        //   response=>{
        //     console.log('获取数据成功了', response);
        //   },
        // ).catch(
        //   统一处理错误
        //   error=>{console.log('请求出错', error);}
        // )
        try {
          const response = await fetch(`/api1/search/users2?q=${keyword}`);
          const data = await response.json();
          PubSub.publish('atguigu',{isLoading:false,users:data.items})
        } catch (error) {
          console.log("请求出错", error);
          PubSub.publish('atguigu',{isLoading:false,users:error.message})
        }
      };
      render() {
        return (
          

    搜索Github用户

    (this.keyWorldElement = c)} type="text" placeholder="输入关键词点击搜索" />  
    ); } } // const xhr = new XMLHttpRequest() // xhr.open() // xhr.send() // ... // jQuery将这些方法封装好了 // $(_,_)就可以直接发送请求 // 有一个问题,可能会产生回调地狱,jQuery发送ajax请求都靠回调函数来进行沟通 //成功就调用成功的回调,失败调用失败的回调 //如果是这样的需求,第一次成功了,在发第二次,第二次成了,再发第三次,第三次成了,再发第四次....很容易形成回调地域 //axios回调地域能解决,是promise风格的 // jQuery与axios都是对xhr的封装,他们都需要下载引入,都是第三方的对xhr的封装 // fetch也能发送请求,不用xhr,不是一个库,内置就有,window上面就有,fetch与xhr是并列的, // 本身就是promise风格的 //优势:不是第三方库,不需要下载,直接使用,有浏览器就能使用

     List/index.jsx

    import React, { Component } from 'react'
    import PubSub from 'pubsub-js'
    import './index.css'
    export default class index extends Component {
      state = { //初始化状态
        users: [], //users初始值为数组
        isFirst: true, //是否为第一次打开页面
        isLoading: false, //标识是否处于加载中
        err: '',//存储请求相关的错误信息
      }
      componentDidMount(){
        //谁接收数据就在什么地方订阅消息
        //只要写了这个订阅消息之后,一旦有人发布这个消息就会收到数据
        this.token = PubSub.subscribe('atguigu',(_, stateObj)=>{
          // console.log('List组件收到数据了',stateObj);
          this.setState(stateObj)
        })
      }
      componentWillUnmount(){
        PubSub.unsubscribe(this.token)
      }
      render() {
        const {users,isFirst,isLoading,err} = this.state
        return (
          
    { isFirst ?

    欢迎使用,输入关键字,随后点击搜索

    : isLoading ?

    Loading...

    : err ?

    {err}

    : users.map((userObj)=>{ return (
    head_portrait

    {userObj.login}

    ) }) }
    ) } }

    你可能感兴趣的:(react.js,学习,笔记,ajax)