React面向组件编程【第二章】

基本理解和使用

  • 函数式组件
    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">
    <title></title>
    </head>
    <body>
    <div id="app"></div>
    <script src="./js/react.development.js"></script>
    <script src="./js/react-dom.development.js"></script>
    <script src="./js/babel.min.js"></script>
    <script type="text/babel">
    // 1.创建函数式组件【组件:包含html,js,css等等】
    function MyComponent(){
    console.log(this); // undefined babel编译后,开启了严格模式
    return <h2>我使用函数定义的组件(适用于【简单函数】的定义)</h2>
    }
    // 2.渲染组件到页面
    // 不能写函数名【写标签,开头大写,标签闭合】
    ReactDOM.render(<MyComponent/>,document.getElementById('app'))
    /*
    执行了ReactDOM.render() .........之后,发生了什么?
    1.React解析组件标签,找到 MyComponent 组件
    2.发现组件是用函数定义的,调用该组件,将返回的虚拟DOM转为真实的DOM,随后呈现在页面中
    */
    </script>
    </body>
    </html>
  • 类式组件
    • 类的基本使用
      • 类中的构造器不是必须写的,要对实例进行一些初始化的操作,如添加指定属性时才写
      • 如果A类继承了B类,且A类中写了构造器,那么A类构造器中的super是必须要调用的
      • 类中所定义的方法,都是放在了类的原型对象上,供实例去使用
        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
           // 定义一个类
        class Person{
        // 构造器
        constructor(name,age){
        // this是类的实例对象【lisi】
        this.name = name
        this.age = age
        }
        speak(){
        // 通过Person实例调用speak,speak中的this就是Person实例
        console.log(`${this.name}在说话`); ————类的原型对象上,供实例使用
        }
        info(){
        console.log(`我的名字${this.name},今年${this.age}`);
        }
        }
        class Student extends Person{
        constructor(name,age,grade){
        super(name,age)
        this.grade = grade
        }
        info(){
        console.log(`我的名字${this.name},今年${this.age},${this.grade}`);
        }
        }
        let s1 = new Student('小明',23)
        let s2 = new Student('李四',20,'大一')
        s1.info()
        s1.speak()
        s2.info()
        s2.speak()
        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
        <!DOCTYPE html>
        <html lang="en">
        <head>
        <meta charset="UTF-8">
        <title></title>
        </head>
        <body>
        <div id="app"></div>
        <script src="./js/react.development.js"></script>
        <script src="./js/react-dom.development.js"></script>
        <script src="./js/babel.min.js"></script>
        <script type="text/babel">
        // 1.创建类式组件
        class MyComponent extends React.Component {
        render(){
        // render是放在哪里的? ————MyComponent的原型对象上,供实例使用
        // render中的this是谁? ————MyComponent的实例对象【MyComponent组件实例对象】
        console.log(123,this);
        return <h2>我使用类定义的组件(适用于【复杂函数】的定义)</h2>
        }
        }
        // 2.渲染组件到页面
        ReactDOM.render(<MyComponent/>,document.getElementById('app'))
        /*
        执行了ReactDOM.render() .........之后,发生了什么?
        1.React解析组件标签,找到 MyComponent 组件
        2.发现组件是用类定义的,随后 new 出来该类【MyComponent】的实例,通过该实例调用原型上的方法【render】
        3.将 render 返回的虚拟DOM转为真实的DOM,随后呈现在页面中
        */
        </script>
        </body>
        </html>

组件实例【类式组件】三大核心属性:state【状态】

