React

Article Directory
  1. 1. React:用于构建用户界面的JavaScript库。
    1. 1.1. 以前的基本操作
    2. 1.2. 为什么要学React
    3. 1.3. React特点
    4. 1.4. 不用组件的流程
  2. 2. Js和Jsx
    1. 2.1. Jsx语法规则
      1. 2.1.1. Jsx语法规则
  3. 3. 虚拟DOM和真实DOM
    1. 3.1. 断点debugger
  4. 4. 组件与模块
    1. 4.1. 模块
    2. 4.2. 组件
  5. 5. React面向组件编程
    1. 5.1. 函数式组件
      1. 5.1.1. 用函数式组件的流程
    2. 5.2. 类式组件
      1. 5.2.1. 复习类相关知识
    3. 5.3. 类式组件
      1. 5.3.0.1. 原型、自身实例
  6. 5.4. 简单组件和复杂组件
  7. 5.5. 组件实例的三大核心属性
    1. 5.5.1. 核心属性之一:state
    2. 5.5.2. setState
    3. 5.5.3. 类中的方法,this指向
    4. 5.5.4. 核心属性二:props
      1. 5.5.4.1. 简单用法
      2. 5.5.4.2. 批量传递props
        1. 5.5.4.2.1. 展开运算符 …
        2. 5.5.4.2.2. 批量传递props,批量传递标签属性
        3. 5.5.4.2.3. props限制属性类型,默认等
      3. 5.5.4.3. 简写
    5. 5.5.5. 构造器
    6. 5.5.6. 函数式组件:props
    7. 5.5.7. 核心属性之三:refs
      1. 5.5.7.1. 1、字符串形式ref
      2. 5.5.7.2. 2、回调形式的ref
      3. 5.5.7.3. 3、createRef
      4. 5.5.7.4. 4、ref总结
  • 6. react应用(基于react脚手架)
    1. 6.1. 创建项目并启动
    2. 6.2. 脚手架,各个文件介绍
      1. 6.2.1. html.index文件
      2. 6.2.2. App.js
      3. 6.2.3. index.js
      4. 6.2.4. 文件执行顺序
      5. 6.2.5. 一些文件注意:
    3. 6.3. 默认暴露和分别暴露
    4. 6.4. 样式模块化
      1. 6.4.1. 样式模块化:
    5. 6.5. 功能界面的组件化编码流程
  • 7. React ajax
    1. 7.1. 4.1 前置说明
    2. 7.2. 4.2 常用的ajax请求库
    3. 7.3. 脚手架配置代理(跨域)
      1. 7.3.1.
      2. 7.3.2. 2种方法配置这个中间代理。
        1. 7.3.2.1. 1、package.json中增加配置
        2. 7.3.2.2. 2、配置多个代理setupProxy.js
  • 8. 打包到生产
  • React全家桶

    官网

    https://reactjs.org/

    https://react.docschina.org/

    React:用于构建用户界面的JavaScript库。

    React是啊一个将数据渲染为HTML视图的开源JavaScript库。

    Facebook开发且开源。

    React核心就是不要自己操作DOM,让React帮忙,还有虚拟DOM,diff等。

    以前的基本操作

    1、发送请求获取数据

    2、处理数据(过滤、整理格式等)

    3、操作DOM呈现页面

    为什么要学React

    1、原生JS操作DOM繁琐、效率低(DOM-API操作UI)。

    2、使用JS直接操作DOM,浏览器会进行大量的重绘重排。

    3、原生JS,没有组件化编码方案(不是模块化,模块化就是写成a.js啥的,但是组件化其实就是结构样式都拆出来css等),代码复用率低。

    React特点

    1、采用组件化模式、声明式编码、提高开发效率以及组件复用率。

    2、在React Native中可以使用React语法进行移动端开发

    3、使用虚拟DOM+优秀的Diffing算法,尽量减少与真实DOM的交互,最小化页面重绘。

    Screenshot2023-08-19at18.51.59

    不用组件的流程

    1、创建虚拟DOM

    2、渲染虚拟DOM到页面

    Js和Jsx

    Js和Jsx都能创建虚拟DOM,然后放到容器DOM节点。

    但是Js通过React.createElement,需要嵌套创建,麻烦。

    Jsx可以直接跟h5一样写标签,还能看清层级结构,且不需要加引号。

    Jsx语法规则

    JavaScript XML,类似于XML的JS扩展语法。

    XML:早期用于存储和传输数据。后面被Json取代了。

    parse:快速把Json字符串解析成js对应的数组和对象

    stringfy:快速把Js对应的数组和对象解析成字符串

    Jsx语法规则

    在Jsx中,写的都是Jsx标签,会被转成html标签。

    1、定义虚拟DOM时,不要写引号。

    2、标签中混入JS表达式时要用{},引入css啥的不需要的。

    3、样式的类名指定不要用class,要用className。

    4、内联样式。正常的css中style,是style=”color: orange; width: 100px;”。然后在Jsx中的style,不应该写引号,而是两个{ {key: value} }花括号。外面花括号表示你要写js表达式,里面的花括号表示,你要写一个js对象

    5、虚拟DOM必须只有一个根标签,要写两个,用div标签包一下。

    6、标签必须闭合,没有标签体么,自闭合。

    7、标签首字母

    ​ (1)若小写字母开头,则将该标签转为html中同名元素。若html中无该标签对应的同名元素,则报错。

    ​ (2)若大写字母开头,react就会去渲染对应的组件(component),若组件没有定义,则报错。

    8、表达式、语句(代码)。

    ​ 在虚拟DOM中,只能写表达式
    ​ (1)表达式:一个表达式,会产生一个值,可以放在任何一个需要值的地方。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    (1) a
    (2) a+b
    (3) demo(1)
    (4)
    arrs.map((item => {
    return item
    }))
    (5)
    function test() {

    }
    // 就是用一个const x = 去接,能接到就是一个表达式

    ​ (2)语句(代码):

    1
    2
    3
    (1) if () {}
    (2) for () {}
    (3) switch () { case: xxxx }

    此处abc等等编号是视频中没有写的,自己写一下。

    a、虚拟DOM,each child in a list should have a unique key。

    1
    2
    3
    4
    5
    {
    data.map((item, index)) => {
    return <li key={index}>{iten}</li> // 注意这个key,现在用index,可能还会有一点问题的。
    }
    }

    b、给react数组,可以帮忙自动遍历,如果给他一个对象,是遍历不了的。那么我自己遍历呗,但是貌似不能用for循环。

    虚拟DOM和真实DOM

    虚拟DOM

    1、Js里面的一个普通的一般对象,Object

    2、虚拟DOM比较轻,真实DOM比较“重”,因为虚拟DOM是React内部在用,无需真实DOM那么多的属性。

    3、虚拟DOM最终会被React转化为真实DOM,呈现在页面上

    断点debugger

    真实DOM通过console.log打印出来,浏览器直接打出标签的,可以加断点,debugger。

    组件与模块

    模块

    (1):向外提供特定功能的js程序,一般就是一个js文件。

    (2):为什么要拆成膜快:随着业务逻辑增加,代码越来越多且复杂。

    (3):作用:复用js,简化js的编写,提高js运行效率。

    模块化:当应用的js都以模块来编写,这就是一个模块化的应用。

    组件

    (1)用来实现局部功能效果的的代码加资源的集合:html、css、js、img、video、font等等。

    (2)为什么:一个界面的功能更复杂。

    (3)复用编码,简化项目编码,提高运行效率。

    组件化:当应用是以多组件的方式实现,这个应用就是一个组件化的应用。

    React面向组件编程

    函数式组件

    类式组件

    组件首字母大写。

    函数式组件

    用函数式组件的流程

    1、创建函数式组件

    2、渲染组件到页面

    a、首字母必须大写。

    b、函数必须有返回值。

    c、ReactDOM.render里面必须有组件标签,不能直接写组件名字。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // 1、创建函数式组件
    function MyFunctionConponent() {
    // 此处的this是undefined,因为babel编译后开启了严格模式。use 'strict'
    console.log(this);
    return <h2>这是展示的内容</h2>
    }
    // 2、渲染组件到页面
    // 注意,下面这么些没有调用函数,react会帮我们自己调用的。
    ReactDOM.render(<MyFunctionConponent/>, document.getElementById("test")); // 后面的那个是父容器


    // a、2中执行了React解析组件标签,找到了MyFunctionConponent组件
    // b、发现组件时使用函数定义的,随后调用该函数,将返回的虚拟DOM专为真实DOM,然后呈现在页面中。

    类式组件

    复习类相关知识

    总结:

    1、类中的构造器不是必须写的,要对实例进行一些初始化的操作,如添加指定属性才行

    2、如果A类继承了B类,且A类中写了构造器,那么A类构造器中的super必须要调用。

    3、类中所定义的方法,都是放在了类的原型对象上,供实例去使用。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    // 创建一个Person类
    class Persion {
    // 构造器方法
    constructor(name, age) {
    // 此处的this指的是:类的实例对象
    this.name = name
    this.age = age
    }

    // 一般方法
    speak() {
    // speak方法放在了哪里?放在了Person这个类的原型对象上
    // speak方法供实例使用
    // 通过Person实例调用speak时,speak中的this就是Person实例。因为比如p1.speak.call 或者apply或者bind可以改变this的指向。
    console.log(`我叫${name},我的年龄是${this.age}`)
    }
    }

    // 创建一个Person的实例对象
    const p1 = new Persion('tom', 18)


    // 创建一个Student类,继承Person类
    class Student extends Person {
    // 构造器一定要调用super
    constructor(name, age, grade) {
    super(name, age)
    this.grade = grade
    }

    // 重写从父类继承过来的方法
    speak() {
    console.log(`我叫${name},我的年龄是${this.age},我的年级是$(this.grade)`)
    }

    study() {
    // study方法放在了哪里?类的原型对象上,供实例使用
    // 但是通过Student实例调用study时,study中的this就是Student实例
    console.log("我很努力的学习")
    }
    }

    const s1 = new Student("小张", 13, "高一")
    console.log(s1);
    s1.speak(); // 原型链找speak,从下往上

    类式组件

    原型、自身实例

    非常注意,原型,与,自身实例,是两个东西。可以console.log实例打印出来看。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    // 1、创建类式组件,类名就是组件名,需要继承,需要写render,render一定要有返回值
    // 可以打开React中文网,看例子等等
    class MyClassComponent extends React.Component {
    render() {
    // render方法放在了哪里?类的原型对象上MyClassComponent,供实例使用
    // 但是2中我没有显示创建呀?
    // render中的this是谁?MyClassComponent组件实例对象
    console.log(this);
    return <h2>这是显示的内容</h2>
    }
    }

    // 2、渲染组件到页面
    ReactDOM.render(<MyClassComponent/>, document.getElementById('test'))

    // 执行了ReactDOM.render后发生了什么?
    // a、2中执行了React解析组件标签,找到了MyClassComponent组件
    // b、发现组件时使用类定义的,随后new出来该类的实例,并通过该实例调用到原型上的render方法。
    // 将render返回的虚拟DOM专为真实DOM,然后呈现在页面中。

    简单组件和复杂组件

    有状态的是复杂组件

    组件实例的三大核心属性

    类组件才有,因为函数式组件根本没有实例。最新版提出了hooks,函数式组件貌似也有了。

    1、state

    2、props

    3、refs与事件处理

    核心属性之一:state

    (1)state是组件对象的最重要的属性,值是对象(可以包含多个key-value组合)

    (2)组件被称为“状态机”,通过更新组件的state来更新对应的页面显示(重新渲染组件)

    注意:

    (1)组件中的render方法中的this为组件实例对象。

    (2)组件自定义的方法中this为undefined,如何解决?

    ​ a. 强制绑定this,通过函数对象的bind()

    ​ b. 箭头函数

    (3)状态数据,不能直接修改或更新

    组件的数据存在状态里,数据的改变就会去懂页面的展示。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    class Weather extends React.Compomnent {
    constructor(props) {
    super(props)
    this.state = {isHot: true}
    }
    render() {
    return <h2>今天天气很{this.state.isHot ? "炎热" : "凉爽" }</h2>
    }
    }

    ![Screenshot2023-08-20at19.37.44](https://gitee.com/JiaChengCC/u-pic-chart-bed/raw/master/uPic/Screenshot%202023-08-20%20at 19.37.44.png)

    setState

    修改状态,不能直接把状态拿到然后赋值,需要setState。

    类中的方法,this指向

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    class Person {
    constructor(name, age) {
    this.name = name
    this .age = age
    // 否则speak的this在回调的时候是undefined。
    // 所以简写下面这个bind,因为这些类中的方法基本上都是要绑定的,不然没意思。
    this.speak = this.speak.bind{this}
    }

    speak() {
    console.log(this)
    }
    }

    const p1 = new Person('tom', 18) // 这个属于实例调用
    const x = p1.study
    x() // 这个属于函数的直接调用(本来this指向Window),但是类中所有定义的方法在局部都开启了严格模式,所以this变成了undefined。 可以修改成
    const x = Person.bind({name: 's', age: 19}) // .bind返回一个新的函数
    x()

    // 所以说,在继承Component的类中,绑定事件,onClick={this.speak},这个回调的this是undefined了,所以报错。
    // 所以在constructor初始化方法中,绑定this
    // 这么做了之后,原型上的方法得有,拿着原型上的,生成一个新的,挂在自身实例上,所以onclick后面调用的是挂在自身实例上的。 注意是生成了挂在实例自身的一个函数。
    this.speak = this.speak.bind{this}

    这是state的一开始的方式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    class Weather extends React.Component() {
    constructor(props) {
    super(props)
    this.state = {isHot: false, wind: '微风'}
    this.changeWeather.bind(this)
    }

    render() {
    const {isHot, wond} = this.state
    return <h1 onClick={this.changeWeather}>今天天气很{isHot ? '炎热' : '凉爽'}, {wind}</h1>
    }

    // 放在原型对象上,供实例使用
    changeWeather() {
    const isHot = this.state.isHot
    this.setState(isHot: !isHot)
    }
    }

    ReactDOM.render(<Weather/>, document.getElementById('test'))

    然后简写:

    1、类中可以直接写赋值语句,这样的话是写死的,固定。所以可以简写。

    所以,要用赋值语句的形式 + 箭头函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    class Weather extends React.Component() {
    constructor(props) {
    super(props)
    // this.state = {isHot: false, wind: '微风'}
    // this.changeWeather.bind(this)
    }

    // 这句话放到这里,不用在构造器里。放在实例自身上。
    state = {isHot: false, wind: '微风'}
    render() {
    const {isHot, wond} = this.state
    return <h1 onClick={this.changeWeather}>今天天气很{isHot ? '炎热' : '凉爽'}, {wind}</h1>
    }

    // 放在实例自身,但是不能用普通函数function,得用箭头函数
    // 箭头函数没有自己的this,找起外层函数的this作为自己的this使用。此处外层的this就是Weather实例自身。
    // changeWeather = function() {
    changeWeather = () => {
    const isHot = this.state.isHot
    this.setState(isHot: !isHot)
    }
    }

    ReactDOM.render(<Weather/>, document.getElementById('test'))

    这是简写:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    class Weather extends React.Component() {
    state = {isHot: false, wind: '微风'}

    render() {
    const {isHot, wond} = this.state
    return <h1 onClick={this.changeWeather}>今天天气很{isHot ? '炎热' : '凉爽'}, {wind}</h1>
    }

    changeWeather = () => {
    const isHot = this.state.isHot
    this.setState(isHot: !isHot)
    }
    }

    ReactDOM.render(<Weather/>, document.getElementById('test'))

    核心属性二:props

    state得自己初始化。

    组件标签的所有属性都保存在props中。

    1、通过标签属性从组件外向组件内传递变化的数据

    2、注意:组件内部不要修改props数据

    简单用法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class Person extends React.Component {
    render() {
    const {name, age, sex} = this.props

    return (
    <ul>
    <li>姓名:{this.name}</li>
    </ul>
    )
    }
    }
    // 这里面就直接传到props里面去了。
    ReactDOM.render(<Person name="jerry"/>, document.getElementById("test"));
    批量传递props
    展开运算符 …
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    // 1、展开运算符 ...
    let arr1 = [1, 2, 3, 4, 5]
    console.log(arr1) // 输出的是 [1, 2, 3, 4, 5]
    console.log(...arr1) // 输出的是展开的 1, 2, 3, 4, 4
    let arr2 = [3, 4, 5]

    // 2、数组组合
    let arr3 = [...arr1, ...arr2]


    // 3、函数传参数
    function sum(...nums) {
    console.log('@', nums)
    nums.reduce((preValue, currentValue) => {
    return preValue + currentValue;
    })
    }
    sum(1, 2, 3, 4)

    // 但是展开对象好像不行?
    let person = {name: '', age: 18}
    let person2 = {...person} // 包了一个花括号。深度拷贝,构造字面量对象时使用展开语法
    let person3 = person. // 浅拷贝
    // console.log(...person) // 报错直接写一个...是不行的,不能展开一个对象

    // 构造对象并且修改
    let person4 = {...person, name:'jack', address: "地址"}
    批量传递props,批量传递标签属性
    1
    2
    3
    4
    // 批量传递props,批量传递标签属性
    // 但是,babel + js的,这里面的{...p}加了花括号也不是构造字面量对象,只是单纯的js+babel可以把对象展开了,但是不能log出来,仅适用于标签的适用对象。这是一个浅拷贝??
    const p = {name: "我", age: 18, sex: '女'}
    ReactDOM.render(<Person {...p}/>, document.getElementById('test'))
    props限制属性类型,默认等

    需要依赖包,prop-types.js,用于对组件标签属性进行限制

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // 对标签属性进行类型、必要性的限制
    Person.propTypes = {
    name: PropTypes.string.isRequired, // name必须是字符串,而且必须有
    sex: PropTypes.string, // 性别可以不传,有默认值,在下面
    age: PropTypes.number,
    speak: PropTypes.func // 必须是函数,注意不是function这个关键字
    }
    // 指定默认标签属性值
    Person.defaultProps = {
    sex: "default",
    age: 19
    }

    Screenshot2023-08-27at17.36.27

    简写

    对状态初始化,对标签限制、指定默认值,都放到类的里面去。前面是放在了外侧

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    class Person extends React.Component {

    static propTypes = {
    name: PropTypes.string.isRequired,
    sex: PropTypes.string,
    age: PropTypes.number,
    speak: PropTypes.func
    }

    static defaultProps = {
    sex: "default",
    age: 19
    }

    render() {
    const {name, age, sex} = this.props

    return (
    <ul>
    <li>姓名:{this.name}</li>
    </ul>
    )
    }
    }

    构造器

    通常在React中,构造函数仅用于两种情况。

    1、通过给this.state赋值对象来处理话内部

    2、为事件处理函数绑定实例

    1
    2
    3
    4
    5
    6
    7
    // 不过这两个都可以通过赋值语句不写在构造器里面
    // 但是如果需要通过props设置state那估计就要写了吧?奥,不然也值语句写吧?
    constructor(props) {
    super(props)
    this.state = {isHot: false, wind: '微风'}
    this.changeWeather.bind(this)
    }

    注意:

    1、在React组件挂在之前,回调用它的构造函数,在为ReactComponent子类实现构造函数时,应在其他语句之前调用super(props)。否则,this.props在构造函数中可能会出现为定义的bug。

    2、在costructor函数中不要调用setState方法。如果组件需要使用内部state,直接在构造函数中为this.state赋值初始state。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    class Person extends React.Component {
    // 外面构建时,标签属性里的,还有模型加上的,都会在这个props里面收到
    constructor(props) {
    console.log(props)
    super(props)
    console.log(this.props) // 注意,这个是实例的props,需要在使用之前调用super(props)
    }

    static propTypes = {
    name: PropTypes.string.isRequired,
    sex: PropTypes.string,
    age: PropTypes.number,
    speak: PropTypes.func
    }

    static defaultProps = {
    sex: "default",
    age: 19
    }

    render() {
    const {name, age, sex} = this.props

    return (
    <ul>
    <li>姓名:{this.name}</li>
    </ul>
    )
    }
    }

    函数式组件:props

    函数没有state、refs,但是有props,因为它能接收参数。
    函数式,最新属性,hooks可以

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    function Person(props) {
    const {name, age, sex} = this.props

    return (
    <ul>
    <li>姓名:{name}</li>
    <li>年龄:{age}</li>
    </ul>
    )
    )
    }

    // 然后属性限制啥的,可以写在外面,奏效
    Person.propTypes = {
    name: PropTypes.string.isRequired,
    sex: PropTypes.string,
    age: PropTypes.number,
    speak: PropTypes.func
    }
    Person.defaultProps = {
    sex: "default",
    age: 19
    }

    ReactDOM.render(<Person name="jerry" sex="女" age={18}/>, document.getElementById("test"));

    核心属性之三:refs

    组件内的标签可以用ref来标识自己。感觉其实跟id差不多,但是更方便。

    1、字符串形式ref

    已经要被遗弃了。过时,因为string类型的ref会出现一些问题:会有一些效率问题,写多了效率不高。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    class Demo extends React.Component {
    showData = () => {
    // const input = document.getElementById('input1')
    // alert(input.value)
    console.log(this)
    console.log(this.refs.input1) // 就能访问到下面那个了
    const {input1} = this.refs;
    alert(input1.value)
    }

    showData2 = () => {
    const {input2} = this.refs;
    alert(input2.value)
    }
    // onBlur是失去焦点回调
    render() {
    return (
    <div>
    <input ref='input1'/>&nbsp;
    <input ref='input2' onBlur={this.showData2}/>&nbsp;
    </div>
    )
    }
    }

    注意,此处的ref,ref是当前所处的节点。

    可以通过上面的打印,看到refs这一个属性,是一个keyvalue对,收集了这个组件内的所有ref。

    其实可以不用上面的document.getElementById(‘input1’),

    可以直接用this.refs.input1访问。访问到的事,虚拟DOM转成真实DOM后的一个真实的节点。

    2、回调形式的ref
    1
    2
    3
    4
    5
    6
    7
    // 1、回调。可以先这样看看这个a是什么,a是input这个节点本身
    <input ref=={(a)=>{console.log(a)}} type="test" placeholder="点击输入"/>

    // 2、所以把这个a给组件实例自身
    <input ref=={currentNode => this.input1 = currentNode } type="test" placeholder="点击输入"/>

    // 3、然后可以用this.input1访问到

    细节:回调函数,调用次数问题。

    内联函数(应该就是闭包的意思),就是直接把函数丢在里面,而不是单独写出来有方法名啥的。

    如果ref回调函数以内联函数的方式定义,在更新过程中(不是第一次上来的时候)会被执行两次,第一次传入参数null,然后第二次回传入参数DOM元素。这是因为在每次渲染的时候会创建一个新的函数实例(因为内联,所以重新创建函数实例),所以React清空旧的ref并且设置新的。通过将ref的回调函数定义成class的绑定函数的方式可以避免上述问题(如下),但是大多数情况下都是无关紧要的。

    组件的更新次数是1+n次,第一次是初始化,n次是状态的更新次数。

    1
    2
    3
    4
    5
    const saveInput = (currentNode) => {
    this.input1 = currentNode
    }

    <input ref = { this.saveInput } type="test" placeholder="点击输入"/>
    3、createRef

    一个容器存储一个,可以创建多个容器。

    注意这个,这些都是class组件,我外面没写。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // React.createRef调用后可以返回一个容器,容器可以存储被ref所标识的节点
    // 注意,容器只能存储一个,后放进去的会把前面的顶掉
    // 要多个就多个容器呗
    myRef = React.createRef调用后可以返回一个容器,容器可以存储被ref所标识的节点()
    // 此处把这个input节点存储在this.myRef容器里面
    <input ref = { this.myRef } type="test" placeholder="点击输入"/>

    // 使用,下面打印的就是input节点。
    console.log(this.myRef.current)
    4、ref总结

    条件允许下,避免字符串形式的ref。

    回调形式稍微麻烦一点,更新被调用两次其实也没有太大问题。

    createRef,创建容器,把容器放在实例自身,然后当前节点存进去,然后可以使用。

    react应用(基于react脚手架)

    前端:Angulat、React、Vue都有自己的脚手架

    1、包含了所需要的配置(语法检查、jsx编译、devServer…),下载好了所有相关以来,可以直接运行一个简单效果。

    2、react提供了一个用语创建react项目的脚手架库:create-react-app

    3、项目的整体技术架构:react+webpack+es6+eslint

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

    创建项目并启动

    1、全局安装库:sudo npm install -g create-react-app

    2、切换到想创建项目的目录,使用命令:create-react-app react-staging

    3、进入项目文件夹:cd react-staging

    4、启动项目:npm start

    脚手架,各个文件介绍

    public文件夹一般存储静态资源文件,页面样式公共图片

    浏览器只认识html、css、js。写的其他的,都会被转成这些。

    应用加壳技术,做应用夹壳,配置文件在这里manifest.json。应用加壳技术就是把网页包装成iOSapp或者Androidapp。这样就是一套代码,两类客户端复用了。

    robots.txt爬虫规则文件,别人爬虫的时候,可以定一些规矩,什么能爬取,什么不能爬取。

    html.index文件

    整个项目里面只有一个index.html文件,没有其他html文件出现。其它需要用组件。

    SPA,单页面应用。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="utf-8" />
    <!-- %PUBLIC_URL%代表public这个文件夹的路径 -->
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    <!-- 开启理想视口,用于做移动端网页的适配 -->
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <!-- 用于配制浏览器页签+地址栏的颜色(仅支持安卓手机浏览器) -->
    <meta name="theme-color" content="#000000" />
    <meta
    name="description"
    content="Web site created using create-react-app"
    />
    <!-- 网站被别人添加到主屏幕之后,显示的图标。只支持苹果手机。 -->
    <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>
    <!-- 组件以后都往这个容器里面放 -->
    <div id="root"></div>
    </body>
    </html>

    setupTests.js,组件测试。第三方库:jest-dom

    reportWebVitals.js,实现页面性能的监测,用了这个库:web-vitals

    App.js

    里面一个组件App.js(函数式组件),App.css。

    以后只往 id=”root” 这个容器里面放一个组件App。然后其它的都放在App里面去。所以会给我们写好一个App。

    index.js

    是一个入口文件。

    这里面会把App这个函数是组件挂载到 id=”root” 这个容器里面。

    1
    2
    3
    4
    5
    6
    ReactDOM.render(
    <React.StrictMode>
    <App/>
    </React.StrictMode>
    document.getElementById('root')
    );

    文件执行顺序

    src下的index.js,引入React、ReactDOM、index.css、App、reportWebVitals,然后ReactDOM渲染,把App这个函数是组件挂载到 id=”root” 这个容器里面。

    此处的getElementById会自动去public下的index.html找id=”root”的容器。(webpackpage里面写了让它去这里面找)

    然后App组件展示到页面上去了。

    一些文件注意:

    webpack-dev-server,支撑整个3000端口服务运行。

    可以新建一个文件夹components,所有的组件文件都放到这里面去。

    components里面对于每一个组件都再创建一个文件夹,比如Hello组件,创建Hello文件夹,然后里面放Hello.js, Hello.css等都是这个组件信息的文件。

    文件夹components应该和App.js还有index.jx平级。

    如果组件文件夹里面有index.jsx,那么引入的时候,可以不写import …../index中的这个/index,但是感觉这样不太好。

    由于有一些js文件是业务功能的,还有一些js文件是组件,怎么区分呢?

    1、组件的js文件首字母大写。

    2、把js改成jsx,jsx结尾的都是组件。App.js一般不改成jsx,但是改也可以。

    React脚手架里面,引入js和jsx文件都不需要写后缀。

    React组件结构

    默认暴露和分别暴露

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    // 以下是module.js
    const React = {a: 1, b: 2}
    // 分别暴露
    export class Component {

    }
    React.Component = Component
    // 统一暴露
    export default React

    // 以下是index.js
    import React from '.module.js'
    const {Component} = React // 这个是统一暴露,先拿到React,在拿到React.Component,这是React的Component

    import React, {Component} from '.module.js'// 这个因为分别暴露,上一个文件分别export Component和React,所以才能这么写。这个Component是文件中export的。
    // 以后看到这么写,就是多个export了。不是解构赋值。

    样式模块化

    1
    import './index.css'

    样式冲突。不同组件里面有个字的css,但是如果css中写的都是一样的className,因为最后都要汇总到App,所以可能会冲突,下面的会覆盖上面的。

    可以直接在样式外面套一个组件名字,比如

    1
    2
    3
    4
    5
    .hello {
    .title {

    }
    }

    样式模块化:

    在index.css中间加一个.module,然后引入hello这个“对象”

    1
    2
    3
    4
    import hello from '.index.module.css'

    // 使用
    <h2 className={hello.title}> 这是Hello! </h2>

    现实中用的少,因为less文件,less是啥?

    功能界面的组件化编码流程

    1、拆分组件:拆分界面、抽取组件

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

    3、实现动态组件

    ​ 3.1、动态显示初始化数据

    ​ 3.1.1、数据类型

    ​ 3.1.2、数据名称

    ​ 3.1.3、保存在哪个组件

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

    React ajax

    4.1 前置说明

    1、React本身只关注洁面,并不包含发送ajax请求的代码

    2、前端应用需要通过ajax请求与后台进行交互(json数据)

    3、React应用中需要继承第三方ajax库(或者自己封装)

    4.2 常用的ajax请求库

    1、jQuery比较重,如果需要另外引入不建议使用。(jQuery是专门操作DOM的,然而React专门不让操作DOM,而是React帮忙操作DOM。)

    2、axios:轻量级,建议使用

    ​ (1)封装XmlHttpRequest对象的ajax

    ​ (2)promise风格

    ​ (3)可以在浏览器端和node服务器端,可以双端运行。

    脚手架配置代理(跨域)

    create-react-app react-axios-test

    在终端:yarn add axios

    yarn add

    错误:The engine “node” is incompatible with this module. Expected version “>=16.0.0”. Got “14.19.3”

    本来应该升级node的,但我为了省事,直接

    yarn config set ignore-engines true

    了,直接忽略错误。

    见部分代码,其他代码见工程react-axios-test。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    import logo from './logo.svg';
    import './App.css';
    import React, {Component} from 'react';
    import axios from 'axios';

    class App extends Component {
    getWelcomeMessage = () => {
    axios.get('http://43.142.73.10:10007/welcome').then(
    (response) => {
    console.log(response)
    },
    error => {
    console.log(error)
    }
    )
    }

    render() {
    return (
    <div>
    <button onClick={this.getWelcomeMessage}>
    Get Welcome Message
    </button>
    </div>
    )
    }
    }

    错误:No ‘Access-Control-Allow-Origin’ header is present on the requested resource.

    跨域,ajax请求不允许从10007返回到3000。端口不一样。

    端口的请求是成功的,但是返回不来。就是ajax引擎是可以让我发出去的,但是请求返回的时候,ajax引擎把返回拦在了外面。因为跨域了。

    解决:搞一个中间服务器(本地的),端口也是3000(react默认端口3000),ajax引擎从3000发送请求给3000端口的中间服务器,中间服务器到10007请求并且拿到返回(这个可以返回,因为没有ajax引擎。),然后给ajax引擎3000端口

    2种方法配置这个中间代理。

    1、package.json中增加配置
    1
    "proxy":"http://http://43.142.73.10:10007"

    这句话一加表示所有转发给localhost的3000的请求都转发给了这个服务器43.142.73.10的10007。

    所以发送请求的时候,要请求这个localhost的3000端口。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    getWelcomeMessage = () => {
    axios.get('http://localhost:3000/welcome').then(
    (response) => {
    console.log(response)
    },
    error => {
    console.log(error)
    }
    )
    }

    然后如果我请求’http://localhost:3000/welcome',因为这个中间代理是这个前端,前端public/index.html存在,直接返回,根本不需要往这个服务器43.142.73.10的10007发送请求。

    2、配置多个代理setupProxy.js

    1中的缺点很大,如果要请求两个服务器,proxy中可以写两个吗?应该不行。

    2的优点是可以配置多个代理,可以灵活的控制请求是否走代理。缺点是配置繁琐,前端请求资源时必须加上前缀。

    在src中添加文件,setupProxy.js。不能用ES6写,要用CJS写。

    React会找到这个文件,加到webpack的page里面,webpack用的都是node里的语法,写的是CJS。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    // setupProxy.js
    // const proxy = require("http-proxy-middleware")
    const { createProxyMiddleware } = require("http-proxy-middleware")

    module.exports = function(app) {
    app.use(
    // 只要有/api1就会走这个代理
    createProxyMiddleware("/api1", {
    target: "http://43.142.73.10:10007", // 配置转发目标地址(能返回数据的服务器地址)
    changeOrigin: true, // 控制服务器收到的请求头中Host的值,请求时哪里发出来的
    pathRewrite: {"^/api1": ""} // 去除请求前缀,保证交给后台服务器的是正常请求地址(必须配置)
    }),
    // 只要有/api2就会走这个代理
    createProxyMiddleware("/api2", {
    target: "http://43.142.73.10:10008", // 配置转发目标地址(能返回数据的服务器地址)
    changeOrigin: true, // 控制服务器收到的请求头中Host的值
    pathRewrite: {"^/api2": ""} // 去除请求前缀,保证交给后台服务器的是正常请求地址(必须配置)
    }),
    )
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    // App.js

    class App extends Component {
    getWelcomeMessage = () => {
    axios.get('http://localhost:3000/api1/welcome').then(
    (response) => {
    console.log(response)
    },
    error => {
    console.log(error)
    }
    )
    }

    render() {
    return (
    <div>
    <button onClick={this.getWelcomeMessage}>
    Get Welcome Message
    </button>
    </div>
    )
    }
    }

    打包到生产

    1
    npm run build

    之后,会生成一个build文件夹。

    1
    npm i serve -g

    全局安装serve,用serve就可以将build的静态文件开一个服务器运行起来。

    然后到build文件夹下面(有index.html)运行下面命令就行

    1
    serve [文件夹名字]

    Author: Jcwang

    Permalink: http://example.com/2023/08/19/React/