本博客pdf链接: https://pan.baidu.com/s/1E_E4RqZUPgQiby1T7Afj8Q 提取码: h77d
React进阶之路系列学习笔记:React进阶之路系列学习笔记
github源码:React_learning
JSX是一种用于描述UI的JavaScript扩展语法,React使用这种语法描述组件的UI。
1.基本语法
JSX的基本语法和XML语法相同,都是使用成对的标签构成一个树状结构的数据。
const element = (
Hello, world!
)
2.标签类型
两种标签类型:
(1)DOM类型的标签(div,span等), 使用时,标签首字母必须小写
(2)React组件类型的标签,使用时,组件名称的首字母必须大写
React通过首字母的大小写判断渲染的是DOM类型标签还是React组件类型标签。
3.JavaScript表达式
JSX的本质是JavaScript。在JSX中使用JSX表达式需要用{}将表达式包起来。
表达式使用主要场景:
const element =
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的注释需要大括号”{}”将/**/包裹起来。
在《React进阶之路》一书中提到JSX不是必需的。
定义组件的两种方式:使用ES6 class(类组件)和使用函数(函数组件)。
使用class定义组件满足的两个条件:
使用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"));
组件的props用于把父组件中的数据或方法传递给子组件,供子组件使用。
Props是一个简单结构的对象,它包含的属性正是由组件作为JSX标签使用时的属性组成。 下面是一个使用User组件作为JSX作为JSX标签的声明:
props = {
name: 'React',
age: '4',
address: 'America'
}
{data.map(item =>
)}
这是类组件PostList中使用PostItem的代码,对data数组中的元素遍历一遍,然后返回大小相同的数组(其中包含了title,author,date属性),最终在UI展示。
组件的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
});
}
UI = Component(props,state)
React组件是通过props和state两中数据驱动渲染出组件UI。Props是组件对外的接口,它是只读的,组件通过props接受外部传入的数据;state是组件对内的接口,它是可变的,组件内部状态通过state来反映。
state是用来反映组件内部状态的变化,如果一个组件的内部状态是不变的,这样的组件称为无状态组件;反之称为有状态组件。
定义无状态组件除了使用ES6 class方式外,还可以用函数定义。一个函数组件接收props作为参数,返回代表这个组件UI的React元素结构。
function Welcome(props){
return Hello, {props.name}
;
}
有状态组件主要关注状态业务变化的业务逻辑,无状态组件主要关注组件UI的渲染。
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实现。
1. 外部CSS样式表【首选】
此方式和平时开发Web应用时使用外部CSS文件相同,CSS样式表中根据HTML标签类型、ID、class等选择器定义元素样式。唯一区别:React要用className代替class作为选择器。
样式表的引入方式:
注意class名称冲突
2. 内联样式
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 = ;
//在组件中使用React元素button
class Page extends React.Component{
render(){
return (
{button}
);
}
}
这个阶段组件被创建,执行初始化,并被挂载到DOM中,完成组件的第一次渲染。依次调用的生命周期方法有:constructor,componentWillMount,render,componentDidMount。
这是ES6class的构造方法,组件被创建时,会先调用组件的构造方法。如果父组件没有传入属性而组件自身定义了默认属性,那么参数props指向的是组件的默认属性。Constructo通常用于初始化组件的state以及绑定时间处理方法等工作。
这个方法在组件被挂载到DOM前调用,且只会被调用一次。在实际项目中很少会用到,因为可以将该方法提前到constructor中执行。在这个方法中调用this.setState不会引起组件的重新渲染。
这是定义组件时唯一必要的方法(组件的其他生命周期方法都可以省略)。在这个方法中,根据组件的props和state返回一个React元素,用于描述组件的UI,通常React元素使用JSX语法定义。注意:render并不负责组件的实际渲染工作,它只是返回一个UI的描述,真正的渲染出页面DOM的工作由React自身负责。Render是一个纯函数,在这个方法中不能执行任何有副作用的操作。
在组件被挂载到DOM后调用,且只会被调用一次。这时候已经可以获取到DOM结构了。这个方法通常还会用于向服务器端请求数据。在这个方法中调用this.setState会引起组件的重新渲染。
组件被挂载到DOM后,组件的props或state可以引起组件更新。props引起的组件更新,本质上是由渲染该组件的父组件引起的,也就是父组件的render方法被调用时,组件会发生更新过程,这个过程无论props是否改变,父组件render方法每一次调用,都会导致组件更新。State引起的组件更新,是通过调用this.setState修改组件 state触发的。组件更新阶段依次调用的声明周期方法有:compoentWillReceiveProps、shouldComponentUpdate、componentWillUpdate、render和componentDidUpdate。
这个方法只有在props引起的组件更新过程中,才会被调用。State引起的组件更新并不会触发该方法的执行。方法的参数nextProps是父组件传递给当前组件的新的props。因此往往需要比较nextProps和this.props来决定是否执行props发生变化后的逻辑,比如根据新的props调用this.setState触发组件的重新渲染。
这个方法决定组件是否继续执行更新过程。方法返回true时,组件继续更新过程;返回false时,更新过程停止。后续的方法也不会再被调用给你。一般通过比较nextProps、nextState和当前组件的props、state决定这个方法的返回结果。这个方法可以用来减少组价不必要的渲染从而优化组件性能。
这个方法在组件render调用前执行,可以作为组件更新发生前执行,某些工作的地方,一般也很少用到。
组件更新后被调用,可以作为操作更新下后DOM的地方。这个方法的两个参数分别代表组件更新前的props和state。
组件从DOM中被卸载的过程,这个过程只有一个生命周期方法:componentWillUnMount。 这个方法在组件被卸载之前调用,可以在这里执行一些清理工作,比如清理组件中使用的定时器等等,避免引起内存泄露。
最后注意:只有类组件具有生命周期方法,函数组件是没有生命周期方法的。
①当列表数据为多组时,调用数据运行时为提示警告(浏览器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的渲染优化。
1.React元素绑定事件有两点注意事项:
①在React中,事件的命名采用驼峰的形式(两个或多个单词组成的事件第一个单词首字母小写,其余的都要大写),而不是DOM元素中的小写字母命名方式。如:JavaScript里的onclick要改为onClick、onchange要改为onChange。
②处理事件的响应函数要以对象的形式赋值给事件属性,而不是DOM中的字符串形式。
如:在DOM中绑定一个点击事件是这样(传统的js写法):
而在React元素中绑定一个点击事件变成这种形式:
class App extends Component {
clickButton(){}; //clickButton函数
render() {
return (
)
}
}
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,也不需要担心组件重复渲染导致的函数重复创建问题。
如果一个表单元素的值是由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又会触发表单元素重新渲染,从而实现表单元素状态的控制。
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方法处理
非受控组件看似简化了操作表单元素的过程,但这种方式破坏了React对组件状态管理的一致性,往往出现不容易排查的问题,因此不建议使用。
本章详细介绍了React的主要特性及其用法。React通过JSX语法声明界面UI,将界面UI和它的逻辑封装在同一个JS文件中。组件是React的核心,根据组件的外部接口props和内部接口state完成自身的渲染。使用组件需要理解它的生命周期,借助不同的生命周期方法,组件可以实现复杂逻辑。渲染时,注意key的使用,事件处理时,注意事件名称和事件处理函数的写法。最后介绍表单元素的用法,使用方式分受控组价和非受控组件。另外,在工程项目中,注意有状态组件和无状态组件的划分从而妥善运用props和state。