state是组件对象最重要的属性,值是对象
组件被称为 “状态机” ,通过更新组件的 state 来更新对应的页面(重新渲染组件)

  • 组件中 render 的this为组件实例对象
  • 组件中的自定义方法的 this 为undefined【解决:1.bind()方法 2.箭头函数】
  • 状态数据不能直接修改和更新
    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
    <button id="btn1">按钮1</button>
    <button id="btn2">按钮2</button>
    <button id="btn3" onclick="demo()">按钮3</button>
    <script type="text/javascript">
    const btn1 = document.getElementById('btn1')
    btn1.addEventListener('click',()=> {
    alert('按钮1被点击')
    })
    btn2.onclick = () => {
    alert('按钮2被点击')
    }
    function demo(){
    alert('按钮3被点击')
    }

    // 类中的实例调用 与 直接调用
    class Person {
    constructor(name,age){
    this.name = name
    this.age = age
    }
    speak(){
    console.log(this);
    }
    }
    let p1 = new Person('小明',18)
    // 实例调用
    p1.speak() // Person的实例对象
    // 直接调用
    let x = p1.speak
    console.log('直接调用',x); // 函数

    // undefined 严格模式下this为undefined
    // 不开启this指向window
    function text(){
    // 'use strict'
    console.log(this);
    }
    text()
    </script>
    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
    46
    47
    48
    49
    50
    51
    52
    53
    <!DOCTYPE html>
    <html lang="en">

    <head>
    <meta charset="UTF-8">
    <title></title>
    </head>

    <body>
    <div id="app"></div>
    <script src="./js/react.development.js"></script>
    <script src="./js/react-dom.development.js"></script>
    <script src="./js/babel.min.js"></script>
    <script type="text/babel">
    class Weather extends React.Component {
    // 构造器调用几次? ————————1次 【只用了一次Weather标签,只new出来一个实例对象】
    // 构造器的作用:1.初始化状态 2.修改this指向
    constructor(props) {
    console.log('constructor');
    super(props)
    // 初始化状态
    this.state = { isHot: false ,wind:'微风'}
    // bind改变this的指向,解决changeWeather中的this指向问题
    this.changeWeather = this.changeWeather.bind(this)
    }
    // render调用几次? ————————1+n次【每次修改状态,就会render一次(1是初识渲染,n是状态更新次数)】
    // render的作用:1.从状态里读取数据 2.根据状态值做展示
    render() {
    console.log('render');
    const { isHot,wind } = this.state
    return (
    <h1>今天天气很<span onClick={this.changeWeather}>{isHot ? '炎热' : '寒冷'},{wind}</span></h1>
    )
    }
    // 由于 changeWeather 是作为 onClick 的回调,所以不是通过实例调用的,是直接调用
    // 类中的方法默认开启了局部的严格模式
    // changeWeather() { console.log(this); } // 为什么demo里的this是undefined ? ————————Weather实例没有调用demo
    // render调用几次? ————————点几次,调几次
    changeWeather() {
    // 通过 Weather 实例调用 changeWeather 时,changeWeather中的this就是Weather实例
    // 注意: React.js不允许直接更改 state 里的数据 【例如: this.state.isHot = !this.state.isHot】
    // 注意: 状态必须通过 setState 进行更改,且更新是一种合并,以前的数据不会丢失
    this.setState({
    isHot:!this.state.isHot
    })
    }
    }
    ReactDOM.render(<Weather />, document.getElementById('app'))

    </script>
    </body>

    </html>
  • 简写state*
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    <div id="app"></div>
    <script src="./js/react.development.js"></script>
    <script src="./js/react-dom.development.js"></script>
    <script src="./js/babel.min.js"></script>
    <script type="text/babel">
    class Weather extends React.Component {
    // 初始化状态
    state = { isHot: false, wind: '微风' }
    render() {
    const { isHot, wind } = this.state
    return (
    <h1>今天天气很<span onClick={this.changeWeather}>{isHot ? '炎热' : '寒冷'},{wind}</span></h1>
    )
    }
    // 自定义方法
    changeWeather = () => {
    this.setState({
    isHot: !this.state.isHot
    })
    }
    }
    ReactDOM.render(<Weather />, document.getElementById('app'))
    </script>

组件实例【类式组件】三大核心属性:props

基本使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<div id="app"></div>
<div id="app1"></div>
<script src="./js/react.development.js"></script>
<script src="./js/react-dom.development.js"></script>
<script src="./js/babel.min.js"></script>
<script type="text/babel">
class Weather extends React.Component {
render() {
return (
<div >
<ul>
<li>姓名:{this.props.name}</li>
<li>性别:{this.props.gender}</li>
<li>年龄:{this.props.age}</li>
</ul>
</div>
)
}
}
ReactDOM.render(<Weather name="lisi" gender='男' age='23' />, document.getElementById('app'))
ReactDOM.render(<Weather name="wangwu" gender='女' age='18' />, document.getElementById('app1'))
</script>

扩展运算符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<script type="text/javascript">
// 构造字面量对象时使用展开语法
let person = {
name: '张三',
gender: '男',
age: 18,
cars:['奔驰','宝马','长安']
}
let student = {
name: '张三san',
grade:'高三'
}
let person1 = {...person}
// 进行拷贝,浅层拷贝
person1.cars[1] = '奔驰1111111'
person.name = '张三3333'
console.log(person);
console.log(person1);
console.log({...person,...student});
</script>

对props进行限制

  • 添加类的规则写法:类名.propTypes = {} 例如:Person.propTypes = {id: PropTypes.number.isRequired}
  • 默认的标签属性值写法:类名.defaultProps = {}
    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
    46
    47
    48
    49
    50
    <div id="app"></div>
    <div id="app1"></div>
    <script src="./js/react.development.js"></script>
    <!-- 用于支持React操作DOM -->
    <script src="./js/react-dom.development.js"></script>
    <!-- 用于jsx转js -->
    <script src="./js/babel.min.js"></script>
    <!-- 用于对属性标签数据限制 -->
    <script src="./js/prop-types.js"></script>
    <script type="text/babel">
    class Person extends React.Component {
    render() {
    let { name, gender, age } = this.props
    return (
    <div >
    <ul>
    <li>姓名:{name}</li>
    <li>性别:{gender}</li>
    <li>年龄:{age + 1}</li>
    </ul>
    </div>
    )
    }
    }
    // 添加类的规则【对标签属性进行类型、必要性的限制】
    // isRequired必传 defaultProps默认值
    Person.propTypes = {
    name: PropTypes.string.isRequired,
    gender: PropTypes.string,
    age: PropTypes.number,
    speak: PropTypes.func,
    }
    // 指定默认的标签属性值
    Person.defaultProps = {
    gender: '男',
    age: 18
    }
    let data = {
    name: '12',
    gender: '男',
    age: 18
    }
    // {...data} 的花括号是作为分割符在使用 相当于 ...data
    ReactDOM.render(<Person name='222' gender='女' age={18} speak={speak}/>, document.getElementById('app'))
    ReactDOM.render(<Person {...data} />, document.getElementById('app1'))

    function speak(){
    console.log('说话了');
    }
    </script>

