# chapter1-React基本语法

# 预习视频

【最新】零基础快速入门React 17.x (opens new window)

视频P1-P20

# 今日学习目标

  • 了解React的MVC设计理念
  • 能够使用脚手架创建React项目
  • 掌握JSX语法
  • 掌握组件化开发

# 一、React简介【了解】

React 是一个用于构建用户界面的 JavaScript 库,它是 Facebook 的内部项目,用来架设 Instagram 的网站,并于 2013 年 5 月开源。React 主要用于构建UI,很多人认为 React 是 MVC 中的 V(视图)。由于拥有较高的性能,且代码逻辑非常简单,越来越多的人已开始关注和使用它。

中文官网:https://react.docschina.org

# 二、React特点【了解】

1、声明式设计

​ react是面向数据编程,不需要直接去控制dom,你只要把数据操作好,react自己会去帮你操作dom,可以节省很多操作dom的代码。这就是声明式开发。

2、使用JSX语法

​ JSX 是 JavaScript 语法的扩展。React 开发大部分使用 JSX 语法(在JSX中可以将HTML于JS混写)。

3、灵活

​ react所控制的dom就是id为root的dom,页面上的其他dom元素你页可以使用jq等其他框架 。可以和其他库并存。

4、使用虚拟DOM、高效

​ 虚拟DOM其实质是一个JavaScript对象,当数据和状态发生了变化,都会被自动高效的同步到虚拟DOM中,最后再将仅变化的部分同步到DOM中(不需要整个DOM树重新渲染)。

5、组件化开发

​ 通过 React 构建组件,使得代码更加容易得到复用和维护,能够很好的应用在大项目的开发中。

6、单向数据流

​ react是单向数据流,父组件传递给子组件的数据,子组件能够使用,但是不能直接通过this.props修改。 这样让数据清晰代码容易维护。

# 三、创建React脚手架项目【熟悉】

# 1、构建一个名为 demo 项目

# React不再支持全局安装create-react-app,因此官方建议使用npx来安装
npx create-react-app demo
1
2

注意:项目路径不能有中文,不能有怪异符号,包括“!”也不行!!!!

如图,创建成功:

![](https://tva1.sinaimg.cn/large/008eGmZEgy1gpnpektjcej31040ok41e.jpg)

如果安装过于缓慢,或者有报错,请参考下文:

https://www.jianshu.com/p/91bf816cc1cc

# 2、进入这个文件夹,并且跑起来

cd demo && yarn start
1

项目运行,自动打开谷歌浏览器看见项目页面(第一次项目运行会很慢,要耐心等待)

# 四、hello_world程序编写【熟悉】

1、删除在src目录下所有文件

2、src目录下新建index.js文件作为入口文件

//1、引入React两个核心模块
import React from 'react';
import ReactDOM from 'react-dom';


//2、通过JSX语法将组件/标签渲染到指定标签上
ReactDOM.render(
    <h1>
        hello world!
    </h1>
    , document.getElementById('root'));
1
2
3
4
5
6
7
8
9
10
11

ReactDOM.render(参数1,参数2)

参数1 是JSX语法的标签/组件

参数2 是要把参数1这个标签渲染到的位置

# 五、VSCode中JSX的编写优化【了解】

在vscode中的 文件>首选项>设置 中,直接搜索 include Language ,进入 settings.json

找到 "emmet.includeLanguages" 字段,添加:

"emmet.includeLanguages": {
  "javascript": "javascriptreact" 
}
1
2
3

搜索 trigger ,找到如图位置,打钩:

设置后就可以通过 标签名+Tab 键快速码出标签

# 六、ES5与ES6面向对象复习【重要】

# 1、ES5封装与继承

我们声明一个构造函数:

function Animal(vname){
    this.name = vname;
    this.sayInfo = function(){
        console.log('这是一只'+this.name)
    }
}

var animal = new Animal('动物')
animal.sayInfo();		// 这是一只动物
1
2
3
4
5
6
7
8
9

现在我想用一个Dog类来继承这个Animal类,那么就可以:

function Animal(vname){
    this.name = vname;
    this.sayInfo = function(){
        console.log('这是一只'+this.name)
    }
}

function Dog(vname){
  // 在子类型构造函数的内部调用超类型构造函数,通过使用apply()和call()方法可以在新创建的对象上执行构造函数
  Animal.call(this, vname);
}

var dog = new Dog('狗狗');
dog.sayInfo();	// 这是一只狗狗
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 2、ES6封装与继承

我们声明一个类:

class Animal {
    // 构造器
    constructor(vname){
        this.name = vname;
    }
    sayInfo(){
        console.log('这是一只'+this.name);
    }
}

let animal = new Animal('动物');
animal.sayInfo();
1
2
3
4
5
6
7
8
9
10
11
12

再定义一个Dog类来继承Animal类:

class Animal {
    // 构造器
    constructor(vname){
        this.name = vname;
    }
    sayInfo(){
        console.log('这是一只'+this.name);
    }
}

class Dog extends Animal {
    constructor(props){
        // 超类,接收父类传递过来的属性
        super(props)
    }
}

let dog = new Dog('狗狗');
dog.sayInfo()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 七、组件化开发【重要】

在React的项目中,都是使用组件化开发的模式,所以,我们可以把刚才的hello world的h1定义为一个组件:

定义组件分为3步:

​ 1、导入React核心模块

​ 2、定义组件类

​ 3、导出组件

在src目录下新建App.js文件:

//1、导入React核心模块
import React from 'react'

//2、定义组件类
class Hello extends React.Component{   //类
    render(){     //函数
        return (   //返回值
            <div>
                hello world !!! 我是组件222
            </div>
        )
    }
}


//3、导出组件
export default Hello
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

在需要引入该组件的index.js中,导入,就可以直接使用这个组件了:

//import 组件名 from '文件路径'
import Hello from './App'   //1、导入Hello组件  (首字母必须大写)

ReactDOM.render(
    <Hello />        // 2、使用Hello组件  (首字母必须大写)   
    , document.getElementById('root'));

//注意:Hello是组件名,在使用的时候就应该写JSX标签写法,而不能直接写Hello
1
2
3
4
5
6
7
8

!!注意:

1、定义组件的时候,return 后面只能有一个根标签,不能有多个,但这个标签内部可以有其他多个标签

2、使用组件的时候,首字母必须大写

3、如果最外层实在不想放置一层div根目录,可以使用 <></> 空标签代替

# 八、JSX语法糖【重要】

React 使用 JSX 来替代常规的 JavaScript。JSX 是一个看起来很像 XML 的 JavaScript 语法扩展,在React中会被babel编译为JavaScript。

# 1、JSX的特点

  • JSX 执行更快,因为它在编译为 JavaScript 代码后进行了优化。
  • 它是类型安全的,在编译过程中就能发现错误。
  • 使用 JSX 编写模板更加简单快速。

# 2、JSX几个注意的格式:

1、React的JSX是使用大写和小写字母来区分本地组件和HTML组件

(如:html就用 div p h1 , 组件就用 MyButton App Home List等 )

2、JSX和html的标签属性的区别

HTML标签属性 JSX 原因
for htmlFor for在JS中为for循环关键字
class className class在JS中为声明类关键字
style 需使用JS对象(使用双花括号--{{}})

组件中:

import React, { Component } from 'react'
var MyImg = require('./assets/01.jpg')

export default class App2 extends Component {
    render() {
        return (
            <>
                <label htmlFor="ipt">label</label>
                <input type="text" id="ipt" className="ipt" style={{background: 'pink', color: 'green'}} />
                <hr/>
                <img src={MyImg} alt=""/>
            </>
        )
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

注意:图片查找路径在src目录,所以引入的时候从src目录触发查找文件

# 3、React的JSX创建出来的是虚拟DOM,而不是HTML

在index.js中:

import React from 'react'
import ReactDOM from 'react-dom'
import App from './App3'

ReactDOM.render(
    <App />, 
    document.getElementById('root')
);

const virtualDOM = React.createElement('div')
const realDOM = document.createElement('div')

let virtualNum = 0;
for(var key in virtualDOM){
    virtualNum++;
}

let realNum = 0;
for(var key in realDOM){
    realNum++;
}

console.log('虚拟DOM的属性个数:'+virtualNum)	// 7
console.log('真实DOM的属性个数:'+realNum)		// 298
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 4、JSX变量引用、三目运算符、for循环

在JSX中,想要调用变量,需要在return中直接使用单花括号--**{}**调用

index.js中:

//第四节:JSX中变量引用、三目运算符、for循环的使用
import App3 from './App3'

//2、通过JSX语法将标签/组件渲染到指定标签上
ReactDOM.render(
    <App3 />
    , document.getElementById('root'));
1
2
3
4
5
6
7

App3.js中

import React, { Component } from 'react'

let name = "小明", num1=20, num2=30, arr=[1, 2, 3, 4, 5]

export default class App3 extends Component {
  render() {
    return (
      <div>
        {/* 这是注释的格式 */}
        {/* JSX中引用变量需要加单花括号 */}
        <p>{name}</p>

        {/* 三目运算符的使用 */}
        <p>num1和num2中比较大的是:{num1>num2? num1: num2}</p>
        <p>{gender === 0 ? '男' : (gender === 1 ? '女' : '保密')}</p>

        {/* for循环的使用 */}
        <ul>
          {/* 数组名.map(函数) */}
          {
            //格式1:
            arr.map((v,k)=>(
              <li key={k}>{v}</li>
            ))
          }
          {
            //格式2:可以把下面的大括号和return换成小括号
            arr.map((v,k)=>{    
              return <li key={k}>{v}</li>
            })
          }
        </ul>

      </div>
    )
  }
}
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

总结:在jsx语法中,需要书写js代码的时候,请先加上{ } 再书写js语法

# 九、VSCode中,代码片段扩展的安装【了解】

在扩展搜索栏中搜索react,选择下图的扩展进行安装

02

安装后,通过 rcc+Tab ,得到组件的代码片段, clg+回车 快速得到console.log()代码

# 十、事件、state与setState【重要】

App5.js中:

//事件讲解

import React, { Component } from 'react'

//1、实现点击弹框效果(事件基本格式)
export default class App5 extends Component {
    handleClick(){  
        alert(132456)
    }

    render() {
        return (
            <div>
                <button onClick={this.handleClick}>按钮</button>
            </div>
        )
    }
}


//2、实现累加的功能 (状态的使用1)
export default class App4 extends Component {
    constructor(props){
        super(props)
        this.state = {
            num: 0
        }
        // 按钮3的函数写法的前提
        this.handleClick = this.handleClick.bind(this);
    }
    handleClick(){
        this.setState({
            num: this.state.num+1
        })
    }
    render() {
        return (
            <div>
                <h2>{ this.state.num }</h2>
                {/* 通过bind来改变this的指向 */}
                {/* <button onClick={this.handleClick.bind(this)}>按钮1</button> */}
                {/* 箭头函数默认没有this,所以this指向数组对象 */}
                {/* <button onClick={()=>this.handleClick()}>按钮2</button> */}
                <button onClick={this.handleClick}>按钮3</button>
            </div>
        )
    }
}


//3、实现双向数据绑定 (状态的使用2)
export default class App5 extends Component {
    constructor(p){
        super(p)
        this.state = {
            name: "你好,世界"
        }
    }
    handleChange(e){
        console.log(e.currentTarget.value)
        console.log(e.target.value)
        this.setState({
            name: e.target.value
        })
    }
    render() {
        return (
            <div>
                <h2>{ this.state.name }</h2>
                <input type="text" value={this.state.name} onChange={this.handleChange.bind(this)} />
            </div>
        )
    }
}

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

# 十一、state的简写【熟悉】

App6.js中:

import React, { Component } from 'react'

export default class App6 extends Component {
  	// 写在construtor中的state也可以简写
    state = {
        username: "张三"
    }
    render() {
        return (
            <div>
                <h1>{this.state.username}</h1>
                <button onClick={this.handleClick.bind(this)}>按钮</button>
            </div>
        )
    }
    handleClick(){
        this.setState({
            username: "李四"
        })
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 十二、setState是异步的【熟悉】

我们可以通过以下代码验证 setState 是异步或是同步:

import React, { Component } from 'react'

export default class App5 extends Component {
    state = {
      msg: "你好世界"
    }
    render() {
        return (
            <div>
                <h2>{this.state.msg}</h2>
                <button onClick={this.handleClick.bind(this)}>改变msg</button>
            </div>
        )
    }
    handleClick() {
        // this.setState是异步还是同步呢?
        this.setState({
            msg: "hello world"  // 修改msg
        })
        console.log(this.state.msg) // 同步则输出“hello world”,异步则输出“你好世界”

        // 验证结果:this.setState是异步的
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

另外 setState 还有回调函数:

this.setState({
  msg: "hello world"
}, ()=>{
  console.log(this.state.msg);    // hello world
})
1
2
3
4
5

如果想要在修改数据后调用该数据,就可以使用回调函数。

# 十三、累加【重要】

我们来实现一个累加的效果:

点击按钮则数字加1

import React, { Component } from 'react'

export default class App7 extends Component {
    state = {
        num: 0
    }
    render() {
        return (
            <div style={{textAlign: 'center'}}>
                <h2>{this.state.num}</h2>
                <button onClick={this.addNum.bind(this)}>累加</button>
            </div>
        )
    }
    // 累加
    addNum(){
        // num++ (num = num + 1) num++是会修改num的
        // this.state.num++      this.state.num被修改了
        // 唯一能修改state的方法是setState
        this.setState({
            num: this.state.num+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

这里考点在于 ++ 会修改原数据,而在 react 中,修改state中的数据必须通过setState,因此这里不能对 this.state.num 直接进行 ++ 操作。

# 十四、双向数据绑定【重要】

React中没有类似vue的 v-model 指令,因此要实现双向数据绑定,只能操作 valueonChange(或onInput)事件

import React, { Component } from 'react'

// 双向数据绑定

export default class App8 extends Component {
    state = {
        msg: "你好世界"
    }
    render() {
        return (
            <div>
                <input type="text" value={this.state.msg} onChange={this.changeFn.bind(this)} />
                <h2>{this.state.msg}</h2>
            </div>
        )
    }
    // input的输入事件
    changeFn(e){
        /* 
            currentTarget与target有什么区别?

            当只有一层元素的时候,两者没区别
            当存在两层元素嵌套时,就会有冒泡现象,那么真正指向被你事件触发的元素,是currentTarget
        */
        // console.log(e.currentTarget)
        // console.log(e.target)

        this.setState({
            msg: e.currentTarget.value
        })
    }
}
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

这里做个补充:

如果input上写的是箭头函数,那么应当这么写,才能获取到事件对象e:

<input type="text" value={this.state.msg} onChange={(e)=>this.changeFn(e)} />
1

# 十五、state中数组的修改【重要】

我们对数组进行操作时,大部分数组方法会修改到原数组,因此建议先将数组深拷贝一次,修改后再重新赋值:

import React, { Component } from 'react'

export default class App9 extends Component {
    state = {
        arr: ["张飞", "赵云", "刘备"]
    }
    render() {
        return (
            <div>
                <ul>
                    {
                        this.state.arr.map((item, index)=>{
                            return <li key={index}>{item}</li>
                        })
                    }
                </ul>
                <button onClick={this.addFn.bind(this)}>追加一项到arr的最后</button>
            </div>
        )
    }
    // 追加一项到arr的最后
    // 修改state的方式只能是setState
    // 数组的push方法会修改原数组
    // this.state.arr.push() 会造成直接修改了state中的arr
    // 修改数组,不能直接修改this.state.arr
    
    addFn(){
        // 如果我声明一个变量,等于this.state.arr,并且让这个变量和this.state.arr完全脱离关系[深拷贝]
        let newArr = JSON.parse(JSON.stringify(this.state.arr));
        // 往newArr中push一项
        newArr.push('关羽');
        // 把最新的newArr赋值给arr
        this.setState({
            arr: newArr
        })
    }
}
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

# 十六、【验证】为什么修改state只能通过setState【了解】

只有setState可以重新触发render函数对页面进行再次渲染,如果直接对state进行修改,那么是无法显示到界面中的,因此想要修改state,只能通过setState。

import React, { Component } from 'react'

// 验证为什么修改state只能通过setState

export default class App10 extends Component {
    state = {
        msg: "你好世界"
    }
    render() {
        return (
            <div>
                <h2>{this.state.msg}</h2>
                <button onClick={this.changeMsg.bind(this)}>修改msg</button>
            </div>
        )
    }
    // 修改msg
    changeMsg(){
        // this.setState({
        //     msg: "hello world"
        // })
        this.state.msg = "hello world";
        console.log('修改后的msg:'+this.state.msg)
    }
}
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

# 十七、【验证】key用id好还是index好?【了解】

当然是id好,看代码:

import React, { Component } from 'react'

export default class App11 extends Component {
    state = {
        books: [
            {id: 0, name: '老人与海'},
            {id: 1, name: '西游记'},
            {id: 2, name: '三国演义'}
        ]
    }
    render() {
        return (
            <div>
                <ul>
                    {
                        this.state.books.map((item, index)=>{
                            return (
                                // 失去了唯一标识符的意义
                                <li key={item.id}>
                                    <input type="text" />
                                    <strong>{item.name}</strong>
                                </li>
                            )
                        })
                    }
                </ul>
                <button onClick={this.addItem.bind(this)}>追加一项</button>
            </div>
        )
    }
    addItem(){
        let obj = {id: 3, name: '红楼梦'}
        // 深拷贝
        let newBooks = JSON.parse(JSON.stringify(this.state.books));
        newBooks.unshift(obj)

        this.setState({
            books: newBooks
        })

        // 假如追加的这一项会导致整个数组重排,那怎么办?
        // 如果key使用了index,当数组重排时,就会导致索引对不上
        // 解决方案就是key尽量不适用index作为唯一标识符,尽量使用id
    }
}
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

# 十八、案例:Tab栏【重要】

App7.js中:

import React, { Component } from 'react'
import './com.css';

export default class App7 extends Component {
  state = {
    arr: ["新闻", "体育", "知识"],
    activeNum: 0
  }
render() {
  return (
    <div className="banner">
      <ul>
        {
          this.state.arr.map((item, index) => {
            // this.handleClick.bind(this, 参数)
            return <li key={index} onClick={this.handleClick.bind(this, index)} className={this.state.activeNum==index ? 'active' : ''}>{item}</li>;
          })
        }
      </ul>
      <ol>
        {
          this.state.arr.map((item, index) => {
            return <li key={index} className={this.state.activeNum==index ? 'active' : ''}>{item}</li>;
          })
        }
      </ol>
    </div>
  )
}
handleClick(index){
  // console.log(index)  // 得到索引值
  this.setState({
    activeNum: index
  })
}
}
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

com.css中:

*{margin: 0;padding: 0;border: 0;list-style: none;}

.banner{
    width: 500px;
    height: 300px;
    margin: 100px auto;
    border: 1px solid #000;
}

ul{
    height: 60px;
    line-height: 60px;
    display: flex;
    text-align: center;
    justify-content: space-between;
}

ul li{
    height: 60px;
    width: 30%;
    background: #efefef;
}

ul li.active{
    background: darkred;
    color: #fff;
}

ol li{
    height: 240px;
    background: darkred;
    text-align: center;
    line-height: 240px;
    color: #fff;
    display: none;
}

ol li.active{
    display: block;
}
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

# 十九、作业

# 作业1(初级)

独立完成Tab栏。

# 作业2(中级)

请使用下列数组:

var arr = [
    {id: 1, txt: '6月25日峡谷龙舟赛挑战局数异常修复公告', time: '06/25'},
    {id: 2, txt: '6月25日峡谷龙舟赛挑战局数异常说明', time: '06/25'},
    {id: 3, txt: '6月25日体验服停机更新公告', time: '06/25'},
    {id: 4, txt: '6月24日峡谷龙舟赛异常修复公告', time: '06/24'},
    {id: 5, txt: '6月24日“演员”惩罚名单', time: '06/24'},
    {id: 6, txt: '6月24日外挂专项打击公告', time: '06/24'}
];
1
2
3
4
5
6
7
8

完成以下效果:

# 作业3(高级)

在完成作业2的基础上添加点击删除指定项的操作。

支付宝打赏 微信打赏
Last Updated: 12/16/2021, 5:56:10 PM