React进阶之路——React基础

本博客pdf链接: https://pan.baidu.com/s/1E_E4RqZUPgQiby1T7Afj8Q 提取码: h77d

React进阶之路系列学习笔记:React进阶之路系列学习笔记

github源码:React_learning

2.1 JSX

2.1.1JSX简介

    JSX是一种用于描述UI的JavaScript扩展语法,React使用这种语法描述组件的UI。

2.1.2JSX语法

1.基本语法

JSX的基本语法和XML语法相同,都是使用成对的标签构成一个树状结构的数据。

const element = (
   


       

Hello, world!


   

)

2.标签类型

两种标签类型:

(1)DOM类型的标签(div,span等), 使用时,标签首字母必须小写

(2)React组件类型的标签,使用时,组件名称的首字母必须大写

React通过首字母的大小写判断渲染的是DOM类型标签还是React组件类型标签。

3.JavaScript表达式

JSX的本质是JavaScript。在JSX中使用JSX表达式需要用{}将表达式包起来。

表达式使用主要场景:

  1. 通过表达式给标签属性赋值
const element = 
  1. 通过表达式定义子组件

 

const todos = ['item1', 'item2', 'item3'];

const element = {

    
            {todos.map(message => )}    
};

=》是ES6中定义函数的方式。map是一种数组遍历方式,message是箭头函数的参数,key是索引,message是对应todos的值。这里将数组todos依次遍历在HTML代码中展示。

另外,JSX中只能使用JS表达式,不能使用多行JS语句。JSX可以使用三目运算符或者逻辑与(&&)运算符代替if语句。

4.标签属性

1.当JSX标签是DOM类型的标签时,对应的DOM标签支持的属性JSX也支持,部分属性名称有所变化,例如class写成className,之所以改变是因为React对DOM标签支持的事件重新做了封装。事件属性名称采用驼峰格式。

2.当JSX标签是React组件类型时,可以自定义标签的属性名。

5.注释

JSX的注释需要大括号”{}”将/**/包裹起来。

2.1.3 JSX是必需的

在《React进阶之路》一书中提到JSX不是必需的。

2.2 组件

2.2.1组件定义

定义组件的两种方式:使用ES6 class(类组件)和使用函数(函数组件)。

使用class定义组件满足的两个条件:

  1. class继承自React.Component
  2. class内部必须自定义render方法,render方法返回代表该组件UI的React元素。

使用ReactDom.render()将PostList挂载到页面的DOM节点上,在使用ReactDOM.render()需要先导入react-dom库,这个库会完成组件所代表的虚拟DOM节点到浏览器的DOM节点的转换。

import React from "react";

import ReactDOM from "react-dom";

import PostList from "./PostList";



ReactDOM.render(, document.getElementById("root"));

 

2.2.2组件的props

组件的props用于把父组件中的数据或方法传递给子组件,供子组件使用。

Props是一个简单结构的对象,它包含的属性正是由组件作为JSX标签使用时的属性组成。 下面是一个使用User组件作为JSX作为JSX标签的声明:





props = {

    name: 'React',

    age: '4',

    address: 'America'

}

 

  {data.map(item =>

    

  )}

这是类组件PostList中使用PostItem的代码,对data数组中的元素遍历一遍,然后返回大小相同的数组(其中包含了title,author,date属性),最终在UI展示。

2.2.3组件的state

组件的state是组件内部的状态,state的变化最终将反映在组件UI的变化上。在构造方法constructor中通过this.state定义组件的初始状态,并调用this.setState方法改变组件的状态。

constructor(props){

    super(props);

    this.state = {

        vote: 0

    };

}

///处理点赞逻辑

handleClick(){

    let vote = this.state.vote;

    vote++;

    this.setState({

        vote:vote

    });

}
  1. constructor内首先调用super(props),这一步实际上是调用React.Component这个class的constructor方法来完成React组件的初始化;
  2. constructor通过this.state定义组件的初始状态;
  3. render方法中定义了处理点击事件的响应函数,响应函数内部会调用this.setState更新组件点赞数。

UI = Component(props,state)

React组件是通过props和state两中数据驱动渲染出组件UI。Props是组件对外的接口,它是只读的,组件通过props接受外部传入的数据;state是组件对内的接口,它是可变的,组件内部状态通过state来反映。

2.2.4有状态组件和无状态组件

state是用来反映组件内部状态的变化,如果一个组件的内部状态是不变的,这样的组件称为无状态组件;反之称为有状态组件。

    定义无状态组件除了使用ES6 class方式外,还可以用函数定义。一个函数组件接收props作为参数,返回代表这个组件UI的React元素结构。

function Welcome(props){

    return 

Hello, {props.name}

; }

    有状态组件主要关注状态业务变化的业务逻辑,无状态组件主要关注组件UI的渲染。

2.2.5属性校验和默认属性

React提供了ProTypes这个对象,用于校验组件属性的类型。ProTypes包含组件属性所有可能的类型,通过定义一个对象(对象的key是组件属性名,val是对应属性类型)实现组件属性类型的校验。