简写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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<div id="app"></div>
<div id="app1"></div>
<script src="./js/react.development.js"></script>
<!-- 用于支持React操作DOM -->
<script src="./js/react-dom.development.js"></script>
<!-- 用于jsx转js -->
<script src="./js/babel.min.js"></script>
<!-- 用于对属性标签数据限制 -->
<script src="./js/prop-types.js"></script>
<script type="text/babel">
class Person extends React.Component {
// 给Person自身添加一个属性 propTypes

// 添加类的规则【对标签属性进行类型、必要性的限制】
// isRequired必传 defaultProps默认值
static propTypes = {
name: PropTypes.string.isRequired,
gender: PropTypes.string,
age: PropTypes.number,
speak: PropTypes.func,
}

// 给Person自身添加一个属性 defaultProps
// 指定默认的标签属性值
static defaultProps = {
gender: '男',
age: 18
}
render() {
let { name, gender, age } = this.props
// this.props.name = 'Tom' // 报错 props是只读的
return (
<div >
<ul>
<li>姓名:{name}</li>
<li>性别:{gender}</li>
<li>年龄:{age + 1}</li>
</ul>
</div>
)
}
}
let data = {
name: '12',
gender: '男',
age: 18
}
// {...data} 的花括号是作为分割符在使用 相当于 ...data
ReactDOM.render(<Person name='222' gender='女' age={18} speak={speak} />, document.getElementById('app'))
ReactDOM.render(<Person {...data} />, document.getElementById('app1'))

function speak() {
console.log('说话了');
}
</script>

构造器的注意事项

  • 构造器是否接收 props ,是否传递给 super ,取决于:是否希望在构造器中通过this访问props
  • 当 constructor 不传 props,super也不接收 props ,通过实例访问props【this.props】 则为 undefined
    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
    46
    47
    <div id="app"></div>
    <script src="./js/react.development.js"></script>
    <!-- 用于支持React操作DOM -->
    <script src="./js/react-dom.development.js"></script>
    <!-- 用于jsx转js -->
    <script src="./js/babel.min.js"></script>
    <!-- 用于对属性标签数据限制 -->
    <script src="./js/prop-types.js"></script>
    <script type="text/babel">
    class Person extends React.Component {
    // constructor 尽量不写构造器
    constructor(props){
    // 构造器是否接收 props ,是否传递给 super ,取决于:是否希望在构造器中通过this访问props
    super(props)
    // 1.当 constructor 不传 props,super也不接收 props ,通过实例访问props【this.props】 则为 undefined
    console.log(props);
    }
    // 给Person自身添加一个属性 propTypes
    // 添加类的规则【对标签属性进行类型、必要性的限制】
    // isRequired必传 defaultProps默认值
    static propTypes = {
    name: PropTypes.string.isRequired,
    gender: PropTypes.string,
    age: PropTypes.number
    }
    // 给Person自身添加一个属性 defaultProps
    // 指定默认的标签属性值
    static defaultProps = {
    gender: '男',
    age: 18
    }
    render() {
    let { name, gender, age } = this.props
    // this.props.name = 'Tom' // 报错 props是只读的
    return (
    <div >
    <ul>
    <li>姓名:{name}</li>
    <li>性别:{gender}</li>
    <li>年龄:{age + 1}</li>
    </ul>
    </div>
    )
    }
    }
    ReactDOM.render(<Person name='222' />, document.getElementById('app'))
    </script>

函数式组件只能使用 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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
<div id="app"></div>
<div id="app1"></div>
<script src="./js/react.development.js"></script>
<!-- 用于支持React操作DOM -->
<script src="./js/react-dom.development.js"></script>
<!-- 用于jsx转js -->
<script src="./js/babel.min.js"></script>
<!-- 用于对属性标签数据限制 -->
<script src="./js/prop-types.js"></script>
<script type="text/babel">
// 类式组件
class Person extends React.Component {
static propTypes = {
name: PropTypes.string.isRequired,
gender: PropTypes.string,
age: PropTypes.number
}
static defaultProps = {
gender: '男',
age: 18
}
render() {
let { name, gender, age } = this.props
return (
<div >
<ul>
<li>姓名:{name}</li>
<li>性别:{gender}</li>
<li>年龄:{age + 1}</li>
</ul>
</div>
)
}
}
// 函数式组件
function Person1(props) {
let { name, gender, age } = props
return (
<div >
<ul>
<li>姓名:{name}</li>
<li>性别:{gender}</li>
<li>年龄:{age + 1}</li>
</ul>
</div>
)
}
Person1.propTypes = {
name: PropTypes.string.isRequired,
gender: PropTypes.string,
age: PropTypes.number
}
Person1.defaultProps = {
gender: '男',
age: 18
}
ReactDOM.render(<Person name='222' />, document.getElementById('app'))
ReactDOM.render(<Person1 name='李四' gender='男' age={22} />, document.getElementById('app1'))
</script>

