一、老版本的生命周期以及按顺序执行的流程
Initialization (初始化阶段)
这些方法会在组件初始化的时候被调用,只跟实例的建立有关。react
若是用 createReactClass 进行建立,则还有 getInitialState 这些生命周期函数,但不多使用,咱们这里不说起。算法
static defaultProps{} (getDefaultProps())
定义默认 props ,会和父组件传下来的 props 进行合并,且以父组件中的 props 优先级更高,至关于 {...defaultProps, props} 。redux
static propTypes{} (getInitialState())
定义 props 的数据类型,能够帮助咱们肯定其有效性,减小许多开发中没必要要的错误。浏览器
constructor()
在加载阶段前调用一次,进行 state 的初始化。性能优化
constructor(props){服务器
super(props)
}网络
super(props) 用来调用父类的构建方法。数据结构
Mounting(加载阶段)
componentWillMount()
新版中为 UNSAFE_componentWillMount() 。架构
只在组件加载时调用,整个生命周期只调用一次,之后组件更新也不会调用,此时能够修改 state。dom
render()
react 中最重要的生命周期函数,建立虚拟 dom,并进行 diff 算法,更新 dom 树也在此进行。因此不该该在此改变组件的状态或者与浏览器进行交互。
注意:这个函数不能缺乏,若是不建立虚拟 dom,能够 return null 。
componentDidMount()
组件加载完成后当即调用,整个生命周期只调用一次,能够获取到更新后的 DOM,在此处能够进行网络请求等。
Updating(更新阶段)
componentWillReceiveProps()
新版中为 UNSAFE_componentWillReceiveProps() 。
在组件加载后,若是有新的 props 传递过来,会先调用这个函数,能够在这里调用 setState() 修改 state 。
componentWillReceiveProps(nextProps)
shouldComponentUpdate()
react 中一个重要的性能优化点,组件接收到新的 props 或者 state ,返回 true 表示须要更新 dom,返回 false 阻止更新。
shouldComponentUpdate(nextProps, nextState)
componentWillUpdate()
新版中为 UNSAFE_componentWillUpdate() 。
组件加载时不调用,只有在组件须要更新(即 shouldComponentUpdate 返回 true )时调用。
componentWillUpdate(nextProps, nextState)
注意:不能在这个方法中调用 setState() 修改 state 。
不然会从新触发updating
render()
componentDidUpdate()
在组件更新完成后当即被调用,能够进行网络请求等。
componentDidUpdate(prevProps, prevState)
Unmouting(销毁阶段)
componentWillUnmount()
在组件被卸载和销毁以前调用,能够在这里处理任何须要的清理工做,好比解除定时器,取消已经发起的网络请求,清理在 componentDidMount 函数中建立的 DOM 元素。
componentWillUnmount()
Catching(捕获阶段)
componentDidCatch()
错误边界捕获,在v16.0刚推出的时候新增长的一个生命周期函数,用于捕获在 子组件树 中任意地方发生的JavaScript 错误,一个错误边界不能捕获它本身内部的错误。
componentDidCatch(error, info)
二、新版本中新增的生命周期以及对应的使用方法
首先,新版本中有三个生命周期被deprecate(去掉)了,他们分别是:
componentWillReceiveProps
componentWillMount
componentWillUpdate
getDerivedStateFromProps()
这个getDerivedStateFromProps是一个静态函数,因此函数体内不能访问this,简单说,就是应该一个纯函数,纯函数的话,输出彻底由输入决定。
static getDerivedStateFromProps(nextProps, prevState) { //根据nextProps和prevState计算出预期的状态改变,返回结果会被送给setState }
每当父组件引起当前组件的渲染过程时,getDerivedStateFromProps会被调用,这样咱们有一个机会能够根据新的props和以前的state来调整新的state,若是放在三个被deprecate生命周期函数中实现比较纯,没有反作用的话,基本上搬到getDerivedStateFromProps里就好了;
注意:getDerivedStateFromProps不管是Mounting仍是Updating,也不管是由于什么引发的Updating,所有都会被调用。
getSnapshotBeforeUpdate()
这函数会在render以后执行,而执行之时DOM元素尚未被更新,给了一个机会去获取DOM信息,计算获得一个snapshot,这个snapshot会做为componentDidUpdate的第三个参数传入。
getSnapshotBeforeUpdate(prevProps, prevState) { console.log('#enter getSnapshotBeforeUpdate'); return 'foo'; } componentDidUpdate(prevProps, prevState, snapshot) { console.log('#enter componentDidUpdate snapshot = ', snapshot); }
getSnapshotBeforeUpdate把snapshot返回,而后DOM改变,而后snapshot传递给componentDidUpdate。
总结: 用一个静态函数getDerivedStateFromProps来取代被清除的几个生命周期函数,就是强制开发者在render以前只作无反作用的操做,并且能作的操做局限在根据props和state决定新的state,而已。
这是进一步施加约束,防止开发者乱来
Q&A:
一、Willmount or didmount?
以前想过为何不在componentWillMount里写AJAX获取数据的功能,以前的观点是,componentWillMount在render以前执行,早一点执行早获得结果。
可是,要知道,在componentWillMount里发起AJAX,无论多快获得结果也赶不上首次render,
并且componentWillMount在服务器端渲染也会被调用到(固然,也许这是预期的结果),
这样的IO操做放在componentDidMount里更合适。在Fiber启用async render以后,更没有理由在componentWillMount里作AJAX,
由于componentWillMount可能会被调用屡次,谁也不会但愿无谓地屡次调用AJAX吧。
其实这个问题react的开发者早就有想过,因此才运营而生了后面的新的生命周期,既然不想放在这些很差的生命周期里面,那都不如直接让他们干脆没办法作。
二、何时用forceUpdate?
forceUpdate就是从新render。有些变量不在state上,可是你又想达到这个变量更新的时候,刷新render;
或者state里的某个变量层次太深,更新的时候没有自动触发render。
这些时候均可以手动调用forceUpdate自动触发render。因此建议使用immutable来操做state,redux等flux架构来管理state。
三、父组件的状态变化怎么影响子组件的生命周期?
shouldComponentUpdate函数接受nextProps和nextState做为参数。
你能够根据props里面设的值或者state里面设的值来判断需不须要更新。例如判断this.state.count是否等于nextState.count。
四、为何在react componentWillUpdate中调用setstate会形成循环调用?
若是在shouldComponentUpdate和componentWillUpdate中调用了setState,
此时this._pendingStateQueue != null,则performUpdateIfNecessary方法就会调用updateComponent方法进行组件更新。
可是updateComponent方法又会调用shouldComponentUpdate和componentWillUpdate,
所以形成循环调用,使得浏览器内存占满后崩溃。
五、函数式组件有没有生命周期?为何?
由于函数式组件没有继承React.Component,因为生命周期函数是React.Component类的方法实现的
因此没继承这个类,天然就无法使用生命周期函数了
六、PureComponent和Component有什么区别?
PureComponent优点: 不须要开发者本身实现shouldComponentUpdate,就能够进行简单的判断来提高性能。 PureComponent缺点: React.PureComponent 经过props和state的浅对比来实现 shouldComponentUpate()。
在PureComponent中,若是包含比较复杂的数据结构,可能会因深层的数据不一致而产生错误的否认判断,致使界面得不到更新。
PureComponent 在使用 shouldComponentUpdate的处理是由 PureComponent 自身来处理,而不是由用户来控制,
因此咱们在 PureComponent 中若是复写今生命周期回调函数,React 会提示咱们错误的。告诉咱们不容许重写该方法。
七、为何PureComponent比较复杂的数据结构,可能会因深层的数据不一致而产生错误的否认判断?
JavaScript 中的对象通常是可变的(Mutable),由于使用了引用赋值,新的对象简单的引用了原始对象,
改变新的对象将影响到原始对象。
如
foo={a: 1}; bar=foo; bar.a=2
你会发现此时 foo.a 也被改为了 2。
为了解决这个问题,通常的作法是使用 shallowCopy(浅拷贝)或 deepCopy(深拷贝)来避免被修改,但这样作形成了 CPU 和内存的浪费。
缘由在于js使用的是引用赋值,新的对象简单引用了原始对象,改变新对象虽然影响了原始对象,
但对象的地址仍是同样,使用===比较的方式相等。
而在PureComponent中,会被断定prop相等而不触发render()。
避免此类问题最简单的方式是,避免使用值可能会突变的属性或状态,而是使用副原本返回新的变量
八、react中的setstate是同步的仍是异步的?Rax呢?
由React控制的事件处理程序,以及生命周期函数调用setState不会同步更新state 。
React控制以外的事件中调用setState是同步更新的。好比原生js绑定的事件,setTimeout/setInterval等。
大部分开发中用到的都是React封装的事件,好比onChange、onClick、onTouchMove等,这些事件处理程序中的setState都是异步处理的。
Rax的setState是同步的
九、React是怎样控制异步和同步的?
在 React 的 setState 函数实现中,会根据一个变量 isBatchingUpdates
判断是直接更新 this.state 仍是放到队列中延时更新,
而 isBatchingUpdates 默认是 false,表示 setState 会同步更新 this.state;
可是,有一个函数 batchedUpdates,该函数会把 isBatchingUpdates 修改成 true,
而当 React 在调用事件处理函数以前就会先调用这个 batchedUpdates将isBatchingUpdates修改成true,
这样由 React 控制的事件处理过程 setState 不会同步更新 this.state。
十、setState的一些用法?
(1)多个setState调用会合并处理:
在某些处理程序中调用了两次setState,可是render只执行了一次。
由于React会将多个this.setState产生的修改放在一个队列里进行批延时处理。
(2)参数为函数的setState用法
假如setState更新state后我但愿作一些事情,
而setState多是异步的,那我怎么知道它何时执行完成。
因此setState提供了函数式用法
接收两个函数参数,第一个函数调用更新state,第二个函数是更新完以后的回调。
case:
handleClick() {
this.setState({
count: this.state.count + 1
})
}
以上操做存在潜在的陷阱,不该该依靠它们的值来计算下一个状态。
handleClick() {
this.setState({
count: this.state.count + 1
})
this.setState({
count: this.state.count + 1
})
this.setState({
count: this.state.count + 1
})
}
最终的结果只加了1
由于调用this.setState时,并无当即更改this.state,
因此this.setState只是在反复设置同一个值而已,上面的代码等同于这样
handleClick() {
const count = this.state.count
this.setState({
count: count + 1
})
this.setState({
count: count + 1
})
this.setState({
count: count + 1
})
}
count至关于一个快照,因此无论重复多少次,结果都是加1。
第一个函数接收先前的状态做为第一个参数,将这次更新被应用时的props作为第二个参数。
increment(state, props) {
return {
count: state.count + 1
}
}
handleClick() {
this.setState(this.increment)
this.setState(this.increment)
this.setState(this.increment)
}
对于屡次调用函数式setState的状况,React会保证调用每次increment时,state都已经合并了以前的状态修改结果。
也就是说,第一次调用this.setState(increment),
传给increment的state参数的count是10,第二调用是11,第三次调用是12,最终handleClick执行完成后的结果就是this.state.count变成了13。
值得注意的是:在increment函数被调用时,this.state并无被改变,
依然要等到render函数被从新执行时(或者shouldComponentUpdate函数返回false以后)才被改变,
由于render只执行一次。