想要知道一个对象的结构或数据元素的类型,比较好的做法是使用ProTypes.shape或Protypes.arrayof。 如果属性是组件的必需属性,也就是使用某个组件时必须传入的属性,就要爱ProTypes的类型属性上调用isRequired。

PostItem.propTypes = {

  post: PropTypes.shape({

    id: PropTypes.number,

    title: PropTypes.string,

    author: PropTypes.string,

    date: PropTypes.string,

    vote: PropTypes.number

  }).isRequired,

  onVote: PropTypes.func.isRequired

}

    如上面代码,在PostItem组件中post和onVote是必需属性。

React还提供了为组件属性指定默认值的特性,这个特性通过组件的defaultProps实现。

2.2.6组件样式

1. 外部CSS样式表【首选】

    此方式和平时开发Web应用时使用外部CSS文件相同,CSS样式表中根据HTML标签类型、ID、class等选择器定义元素样式。唯一区别:React要用className代替class作为选择器。

样式表的引入方式:

  1. 使用组件的HTML页面中通过标签引入
  2. 把样式表文件当做一个模块,在使用该样式表的组件中,像导入其他组件一样导入样式表文件

注意class名称冲突

2. 内联样式

2.2.7组件和元素

React元素是一个普通的JS对象,这个对象通过DOM节点或React组件描述界面是什么样子的。JSX语法就是用来创建React元素的。

   

Button是一个自定义的React组件。

React组件是一个class或函数,它接收一些属性作为输入,返回一个React元素。React组件是由若干元素组件而成。下面的例子可以解释React组件与React元素的关系:

class Button extends React.Component{

    render(){

        return ();

    }

}



//在JSX中使用组件Button,button表示组件Button的一个react元素

const button = 

 

2.3 组件的生命周期

2.3.1挂载阶段

这个阶段组件被创建,执行初始化,并被挂载到DOM中,完成组件的第一次渲染。依次调用的生命周期方法有:constructor,componentWillMount,render,componentDidMount。

  1. constructor

这是ES6class的构造方法,组件被创建时,会先调用组件的构造方法。如果父组件没有传入属性而组件自身定义了默认属性,那么参数props指向的是组件的默认属性。Constructo通常用于初始化组件的state以及绑定时间处理方法等工作。

  1. componentWillMount

这个方法在组件被挂载到DOM前调用,且只会被调用一次。在实际项目中很少会用到,因为可以将该方法提前到constructor中执行。在这个方法中调用this.setState不会引起组件的重新渲染。

  1. render

这是定义组件时唯一必要的方法(组件的其他生命周期方法都可以省略)。在这个方法中,根据组件的props和state返回一个React元素,用于描述组件的UI,通常React元素使用JSX语法定义。注意:render并不负责组件的实际渲染工作,它只是返回一个UI的描述,真正的渲染出页面DOM的工作由React自身负责。Render是一个纯函数,在这个方法中不能执行任何有副作用的操作。

  1. componentDidMount

在组件被挂载到DOM后调用,且只会被调用一次。这时候已经可以获取到DOM结构了。这个方法通常还会用于向服务器端请求数据。在这个方法中调用this.setState会引起组件的重新渲染。

2.3.2更新阶段

组件被挂载到DOM后,组件的props或state可以引起组件更新。props引起的组件更新,本质上是由渲染该组件的父组件引起的,也就是父组件的render方法被调用时,组件会发生更新过程,这个过程无论props是否改变,父组件render方法每一次调用,都会导致组件更新。State引起的组件更新,是通过调用this.setState修改组件 state触发的。组件更新阶段依次调用的声明周期方法有:compoentWillReceiveProps、shouldComponentUpdate、componentWillUpdate、render和componentDidUpdate。

  1. compoentWillReceiveProps(nextProps)

这个方法只有在props引起的组件更新过程中,才会被调用。State引起的组件更新并不会触发该方法的执行。方法的参数nextProps是父组件传递给当前组件的新的props。因此往往需要比较nextProps和this.props来决定是否执行props发生变化后的逻辑,比如根据新的props调用this.setState触发组件的重新渲染。

  1. shouldComponentUpdate(nextProps,nextState)

这个方法决定组件是否继续执行更新过程。方法返回true时,组件继续更新过程;返回false时,更新过程停止。后续的方法也不会再被调用给你。一般通过比较nextProps、nextState和当前组件的props、state决定这个方法的返回结果。这个方法可以用来减少组价不必要的渲染从而优化组件性能。

  1. componentWillUpdate(nextProps,nextState)

这个方法在组件render调用前执行,可以作为组件更新发生前执行,某些工作的地方,一般也很少用到。

  1. componentDidUpdate(prevProps,prevState)

组件更新后被调用,可以作为操作更新下后DOM的地方。这个方法的两个参数分别代表组件更新前的props和state。

2.3.3卸载阶段

组件从DOM中被卸载的过程,这个过程只有一个生命周期方法:componentWillUnMount。 这个方法在组件被卸载之前调用,可以在这里执行一些清理工作,比如清理组件中使用的定时器等等,避免引起内存泄露。