组件实例【类式组件】三大核心属性:refs 与事件处理

  • refs理解: 组件内的标签可以定义 ref 来标识自己
  • 字符串类型的ref写法: 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
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    <div id="app"></div>
    <script src="./js/react.development.js"></script>
    <!-- 用于支持React操作DOM -->
    <script src="./js/react-dom.development.js"></script>
    <!-- 用于jsx转js -->
    <script src="./js/babel.min.js"></script>
    <!-- 用于对属性标签数据限制 -->
    <script src="./js/prop-types.js"></script>
    <script type="text/babel">
    // 类式组件
    class Person extends React.Component {
    render() {
    let { name } = this.props
    return (
    <div>
    <input ref='input' placeholder='点击按钮提示数据' type="text"/>&nbsp;
    <button ref='btn1' onClick={this.open}>按钮</button>&nbsp;
    <input ref='input1' placeholder='失去焦点提示数据' type="text" onBlur={this.showData}/>
    </div>
    )
    }
    open = () => {
    // 是真实节点DOM,而不是虚拟DOM
    // console.log(this.refs.input);
    let {input} = this.refs
    alert(input.value)
    }
    showData = () => {
    let {input1} = this.refs
    alert(input1.value)
    }
    }
    ReactDOM.render(<Person name='lisi' />, document.getElementById('app'))
    </script>
  • 回调函数形式的ref写法: ref={c => {this.input1 = c}} 【内联函数的方式】
    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
    <div id="app"></div>
    <script src="./js/react.development.js"></script>
    <!-- 用于支持React操作DOM -->
    <script src="./js/react-dom.development.js"></script>
    <!-- 用于jsx转js -->
    <script src="./js/babel.min.js"></script>
    <!-- 用于对属性标签数据限制 -->
    <script src="./js/prop-types.js"></script>
    <script type="text/babel">
    class Person extends React.Component {
    render() {
    let { name } = this.props
    return (
    <div>
    <input ref={c => this.input = c} placeholder='点击按钮提示数据' type="text" />&nbsp;
    <button ref='btn1' onClick={this.open}>按钮</button>&nbsp;
    <input ref={c => this.input1 = c} placeholder='失去焦点提示数据' type="text" onBlur={this.showData} />
    </div>
    )
    }
    open = () => {
    let { input } = this
    alert(input.value)
    }
    showData = () => {
    let { input1 } = this
    alert(input1.value)
    }
    }
    ReactDOM.render(<Person name='lisi' />, document.getElementById('app'))
  • 内联函数的方式:回调ref中回调次数的问题【解决:类绑定函数的方式:ref={this.textFnc}}】
    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
    46
    47
    48
    49
    50
    <div id="app"></div>
    <script src="./js/react.development.js"></script>
    <!-- 用于支持React操作DOM -->
    <script src="./js/react-dom.development.js"></script>
    <!-- 用于jsx转js -->
    <script src="./js/babel.min.js"></script>
    <!-- 用于对属性标签数据限制 -->
    <script src="./js/prop-types.js"></script>
    <script type="text/babel">
    // 回调ref中回调次数的问题
    class Person extends React.Component {
    state = {
    isHot: false
    }
    open = () => {
    let { input } = this
    alert(input.value)
    }
    ChangeWeather = () => {
    this.setState({
    isHot: !this.state.isHot
    })
    }
    // 通过将 ref 的回调函数定义成 class 的绑定函数的方式
    // 不会再重复调用saveInput
    saveInput = (c) => {
    this.input = c
    console.log("123",c)
    }
    render() {

    let { isHot } = this.state
    let { name } = this.props
    // c页面更新后会调用连词,第一次为null,第二次才会返回节点
    // jsx注释 {/* */}
    // 通过将 ref 的回调函数定义成 class 的绑定函数的方式
    return (
    <div>
    <h2>今天天气很{isHot ? '炎热' : '寒冷'}</h2>
    {/* 内联用法比较多,即下面的方式 */}
    {/* <input ref={c => {this.input = c ;console.log("123",c)}} placeholder='点击按钮提示数据' type="text" />&nbsp; */}
    <input ref={this.saveInput} placeholder='点击按钮提示数据' type="text" />&nbsp;
    <button onClick={this.open}>按钮</button>
    <button onClick={this.ChangeWeather}>改变天气</button>
    </div>
    )
    }
    }
    ReactDOM.render(<Person name='lisi' />, document.getElementById('app'))
    </script>
  • createRef的方式 【最推荐的方式】
    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
    <div id="app"></div>
    <script src="./js/react.development.js"></script>
    <!-- 用于支持React操作DOM -->
    <script src="./js/react-dom.development.js"></script>
    <!-- 用于jsx转js -->
    <script src="./js/babel.min.js"></script>
    <!-- 用于对属性标签数据限制 -->
    <script src="./js/prop-types.js"></script>
    <script type="text/babel">
    // React.createRef ;调用后可以返回一个容器,该容器可以存储被 ref 所标识的节点 '该容器是专人专用'
    class Person extends React.Component {
    myRef = React.createRef()
    myRef1 = React.createRef()
    open = () => {
    alert(this.myRef.current.value)
    }
    open1 = () => {
    alert(this.myRef1.current.value)
    }
    render() {
    return (
    <div>
    <input ref={this.myRef} placeholder='点击按钮提示数据' type="text" />&nbsp;
    <button onClick={this.open}>按钮</button>&nbsp;
    <input onBlur={this.open1} ref={this.myRef1} placeholder='失去焦点提示数据' type="text" />
    </div>
    )
    }
    }
    ReactDOM.render(<Person name='lisi' />, document.getElementById('app'))
    </script>
  • 事件处理
    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
    <div id="app"></div>
    <script src="./js/react.development.js"></script>
    <!-- 用于支持React操作DOM -->
    <script src="./js/react-dom.development.js"></script>
    <!-- 用于jsx转js -->
    <script src="./js/babel.min.js"></script>
    <!-- 用于对属性标签数据限制 -->
    <script src="./js/prop-types.js"></script>
    <script type="text/babel">
    class Person extends React.Component {
    /*
    1.通过 onXxx 属性指定事件处理函数 (注意大小写)
    a. React使用的是自定义(合成)事件,而不是原生DOM事件 ——————为了更好的兼容性
    b. React中的事件是通过 事件委托的方式 处理的(委托给组件最外层的元素) ——————为了高效
    2.通过 event.target 得到发生事件的DOM元素对象 【发生事件的元素正好是你要操作的元素 】 ——————不要过度的使用ref
    */
    // 创建容器
    myRef = React.createRef()
    myRef1 = React.createRef()
    open = () => {
    alert(this.myRef.current.value)
    }
    // 发生事件的元素正好是你要操作的元素
    open1 = (e) => {
    alert(e.target.value)
    }
    render() {
    return (
    <div>
    <input ref={this.myRef} placeholder='点击按钮提示数据' type="text" />&nbsp;
    <button onClick={this.open}>按钮</button>&nbsp;
    <input onBlur={this.open1} placeholder='失去焦点提示数据' type="text" />
    </div>
    )
    }
    }
    ReactDOM.render(<Person name='lisi' />, document.getElementById('app'))
    </script>

