React学习(四)----- 基于React脚手架的React应用

1、React脚手架

  • 什么是脚手架???

用来帮助程序员快速创建一个基于React库的模板项目

    1) 包含了所有需要的配置(语法检查、jsx编译、devServer…)

    2) 下载好了所有相关的依赖

    3) 可以直接运行一个简单效果

    4) 项目的整体技术架构为:  react + webpack + es6 + eslint

    5) 使用脚手架开发项目的特点: 模块化, 组件化, 工程化

  • 脚手架的安装
    cnpm  install  create-react-app  -g
  • 创建项目
    1、创建项目
         create-react-app hello-react
    
    2、进入项目目录
         cd hello-react
    
    3、启动项目
         npm start
  • React脚手架项目结构
    • public ----- 静态资源文件夹
      • favicon.ico ----- 网站页签图标
      • index.html ----- 主页面
        <!DOCTYPE html>
        <html lang="en">
          <head>
            <meta charset="utf-8" />
            <!-- %PUBLIC_URL%代表public文件夹的路径 相当于./favicon.ico -->
            <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
            <!-- 开启理想视口,用于做移动端网页的适配 -->
            <meta name="viewport" content="width=device-width, initial-scale=1" />
            <!-- 用于配置浏览器页签 + 地址栏的颜色 ,只针对安卓手机浏览器(兼容性不好,有些机型无效果),不支持ios-->
            <meta name="theme-color" content="#000000" />
            <meta
              name="description"
              content="Web site created using create-react-app"
            />
            <!-- 只支持ios手机:用于指定网页添加到手机主屏幕后的图标 -->
            <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
            <!-- 应用加壳的配置文件 -->
            <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
            <title>React App</title>
          </head>
          <body>
            <!-- 当浏览器不支持js时展示的提示信息 -->
            <noscript>You need to enable JavaScript to run this app.</noscript>
            <!-- 容器,挂载虚拟DOM的地方,也就是ReactDOM.render方法的第二个参数 -->
            <div id="root"></div>
          </body>
        </html>
      • robots.txt ----- 爬虫协议文件
    • src ----- 源码文件夹
      • App.css ----- App组件的样式
      • App.js ----- App组件
      • App.test.js ----- 用于给App做测试
      • index.css ----- 样式
      • index.js ----- 入口文件:React.StrictMode 用于检查App和App的子组件的写法是否合理
      • logo.svg ----- logo图
      • reportWebVitals.js ----- 页面性能分析文件(需要web-vitals库的支持)
      • setupTests.js ----- 组件单元测试的文件(主要jest-dom库的支持)

2、一个简单的Hello组件

  • vscode中react插件的安装
    ES7 React/Redux/GraphQL/React-Native snippets
  • 样式的模块化
    • 组件的文件目录

                     

    • 将组件的css文件命名为:index.module.css,在组件jsx中的引入方式为用一个变量接收,并且定义的时候使用差值表达式的方式定义,这样不会造成样式污染
      import { Component } from "react";
      import hello from './index.module.css'
      
      export default class Hello extends Component {
        render() {
          return <h1 className={hello.title}>Hello,React!!!!</h1>;
        }
      }

      不然结果就是:同名的class标签只会使用后引入的css文件

                   

  •  功能界面的组件化编码流程
    1. 拆分组件: 拆分界面,抽取组件
    2. 实现静态组件: 使用组件实现静态页面效果
    3. 实现动态组件
        3.1 动态显示初始化数据
            3.1.1 数据类型
            3.1.2 数据名称
            3.1.3 保存在哪个组件?
        3.2 交互(从绑定事件监听开始)

3、组件的组合使用(TodoList)

  • 效果图

         

  • 知识点
    • 步骤

                   

    • 生成唯一的id值得库
      cnpm i uuid/nanoid -D//uuid库比较大,建议选择nanoid
    • 非父子组件之间的传值
      1、子传父、父传子  即Header传给App,App传给List
          (1)子传父
                  子组件在父组件当做标签使用
                  1) 子组件this.props.sendData(event.target.value)
                  2) 父组件中的子组件绑定数据
      <Header sendData={this.receiveData} />
                  3) 父组件中的方法
      receiveData = (data) => {
        console.log(data);
      };
          (2)父传子
                  子组件在父组件当做标签使用
                  1) 父组件中的子组件绑定数据
      <Header {...this.state}} />
                  2) 在子组件的render方法中使用this.props进行接收
    • checkbox的相关问题
      1、checked属性必须与onChange事件一起使用;
      2、defaultChecked属性只有第一次会生效,后效修改将无效;

