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

javascript 复制代码
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 (
      <div className="todo-container">
        <div className="todo-wrap">
          {/* 通过props将函数传递给子组件,子组件在适当的时机调用这个函数,并将参数交给App,然后App将这个参数放入自己的状态里,就会引起App状态的更改,然后调用App里面的render,由于List是App的子组件,就会引发子组件的重新渲染 */}
          <Header addTodo={this.addTodo} />
          <List todos={todos} updateTodo={this.updateTodo} deleteTodo={this.deleteTodo} />
          <Footer todos={todos} checkAllTodo={this.checkAllTodo} clearAllDone={this.clearAllDone} />
        </div>
      </div>
    );
  }
}

App.css

javascript 复制代码
/*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

javascript 复制代码
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 (
      <div className="todo-header">
        <input onKeyUp={this.handleKeyUp} type="text" placeholder="请输入你的任务名称,按回车键确认" />
      </div>
    )
  }
}

index.css

javascript 复制代码
/*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

javascript 复制代码
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 (
      <ul className="todo-main">
        {
          todos.map(todo=>{
            // return <Item key={todo.id} id={todo.id} name={todo.name} done={todo.done} />
            return <Item key={todo.id} {...todo} updateTodo={updateTodo} deleteTodo={deleteTodo} />
          })
        }
      </ul>
    )
  }
}

index.css

javascript 复制代码
/*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

javascript 复制代码
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 (
      <li style={{backgroundColor: mouse ? '#ddd' : 'white'}} onMouseLeave={this.handleMouse(false)} onMouseEnter={this.handleMouse(true)}>
        <label>
          <input type="checkbox" checked={done} onChange={this.handleCheck(id)} />
          <span>{name}</span>
        </label>
        <button onClick={()=>this.handleDelete(id)} className="btn btn-danger" style={{ display: mouse ? 'block' : "none" }}>
          删除
        </button>
      </li>
    )
  }
}

index.css

javascript 复制代码
/*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

javascript 复制代码
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 (
      <div className="todo-footer">
        <label>
          {/* defaultChecked只在第一次生效 */}
          <input type="checkbox" onChange={this.handleCheckedAll} checked={doneCount === total && total!==0 ? true : false} />
        </label>
        <span>
          <span>已完成{doneCount}</span> / 全部{total}
        </span>
        <button onClick={this.handleClearAllDone} className="btn btn-danger">清除已完成任务</button>
      </div>
    )
  }
}

index.css

javascript 复制代码
/*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.
    1. 封装XmlHttpRequest对象的ajax
    2. promise风格
    3. 可以用在浏览器端和node服务器端

App.jsx

javascript 复制代码
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 (
      <div>
        <button onClick={this.getStudentData}>点我获取学生数据</button>
        <button onClick={this.geCarData}>点我获取汽车数据</button>
      </div>
    )
  }
}

setupProxy.js

javascript 复制代码
// 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": "" },
    })
  );
};

笔记:

javascript 复制代码
# 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. 缺点:配置繁琐,前端请求资源时必须加前缀。

axios

文档

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

相关 API

1.GET请求

javascript 复制代码
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请求

javascript 复制代码
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

javascript 复制代码
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 (
      <div className="container">
        <Search updateAppState={this.updateAppState}/>
        <List {...this.state}/>
      </div>
    )
  }
}

setupProxy.js

javascript 复制代码
//这种写法会导致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:

javascript 复制代码
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 (
      <section className="jumbotron">
        <h3 className="jumbotron-heading">搜索Github用户</h3>
        <div>
          <input ref={c => this.keyWorldElement = c} type="text" placeholder="输入关键词点击搜索"/>&nbsp;
          <button onClick={this.search}>搜索</button>
        </div>
      </section>
    )
  }
}

List/index.jsx

javascript 复制代码
import React, { Component } from 'react'
import './index.css'
export default class index 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="https://github.com/reactjs" target="_blank">
                  <img alt="head_portrait" src={userObj.avatar_url} style={{width: '100px'}}/>
                </a>
                <p className="card-text">{userObj.login}</p>
              </div>
            )
          })
        }
      </div>
    )
  }
}

List/index.css

javascript 复制代码
.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) //发布消息

github搜索案例_pubsub:

App.jsx

javascript 复制代码
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 (
      <div className="container">
        <Search updateAppState={this.updateAppState}/>
        <List {...this.state}/>
      </div>
    )
  }
}

Search/index.jsx

javascript 复制代码
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 (
      <section className="jumbotron">
        <h3 className="jumbotron-heading">搜索Github用户</h3>
        <div>
          <input ref={c => this.keyWorldElement = c} type="text" placeholder="输入关键词点击搜索"/>&nbsp;
          <button onClick={this.search}>搜索</button>
        </div>
      </section>
    )
  }
}

List/index.jsx

javascript 复制代码
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 (
      <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="https://github.com/reactjs" target="_blank">
                  <img alt="head_portrait" src={userObj.avatar_url} style={{width: '100px'}}/>
                </a>
                <p className="card-text">{userObj.login}</p>
              </div>
            )
          })
        }
      </div>
    )
  }
}

扩展:Fetch

文档

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

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

特点

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

相关API

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

2.POST请求

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

github搜索案例_fetch:

App.jsx

javascript 复制代码
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 (
      <div className="container">
        <Search updateAppState={this.updateAppState}/>
        <List {...this.state}/>
      </div>
    )
  }
}

Search/index.jsx

javascript 复制代码
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 (
      <section className="jumbotron">
        <h3 className="jumbotron-heading">搜索Github用户</h3>
        <div>
          <input
            ref={(c) => (this.keyWorldElement = c)}
            type="text"
            placeholder="输入关键词点击搜索"
          />
          &nbsp;
          <button onClick={this.search}>搜索</button>
        </div>
      </section>
    );
  }
}

// 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

javascript 复制代码
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 (
      <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="https://github.com/reactjs" target="_blank">
                  <img alt="head_portrait" src={userObj.avatar_url} style={{width: '100px'}}/>
                </a>
                <p className="card-text">{userObj.login}</p>
              </div>
            )
          })
        }
      </div>
    )
  }
}
相关推荐
灏瀚星空1 小时前
量化交易之数学与统计学基础2.2——线性代数与矩阵运算 | 特征值与特征向量
笔记·python·学习·数学建模·金融
吴永琦(桂林电子科技大学)1 小时前
Node.js心得笔记
笔记·node.js
李匠20241 小时前
C++负载均衡远程调用学习之自定义内存池管理
c++·学习
哟哟耶耶2 小时前
react-10样式模块化(./index.module.css, <div className={welcome.title}>Welcome</div>)
前端·javascript·react.js
虾球xz2 小时前
游戏引擎学习第252天:允许编辑调试值
c++·学习·游戏引擎
jackson凌3 小时前
【Java学习笔记】递归
java·笔记·学习
DisonTangor3 小时前
Meta 推出 WebSSL 模型:探索 AI 无语言视觉学习,纯图训练媲美 OpenAI CLIP
人工智能·学习·计算机视觉
s_little_monster4 小时前
【Linux】线程池和线程补充内容
linux·运维·服务器·c++·笔记·学习·学习方法
Timmer丿5 小时前
kafka学习笔记(四、生产者(客户端)深入研究(二)——消费者协调器与_consumer_offsets剖析)
笔记·学习·kafka
苦夏木禾5 小时前
关于react19版本更新后部分组件无法正常使用的问题
javascript·react.js