收集表单数据 【受控组件、非受控组件】

  • 非受控组件: 现用现取 【页面中输入类的DOM,你是现用现取就是非受控组件】
    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
    <div id="app"></div>
    <script src="./js/react.development.js"></script>
    <!-- 用于支持React操作DOM -->
    <script src="./js/react-dom.development.js"></script>
    <!-- 用于jsx转js -->
    <script src="./js/babel.min.js"></script>
    <!-- 用于对属性标签数据限制 -->
    <script src="./js/prop-types.js"></script>
    <script type="text/babel">
    /* 非受控组件 ; 现用现取 【页面中输入类的DOM,你是现用现取就是非受控组件】 */
    // 创建组件
    class Login extends React.Component {
    handleSubmit = (e) => {
    e.preventDefault() // 阻止默认事件
    let { username, password } = this
    alert(`用户名:${username.value},密码:${password.value}` )
    }
    render() {
    return (
    <form action="http://www.wuqisuda.cn" onSubmit={this.handleSubmit}>
    用户名: <input ref={c => this.username = c} name="username" type="text" />
    密码: <input ref={c => this.password = c} name="password" type="password" />
    <button onClick={this.open}>登录</button>
    </form>
    )
    }
    }
    // 渲染组件
    ReactDOM.render(<Login name='lisi' />, document.getElementById('app'))
    </script>
  • 受控组件: 页面中输入类的DOM,随着你的输入,就能把数据维护到状态里面去【类似 vue 的 双向绑定】
    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
    <div id="app"></div>
    <script src="./js/react.development.js"></script>
    <!-- 用于支持React操作DOM -->
    <script src="./js/react-dom.development.js"></script>
    <!-- 用于jsx转js -->
    <script src="./js/babel.min.js"></script>
    <!-- 用于对属性标签数据限制 -->
    <script src="./js/prop-types.js"></script>
    <script type="text/babel">
    /* 受控组件 ; 现用现取 【页面中输入类的DOM,随着你的输入,就能把数据维护到状态里面去】【类似 vue 的 双向绑定】 */
    // 创建组件
    class Login extends React.Component {
    // 初始化
    state = {
    username: '',
    password: ''
    }
    saveUsername = (e) => {
    this.setState({
    username: e.target.value
    })
    }
    savePassword = (e) => {
    this.setState({
    password: e.target.value
    })
    }
    handleSubmit = (e) => {
    e.preventDefault() // 阻止默认事件
    let { username, password } = this.state
    alert(`用户名:${username},密码:${password}`)
    }
    render() {
    return (
    <form action="http://www.wuqisuda.cn" onSubmit={this.handleSubmit}>
    用户名: <input onChange={this.saveUsername} name="username" type="text" />
    密码: <input onChange={this.savePassword} name="password" type="password" />
    <button onClick={this.open}>登录</button>
    </form>
    )
    }
    }
    // 渲染组件
    ReactDOM.render(<Login name='lisi' />, document.getElementById('app'))
    </script>