4、React Ajax

  • 为什么要引入Ajax???
    1.React本身只关注于界面, 并不包含发送ajax请求的代码
    2.前端应用需要通过ajax请求与后台进行交互(json数据)
    3.react应用中需要集成第三方ajax库(或自己封装)
  • 常用的Ajax请求库
    1.jQuery: 比较重, 直接操作DOM,会有回调函数地域的问题,如果需要另外引入不建议使用
    2.axios: 轻量级, 建议使用
       1)封装XmlHttpRequest对象的ajax
       2)promise风格
       3)可以用在浏览器端和node服务器端
  • 脚手架配置代理(客户端解决,服务器的话使用cors)
    • 跨域原因
      http://localhost:3000 -----> http://localhost:5000/students
      原因:由于Ajax引擎的限制,请求可以从3000发送到5000端口,但是响应的时候被引擎拦截,这个时候我们可以配置代理
          (设置一个中间人服务器,也在3000端口运行,而中间服务器没有ajax引擎的影响,且服务器之间无同源策略的影响,可以正常接收响应,接收到响应数据后,由于端口都是
      3000,客户端直接收到数据)
      本地3000端口运行这一个脚手架,一个服务器,name脚手架和服务器由于协议、域名、端口号完全一致,所以不会产生跨域,而本地代理的服务器与访问服务器之间不受同源策略的影响
      因此也可以正常的接收到数据,代理服务器收到数据后再返回给客户端,因此解决跨域问题!!!
    • 第一种方式:代理一个服务器的情况下,多个不允许这么配置
      • 步骤
        1、修改package.json
        "proxy": "http://localhost:5000"
        2、重启
        npm start 
        3、修改请求接口
        http://localhost:5000/students -----> http://localhost:3000/students
      • 说明
        1. 优点:配置简单,前端请求资源时可以不加任何前缀。
        2. 缺点:不能配置多个代理。
        3. 工作方式:上述方式配置代理,当请求了3000不存在的资源时,那么该请求会转发给5000 (优先匹配前端资源)
    • 第二种方式:代理多个服务器
      • 步骤
        1. 第一步:创建代理配置文件
           在src下创建配置文件:src/setupProxy.js
        2. 编写setupProxy.js配置具体代理规则:
           // 使用commonjs的语法,引入一个内置插件  无需安装
           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. 缺点:配置繁琐,前端请求资源时必须加前缀。
  • github搜索案例
    • 效果图

                     

    • 知识点
      • 连续解构赋值
         const obj = {a:{b:{c:1}}}
         console.log(obj.a.b.c) //1
         const { a: { b: { c } } } = obj
         console.log(c) //1

5、发布-订阅机制(适用于任意组件的通信)

  • 安装
    cnpm i pubsub-js -S
  • 使用:Search组件传递给-----List组件(兄弟组件之间的传值)
    • List组件订阅消息
      import PubSub from 'pubsub-js'
      
      componentDidMount(){
        // 订阅消息
        this.token = PubSub.subscribe("updateAppState",(msg,data)=>{
          console.log("list")
          console.log(msg,data)//msg是updateAppState,即事件的名称,data是发布的消息
          this.setState(data) 
        }) 
      } 
      //取消订阅
      componentWillUnmount(){
        PubSub.unsubscribe(this.token) 
      }
    • Search组件发布消息
      import PubSub from 'pubsub-js'
      
      PubSub.publish('updateAppState',{ isFirst: false,isLoading:true })

 6、前后端交互的方式

  • jquery/axios
    • 特点
      axios是一个基于Promise,用于浏览器和 nodejs 的 HTTP 客户端,它本身具有以下特征:
          1)从浏览器中创建 XMLHttpRequest
          2)从 node.js 发出 http 请求
          3)支持 Promise API
          4)拦截请求和响应
          5)转换请求和响应数据
          6)自动转换JSON数据, 
          7)客户端支持防止CSRF/XSRF
    • 示例
      import axios from 'axios'
      
      axios.get(`http://localhost:3000/api/search/users?q=${inputValue}`).then((res) => {
        // 请求成功后通知App更新状态
        updateAppState({isLoading:false,users:res.data.items})
      }).catch(err => {
        updateAppState({isLoading:false,err:err.message})
      })
  • fetch ----- 无需安装,浏览器内置
    • 特点
      fetch:返回的是一个未处理的方法集合,我们可以通过这些方法得到我们想要的数据类型。如果我们想要json格式,就执行res.json(),如果我们想要字符串就res.text()
          1)关注分离,没有将输入、输出和用事件来跟踪的状态混杂在一个对象里
          2)更加底层,提供的API丰富(request, response)
          3)脱离了XHR,是ES规范里新的实现方式
          4)fetch只对网络请求报错,对400,500都当做成功的请求,需要封装去处理
          5)fetch默认不会带cookie,需要添加配置项
          6)fetch不支持abort,不支持超时控制,使用setTimeout及Promise.reject的实现的超时控制并不能阻止请求过程继续在后台运行,造成了量的浪费
          7)fetch没有办法原生监测请求的进度,而XHR可以
    • 请求格式
      默认是get请求,如果需要向后端发送数据,则直接在地址后面做拼接
      当请求post方式时:
      fetch(url,
          {
                method:"post",
                body: JSON.stringify(obj),
                headers : {//必须要写
                     "Content-type" : "application/json";
                     "credentials": 'include'//携带cookie进行提交
                }
         }).then((res)=>res.json()).then((data)=>{})
    • 示例:默认为get请求
        fetch(`http://localhost:3000/api/search/users?q=${inputValue}`)
            .then(
                (res) => {
                    console.log('联系服务器成功了', res)//是一个综合各种方法的对象,并不是请求的数据
                    return res.json()
                },
                (error) => {
                  console.log('联系服务器失败了', error)
                  return new Promise(()=>{})//为了防止断网走了上一行后,接着走下一个then中的第一个回调,加上只走上面一行
              })
            .then(
              (res) => {
                console.log("获取数据成功了",res)
              },
              (error) => {
               console.log('获取数据失败了',error)
              })
    • 简化代码后
      fetch(`http://localhost:3000/api/search/users?q=${inputValue}`)
            .then(
                (res) => {
                    console.log('联系服务器成功了', res)//是一个综合各种方法的对象,并不是请求的数据
                    return res.json()
                }
            )
            .then(
              (res) => {
                console.log("获取数据成功了",res)
              }
          ).catch(
            (error) => {
               console.log('请求出错',error)
            }
          )
    • 使用async和await
      try {
           let response = await fetch(`http://localhost:3000/api/search/users?q=${inputValue}`)
           let result = await response.json();
           console.log(result)
       } catch (e) {
           console.log(e)
       }

7、其他问题

     暂无

 

 
posted @ 2021-09-07 10:55  北栀女孩儿  阅读(310)  评论(0编辑  收藏  举报