React知识整理

1、性能高

1) 虚拟DOM,不总是直接操作DOM

React将DOM抽象为虚拟DOM,虚拟DOM其实就是用一个对象来描述DOM,通过对比前后两个对象的    差异,最终只把变化的部分重新渲染,提高渲染的效率
  虚拟DOM比较轻,原生dom有200多个属性,比较重。

创建虚拟DOM方式:
1)createElement()方式:不友好,太复杂
创建虚拟DOM:
//参数1:元素名称 参数2:元素属性对象(null表示无) 参数3:当前元素的子元素string||createElement() 的返回值
React.createElement(‘div’,{title:’hello’},’hello React’)
渲染虚拟DOM:
// 参数1:虚拟dom对象 参数2:dom对象表示渲染到哪个元素内 参数3:回调函数
ReactDOM.render(div,document.getElementById(‘app’))

​ 2)JSX方式

​ jsx方最终扔被编译为createElement()方法
推荐:使用 JSX 的方式创建组件
JSX - JavaScript XML
注意:JSX的语法需要通过 babel-preset-react 编译后,才能被解析执行

​ 注意点:
注意 1: 如果在 JSX 中给元素添加类, 需要使用 className 代替 class , 类似:label 的 for属性,使用htmlFor代替
注意 2:在 JSX 中可以直接使用 JS代码,直接在 JSX 中通过 {} 中间写 JS代码即可
注意 3:在 JSX 中只能使用表达式,但是不能出现 语句!!!
注意 4:在 JSX 中注释语法:{/* 中间是注释的内容 */}

2) DOM diff算法

​ 实际上react的diff算法大大的提升了两个虚拟dom的比对性能,如图,虚拟dom什么时候会被比对,当数据发生改变的时候,虚拟dom才会去做新的比对,那么什么时候数据发生了改变了呢,要么改变了props,要么改变了state。其实props的改变是因为父组件的state发生了改变,所以其实都是setState数据才发生变化。之前说过setState是异步的,那么为什么是异步呢,实际上是为了提升react底层的性能,假设连续调用三次setState,变更三组数据,那么就会进行三次虚拟dom的比对,然后更新三次页面,假设三次改变,时间间隔非常的小,这样会比较浪费性能,所以react,把三次比对变成一次比对,这样就会优化比对带来的性能问题,所以设置react为异步函数。接下来看具体的diff算法 。

​ 如图,diff算法有个很重要的概念,叫做同级比较,首先会比较最顶层的虚拟dom节点是否一致,假设一致,再去比较下一个节点。假设第一层虚拟dom不一致,这个时候怎么办呢?这个时候react就不会往下比了,他会原始的虚拟dom下面的节点全部删除掉,重新生成一遍节点下面的所有dom,然后用重新生成的dom,替换原始页面的dom,也就是只比对一层dom,大家可能会想,这不是性能很低吗?假设第一层节点不同,下面的节点都相同,岂不是下面的节点都没法复用了,确实是这样的,虽然会造成一些dom节点的渲染浪费,但是这种比对有什么好处呢?我们说同层比对,带来的算法非常的简单,只要一层一层的做对比就行了,算法简单,带来的好处就是比对的速度会非常的快,所以可能会造成重新渲染的一些浪费,但大大减少了去比对的算法上的性能消耗。所以采用了同层比对的算法。

2、state 和props

1) state

1、state是组件对象最为重要的属性,值是对象(可以包含多个数据)

2、组件被称为状态机,通过更新组件的state重新渲染页面。

3、初始化state:

1
2
3
4
5
6
7
constructor (props) {
super(props)
this.state = {
stateProp1 : value1,
stateProp2 : value2
}
}

4、读取某个state:

1
this.state.statePropertyName

5、更新state –> 组件界面更新

1
2
3
4
this.setState({
stateProp1 : value1,
stateProp2 : value2
})

2) props

1、每个组件对象都会有props属性;组件标签的所有属性都保存在props中。

2、通过标签属性从组件外向组件内传递数据; 注意: 组件内部不要修改props数据。

3、内部读取某个属性值:

1
this.props.propertyName

4、对props中的属性值进行类型限制和非必要限制:

1
2
3
4
 Person.propTypes = {
name: PropTypes.string.isRequired,
age: PropTypes.number
}

5、默认属性值:

1
2
3
Person.defaultProps = {
name: 'Mary'
}

3) refs

1、组件内的标签都可以定义ref属性来标识自己

​ a. <input type=”text” ref={input => this.msgInput = input}/>

​ b. 回调函数在组件初始化渲染完或卸载时自动调用

​ 2、在组件中可以通过this.msgInput来得到对应的真实DOM元素

注意点:

​ 1、不要直接修改state属性的值,这样不会重新渲染组件。

​ 2、 state和props的主要区别是 props是不可变的,而state可以根据与用户交互来改变。

​ 3、React只需要更新组件的state,然后根据新的state重新渲染用户界面(不要操作DOM)。
修改完state状态后,会自动调用render渲染界面。

3、生命周期

1
2
3
4
5
6
7
8
9
10
11
12
13
14
mounting                     updating                    unmounting
getDefaultProps() componentWillReceiveProps componentWillUnmount
| |
| |
getInitialState() shouldComponentUpdate
| |
| true |
componentWillMount componentWillUpdate
| |
| |
render render
| |
| |
componentDidMount componentDidUpdate

1) mounting时期:

1
2
componentWillMount  //组件被挂载到页面之前执行(只会执行一次)
componentDidMount //组件被挂载到页面之后执行(只会执行一次)

2) Updating时期:

1
2
3
4
5
6
7
8
9
//当子组件将要从父组件接收参数时执行
//如果这个子组件第一次存在于父组件中,不会执行
//当这个子组件仍存在于父组件中,才会被执行
componentWillReceiveProps

//componentWillReceiveProps之后,componentWillUpdate之前执行
//用于判断页面是否需要被更新
//需要有返回值(Boolean类型): true 页面需要更新,false 页面不需要更新
shouldComponentUpdate

3) unmounting时期:

1
componentWillUnmount

4) 哪些生命周期函数不能调用setState方法,调了之后会怎么样?

1
2
3
4
5
6
7
8
1、constructor()函数中不要调用setState方法,控制台会产生警告。为state赋值的话使用this.state 就行。
2、componentWillMount 可以调用setState方法,但是组件不会重新渲染,因为它在render之前执行。
3、render函数中不能调用setState方法,会导致循环调用。
4、componentDidMount 可以调用setState方法。 5、componentWillReceiveProps 可以调用setState方法,该方法仅在父组件重新渲染时触发。
6、shouldComponentUpdate 不能调用setState方法,会导致循坏调用。
7、componentWillUpdate 不能调用setState方法,会导致循坏调用。
8、componentDidUpdate 不能调用setState方法,会导致循坏调用。
9、componentWillUnmount 调用setState无意义。

5) 父子组件执行顺序

第一次渲染时:

1
2
3
父组件constructor执行 -> 父组件componentWillMount执行 -> 
子组件constructor执行 -> 子组件componentWillMount执行 ->
子组件componentDidMount执行 -> 父组件componentDidMount执行

当父组件更新时:

1
2
3
父组件shouldComponentUpdate执行 -> 父组件componentWillUpdate执行 -> 父组件render ->
子组件componentWillReceiveProps执行 -> 子组件shouldComponentUpdate执行 -> 子组件componentWillUpdate执行 -> 子组件 render -> 子组件componentDidUpdate执行 ->
父组件componentDidUpdate执行