高阶函数 函数柯里化

  • 高阶函数: 符合一下2个规范中的任意一个,就是高阶函数
    1. 若A函数,接收的参数是一个函数,A就是高阶函数
    2. 若A函数,调用的返回值是一个函数,A就是高阶函数
    • 常见的高阶函数: Promise 、 setTimeout 、 arr.map()等
  • 函数柯里化: 通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式
    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
    // 创建组件
    class Login extends React.Component {
    // 初始化
    state = {
    username: '',
    password: ''
    }
    // saveFormdata接收一个 type 参数 返回一个函数 ,该函数接收一个 event 参数。在 this.setState 统一处理 【函数柯里化】
    saveFormdata = (type) => {
    // 将 saveFormdata 返回箭头函数给 onChange 作为回调
    // 输入用户名和密码给的是 return 的函数,所以 saveFormdata 函数没有 event
    // 输入后触发 onChange 事件,React 把 event 传给了箭头函数
    return (event) => {
    this.setState({
    [type]:event.target.value
    })
    }
    }
    handleSubmit = (e) => {
    e.preventDefault() // 阻止默认事件
    let { username, password } = this.state
    alert(`用户名:${username},密码:${password}`)
    }
    render() {
    return (
    <form action="http://www.wuqisuda.cn" onSubmit={this.handleSubmit}>
    用户名: <input onChange={this.saveFormdata('username')} name="username" type="text" />
    密码: <input onChange={this.saveFormdata('password')} name="password" type="password" />
    <button onClick={this.open}>登录</button>
    </form>
    )
    }
    }
    // 渲染组件
    ReactDOM.render(<Login name='lisi' />, document.getElementById('app'))
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <script type="text/javascript">
    // 函数柯里化
    function sum(a) {
    return (b) => {
    return (c) => {
    return a + b + c
    }
    }
    }
    let result = sum(1)(2)(3)
    console.log(result);
    </script>
  • 不使用柯里化实现【内联函数】
    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
    // 不使用柯里化函数实现
    class Login extends React.Component {
    // 初始化
    state = {
    username: '',
    password: ''
    }
    saveFormdata = (type, event) => {
    this.setState({
    [type]: event.target.value
    })
    }
    handleSubmit = (e) => {
    e.preventDefault() // 阻止默认事件
    let { username, password } = this.state
    alert(`用户名:${username},密码:${password}`)
    }
    render() {
    return (
    <form action="http://www.wuqisuda.cn" onSubmit={this.handleSubmit}>
    用户名: <input onChange={event => this.saveFormdata('username',event)} name="username" type="text" />
    密码: <input onChange={event => this.saveFormdata('password',event)} name="password" type="password" />
    <button onClick={this.open}>登录</button>
    </form>
    )
    }
    }
    ReactDOM.render(<Login name='lisi' />, document.getElementById('app'))
  • 对象的相关知识
    1
    2
    3
    4
    5
    6
    7
    8
    <script type="text/javascript">
    // 在空对象里添加属性 name :'tom'
    let a = 'name'
    let obj = {}
    // 注意错误示例:obj.a = 'tom' // {a :'tom'}
    obj[a] = 'tom'
    console.log( obj); // {name :'tom'}
    </script>

React 的 生命周期

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
46
47
48
49
50
<div id="app"></div>
<script src="./js/react.development.js"></script>
<!-- 用于支持React操作DOM -->
<script src="./js/react-dom.development.js"></script>
<!-- 用于jsx转js -->
<script src="./js/babel.min.js"></script>
<!-- 用于对属性标签数据限制 -->
<script src="./js/prop-types.js"></script>
<script type="text/babel">
//#region
// 生命周期回调函数 == 生命周期钩子函数
//#endregion
class Life extends React.Component {
state = {
opacity: 1,
}
text = () => {
// 卸载定时器
// clearInterval(this.timer)
// 卸载组件
ReactDOM.unmountComponentAtNode(document.getElementById('app'))
}
// 挂载
componentDidMount() {
this.timer = setInterval(() => {
let { opacity } = this.state
if (opacity >= 0) {
opacity -= 0.1
} else {
opacity = 1
}
this.setState({ opacity })
}, 200)
}
componentWillUnmount() {
// 卸载定时器
clearInterval(this.timer)
}
render() {
let { opacity } = this.state
return (
<div>
<h2 style={{ 'opacity': opacity }}>React学不会怎么办?</h2>
<button onClick={this.text}>不活了</button>
</div>
)
}
}
ReactDOM.render(<Life name='lisi' />, document.getElementById('app'))
</script>

React 的 生命周期 [旧]

  • 初始化阶段:由ReactDOM.render()触发 ——————初次渲染
    1. constructor ————构造器【数据初始化(state)】
    2. componentWillMount ————将要挂载
    3. render ————渲染
    4. componentDidMount ————挂载完毕【常用】
      • 做一些初始化的事情:发起网络请求、开启定时器、订阅消息
  • 更新阶段: 由组件内部 this.setState() 或父组件重新 render 触发【强制更新 this.forceUpdate() : 强制更新数据钩子 ——————不走阀门】
    1. shouldComponentUpdate ————控制更新的阀门
    2. componentWillUpdate ————将要更新
    3. render ————渲染
    4. componentDidUpdate ————更新完毕
  • 卸载组件:由ReactDOM.unmountComponentAtNode(document.getElementById(‘’))触发
    1. componentWillUnmount ————将要卸载
      • 做一些收尾的事情:关闭定时器、取消订阅消息
  • 父组件更新render:
    1. componentWillReceiveProps ————组将将要接受props 【第一次接受的props不会触发componentWillReceiveProps钩子】
    2. shouldComponentUpdate
    3. componentWillUpdate
    4. render ————子组件渲染
    5. componentDidUpdate