最后注意:只有类组件具有生命周期方法,函数组件是没有生命周期方法的

2.4 列表和keys

①当列表数据为多组时,调用数据运行时为提示警告(浏览器F12->Console):” Each child in an array or iterator should have a unique ‘ key ’ prop ”  说明如果没有给每条数据添加一个’ key ‘ 的话,当数据组数过多时,不同组数有相同的数据,调用就可能会出现错误。因此这里的’ key ’ 就相当于数据库里的主键,给每组数据调取都有一个参照。

 

帖子列表

 
        {this.state.posts.map(item =>           )}  

②虽然列表元素的key不能重复,但这个唯一性仅限至于在当前列表中,而不是全局唯一。

③不推荐使用索引作为key,因为一旦列表中的数据发生重排,数据的索引也会发生变化,不利于React的渲染优化。

2.5 事件处理

1.React元素绑定事件有两点注意事项:

①在React中,事件的命名采用驼峰的形式(两个或多个单词组成的事件第一个单词首字母小写,其余的都要大写),而不是DOM元素中的小写字母命名方式。如:JavaScript里的onclick要改为onClick、onchange要改为onChange。

②处理事件的响应函数要以对象的形式赋值给事件属性,而不是DOM中的字符串形式。

如:在DOM中绑定一个点击事件是这样(传统的js写法):

而在React元素中绑定一个点击事件变成这种形式:

class App extends Component {

    clickButton(){}; //clickButton函数

    render() {

        return (

            
               
                 onclick={ this.clickButton }> //clickButton是一个函数

                    Click

                

            
        )     } }

2.React事件处理函数的三种写法:
    ①使用箭头函数(以下几种情况不使用箭头函数

https://www.jianshu.com/p/1357112985ef  )

class ...... {

    constructor(props){

        super(props);

        this.state = { number:0 }

    }

    ......

    

}

②使用组件方法(直接将组件的方法赋值给元素的事件属性,同时在类的构造中,将这个方法的this绑定到当前对象)

class ...... {

    constructor(props){

        super(props);

        this.state = { number:0 };

        this.handleClick = this.handleClick.bind(this);

    }

    handleClick(event){

    const number = ++this.state.number;

    this.setState({

        number:number;

    })

    }

}

    ......

    
{ this.state.number }
    }

③属性初始化语法(property initializer syntax) (使用ES7的property initializers会自动为class中定义的方法绑定this)

handleClick = (event) => {

    const number = ++this.state.number;

    this.setState({

        number:number

    })

}

三种方法进行比较:第一种每次调用render时,会重新创建一个新的事件处理函数,带来额外的性能开销;第二种构造函数中需要为事件处理函数绑定this,多个处理事件时,代码就会显得繁琐;第三种为推荐使用方法,这种方式既不需要在构造函数中手动绑定this,也不需要担心组件重复渲染导致的函数重复创建问题。

2.6表单

2.6.1 受控组件

如果一个表单元素的值是由React来管理的,那它就是一个受控组件对于不同的表单元素,React的控制方式略有不同。下面展示一下三种长用的表单元素的控制方式。

1.文本框 文本框包含类型为text的input元素和textarea元素。它们受控的主要原理是:通过表单元素的value属性设置表单元素的值,通过表单元素的o'nChange事件监听值的变化,并将变化同步到React组件的state中。

import React,{Component} from 'react';

export default class Demo1 extends Component{

    constructor(props){

        super(props);

        this.state = {name:'',password:''};

        handleChange(event){ //监听用户名和密码两个input值的变化

            const target = event.target;

            this.setState({[target.name]: target.value});

        }

        handleSubmit(event){ //表单提交的响应函数

            console.log('login successfully');

            event.preventDefault();

        }

    }

    render(){

        return(

            
                                                           
        )     } }

用户名和密码两个表单元素的值是从组件的state中获取的,当用户更改表单元素的值时,onChange事件会被触发,对应的handleChange处理函数会把变化同步到组件的state,新的state又会触发表单元素重新渲染,从而实现表单元素状态的控制。

  1. 列表 列表select元素时最复杂的表单元素,他可以用来创建一个下拉表
    在React中,通过在select上定义value属性来决定哪一个option元素处于选中状态。这样,对select的控制只需要在select这一个元素上修改即可,不需要再关注option元素
  2. 复选框和单选框  复选框的类型为checkbox的input元素,单选框为radio的input元素。复和单选框的值时不变的,需要改变的是他们的checked状态。React控制的属性变为checked
     

 

 

P48 代码类图及关系的描述

 

PostItem和PostList都继承于Component,两个类之间相互关联
PostList渲染的是

{this.state.posts.map(item => 

     )}


onVote这里用到自己定义的方法handleVote(id),根据帖子的id进行过滤,找到待修改vote属性的帖子,返回新的posts对象,然后更新state。
onSave这里用到自己定义的方法handleSave(post),但用到PostItem中的属性prop,根据post的id,过滤出当前要更新的post
PostItem渲染的是标题(通过handleTitleChange方法处理