17.0.0以后 componentWillReceiveProps componentWillMount componentWillUpdate前面要加 UNSAFE_ 【即将废弃】

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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
<div id="app"></div>
<script src="./js/react.development.js"></script>
<!-- 用于支持React操作DOM -->
<script src="./js/react-dom.development.js"></script>
<!-- 用于jsx转js -->
<script src="./js/babel.min.js"></script>
<!-- 用于对属性标签数据限制 -->
<script src="./js/prop-types.js"></script>
<script type="text/babel">
//#region
// 生命周期回调函数 == 生命周期钩子函数
//#endregion
class Life extends React.Component {
// 构造器
constructor(props) {
console.log('constructor');
super(props)
this.state = {
num: 0,
}
}

// 卸载组件按钮的回调
unMount = () => {
ReactDOM.unmountComponentAtNode(document.getElementById('app'))
}

// 正常更新数据按钮的回调
upData = () => {
let { num } = this.state
num += 1
this.setState({
num
})
}
// forceUpdate()
// 强制更新数据钩子 ——————不走阀门
// 不对状态进行任何修改,更新一下
// 强制更新按钮的回调
force = () => {
this.forceUpdate()
}


// 组件将要挂载
componentWillMount() {
console.log('组件将要挂载,componentWillMount');
}
// 组件挂载完毕
componentDidMount() {
console.log('组件挂载完毕,componentDidMount');
}
// 组件将要卸载
componentWillUnmount() {
console.log('组件将要卸载,componentWillUnmount');
}
// 控制组件更新的阀门 ————————必有返回值【不去设置都是true】【布尔值】
shouldComponentUpdate() {
console.log('控制组件更新的阀门,shouldComponentUpdate');
return true
}
// 组件将要更新
componentWillUpdate() {
console.log('组件将要更新,componentWillUpdate');
}
// 组件更新完毕
componentDidUpdate() {
console.log('组件更新完毕,componentDidUpdate');
}
render() {
console.log('渲染,render');
let { num } = this.state
return (
<div>
<h2>当前求和为{num}</h2>
<button onClick={this.upData}>点我加1</button>
<button onClick={this.unMount}>卸载组件</button>
<button onClick={this.force}>不更改状态中的数据,强制更新</button>
</div>
)
}
}

class A extends React.Component {
state = { carName: '宝马' }
changeCar = () => {
let { carName } = this.state
carName = '奔驰'
this.setState({
carName
})
}
render() {
console.log('父组件组件渲染,render');
let { carName } = this.state
return (
<div>
<div>A</div>
<button onClick={this.changeCar}>换车</button>
<B carName={carName} />
</div>
)
}
}
class B extends React.Component {
// A组件【父组件】render
// 组将将要接受props 【第一次接受的props不会触发componentWillReceiveProps钩子】
componentWillReceiveProps(props){
console.log('组将将要接受props,componentWillReceiveProps',props);
}
shouldComponentUpdate() {
console.log('控制组件更新的阀门,shouldComponentUpdate');
return true
}
// 组件将要更新
componentWillUpdate() {
console.log('组件将要更新,componentWillUpdate');
}
// 组件更新完毕
componentDidUpdate() {
console.log('组件更新完毕,componentDidUpdate');
}
render() {
console.log('子组件渲染,render');
let { carName } = this.props
return (
<div>
<h2>{carName}</h2>
<div>B</div>
</div>
)
}
}

ReactDOM.render(<A name='lisi' />, document.getElementById('app'))
</script>

React 的 生命周期 [新]

  • 初始化阶段:由ReactDOM.render()触发 ——————初次渲染
    1. constructor ————构造器【数据初始化(state)】
    2. getDerivedStateFromProps ————从 props 得到一个派生的状态
    3. render ————渲染【常用】
    4. componentDidMount ————挂载完毕【常用】
      • 做一些初始化的事情:发起网络请求、开启定时器、订阅消息
  • 更新阶段: 由组件内部 this.setState() 或父组件重新 render 触发【强制更新 this.forceUpdate() : 强制更新数据钩子 ——————不走阀门】
    1. getDerivedStateFromProps ————从 props 得到一个派生的状态
    2. shouldComponentUpdate ————控制更新的阀门
    3. render ————渲染
    4. getSnapshotBeforeUpdate ————在更新之前得到快照
    5. componentDidUpdate ————更新完毕
  • 卸载组件:由ReactDOM.unmountComponentAtNode(document.getElementById(‘’))触发
    1. componentWillUnmount ————将要卸载【常用】
      • 做一些收尾的事情:关闭定时器、取消订阅消息
  • static getDerivedStateFromProps(){} : 从 props 得到一个派生的状态
    • 此方法适用于罕见的用例,state 的值在任何时候都取决于 props
    • 必须有返回值 state对象 或者 null
  • getSnapshotBeforeUpdate : 在更新之前得到快照
    • 必须有返回值 snapshot值【快照值】 或者 null
      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
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      83
      84
      85
      86
      87
      88
      89
      <div id="app"></div>
      <script src="./js/react.development.js"></script>
      <!-- 用于支持React操作DOM -->
      <script src="./js/react-dom.development.js"></script>
      <!-- 用于jsx转js -->
      <script src="./js/babel.min.js"></script>
      <!-- 用于对属性标签数据限制 -->
      <script src="./js/prop-types.js"></script>
      <script type="text/babel">
      //#region
      // 生命周期回调函数 == 生命周期钩子函数
      //#endregion
      class Life extends React.Component {
      // 构造器
      constructor(props) {
      console.log('constructor');
      super(props)
      this.state = {
      num: 0,
      }
      }


      // 卸载组件按钮的回调
      unMount = () => {
      ReactDOM.unmountComponentAtNode(document.getElementById('app'))
      }

      // 正常更新数据按钮的回调
      upData = () => {
      let { num } = this.state
      num += 1
      this.setState({
      num
      })
      }
      // forceUpdate()
      // 强制更新数据钩子 ——————不走阀门
      // 不对状态进行任何修改,更新一下
      // 强制更新按钮的回调
      force = () => {
      this.forceUpdate()
      }
      // 从 props 得到一个派生的状态
      // 必须有返回值 state对象 或者 null
      // 此方法适用于罕见的用例,state 的值在任何时候都取决于 props
      static getDerivedStateFromProps(props,state){
      console.log('getDerivedStateFromProps',props,state);
      return null
      }
      // 在更新之前得到快照
      // 必须有返回值 snapshot值【快照值】 或者 null
      getSnapshotBeforeUpdate(prevProps, prevState){
      console.log('快照,getSnapshotBeforeUpdate',prevProps, prevState);
      return '123123'
      }
      // 组件挂载完毕
      componentDidMount() {
      console.log('组件挂载完毕,componentDidMount');
      }
      // 组件将要卸载
      componentWillUnmount() {
      console.log('组件将要卸载,componentWillUnmount');
      }
      // 控制组件更新的阀门 ————————必有返回值【不去设置都是true】【布尔值】
      shouldComponentUpdate() {
      console.log('控制组件更新的阀门,shouldComponentUpdate');
      return true
      }

      // 组件更新完毕
      componentDidUpdate(preProps,preState,snapshotValue) {
      console.log('组件更新完毕,componentDidUpdate',preProps,preState,snapshotValue);
      }
      render() {
      console.log('渲染,render');
      let { num } = this.state
      return (
      <div>
      <h2>当前求和为{num}</h2>
      <button onClick={this.upData}>点我加1</button>
      <button onClick={this.unMount}>卸载组件</button>
      <button onClick={this.force}>不更改状态中的数据,强制更新</button>
      </div>
      )
      }
      }
      ReactDOM.render(<Life num={99} />, document.getElementById('app'))
      </script>

      getSnapshotBeforeUpdate 应用场景:计算滚动位置

      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
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      <!DOCTYPE html>
      <html lang="en">

      <head>
      <meta charset="UTF-8">
      <title>demo</title>
      </head>
      <style>
      .list {
      width: 200px;
      height: 150px;
      background-color: royalblue;
      overflow-y: auto;
      }

      .news {
      height: 30px;
      }
      </style>
      <body>
      <div id="app"></div>
      <script src="./js/react.development.js"></script>
      <!-- 用于支持React操作DOM -->
      <script src="./js/react-dom.development.js"></script>
      <!-- 用于jsx转js -->
      <script src="./js/babel.min.js"></script>
      <!-- 用于对属性标签数据限制 -->
      <script src="./js/prop-types.js"></script>
      <script type="text/babel">
      //#region
      // 生命周期回调函数 == 生命周期钩子函数
      //#endregion
      class NewsList extends React.Component {
      state = {
      newsArr: [],
      }
      clear = () => {
      clearInterval(this.timer)
      }
      componentDidMount() {
      this.timer = setInterval(() => {
      let { newsArr } = this.state
      // 模拟一条新闻
      let news = '新闻' + (newsArr.length + 1)
      newsArr.unshift(news)
      this.setState({
      newsArr
      })
      }, 1000);
      }
      getSnapshotBeforeUpdate() {
      return this.list.scrollHeight
      }
      componentDidUpdate(preProps, preState, snapshotValue) {
      // this.list.scrollHeight 每次比 snapshotValue 多 30px
      this.list.scrollTop += this.list.scrollHeight - snapshotValue
      }
      render() {
      return (
      <div>
      <div className="list" ref={c => this.list = c}>
      {
      this.state.newsArr.map((item, index) => {
      return <div key={index} className='news'>{item}</div>
      })
      }
      </div>
      <button onClick={this.clear}>清除定时器</button>
      </div>
      )
      }
      }
      ReactDOM.render(<NewsList />, document.getElementById('app'))
      </script>
      </body>
      </html>

Diffing算法

验证Diffing算法

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 Time extends React.Component {
state = {date : new Date()}
componentDidMount(){
setInterval(() => {
this.setState({
date: new Date()
})
}, 1000);
}
render() {
// span标签不断变化,外层标签不会受影响,内层input标签也没有影响
return (
<div>
<h1>Hellow</h1>
<input type="text" />
<span>
现在是:{this.state.date.toTimeString()}
<input type="text" />
</span>
</div>
)
}
}
ReactDOM.render(<Time />, document.getElementById('app'))

key的作用

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
46
47
48
49
50
51
52
53
54
<div id="app"></div>
<script src="./js/react.development.js"></script>
<!-- 用于支持React操作DOM -->
<script src="./js/react-dom.development.js"></script>
<!-- 用于jsx转js -->
<script src="./js/babel.min.js"></script>
<!-- 用于对属性标签数据限制 -->
<script src="./js/prop-types.js"></script>
<script type="text/babel">
//#region
// Diffinf算法,逐层对比,最小力度是标签
// 经典面试题:
// 1. react/vue 中的key有什么作用?(key的内部原理?)
// 2. 为什么遍历数组时,key最好不用index?
//#endregion
class Person extends React.Component {
state = {
persons: [
{ id: 1, name: '李四', age: 18 },
{ id: 2, name: '张三', age: 20 }
]
}
addData = () => {
// this.state.persons.push({
// id: 3, name: '王五', age: 19
// })
this.setState({persons:[{id: 3, name: '王五', age: 19},...this.state.persons]})
}
render() {
return (
<div>
<h1>展示人员信息</h1>
<button onClick={this.addData}>添加数据</button>
<ul>
{
this.state.persons.map((item,index) => {
return <li key={index}>姓名:{item.name},年龄:{item.age}<input type="text" /></li>
})
}
</ul>
<hr/>
<ul>
{
this.state.persons.map((item,index) => {
return <li key={item.id}>姓名:{item.name},年龄:{item.age}<input type="text" /></li>
})
}
</ul>
</div>
)
}
}
ReactDOM.render(<Person />, document.getElementById('app'))
</script>

-------------本文结束感谢您的阅读-------------

我的传送门:个人网站GithubGitee