您的位置:首页 > Web前端 > React

学习React生命周期

2019-01-08 16:38 543 查看

React生命周期总体分为三个阶段:创建阶段(Mounting)、运行阶段(Updating)和卸载阶段(Unmounting)。也有种说法分为四个阶段:初始化阶段(Initialization)、创建阶段(Mounting)、运行阶段(Updating)和卸载阶段(Unmounting)

一、React v16.0前的生命周期

1.初始化阶段(Initialization)
也就是下方代码中的类的构造方法,Test类继承Component类,也同时继承这个React的基类(也就继承了render方法和生命周期方法)。

Super是调用基类的构造方法。获取props。
Constructor 构造方法 可初始化state
2.创建阶段(Mounting)
特点:该阶段的函数只执行一次。
(1)ComponentWillMount()
组件被挂在到DOM之前调用,在render()之前被调用,因此在这个方法setState将不会触发页面重新渲染。可以将这里的操作放在constructor()中。(发送ajax请求获取数据);
(2)render()
根据组件中的props和st 4000 ate进行渲染页面(两者的任何变化都会导致页面的重新渲染),返回一个ReactComponent类型的对象,不负责组件的实际渲染工作,之后由react根据此元素去渲染页面DOM。
(注意:不要在render中调用this.setState()否则会造成递归渲染)
React.render的实现是在ReactMount中,源码:

render: function(nextElement, container, callback) {

var prevComponent = instancesByReactRootID[getReactRootID(container)];

if (prevComponent) {
var prevElement = prevComponent._currentElement;
if (shouldUpdateReactComponent(prevElement, nextElement)) {
return ReactMount._updateRootComponent(
prevComponent,
nextElement,
container,
callback
).getPublicInstance();
} else {
ReactMount.unmountComponentAtNode(container);
}
}

var reactRootElement = getReactRootElementInContainer(container);
var containerHasReactMarkup =
reactRootElement && ReactMount.isRenderedByReact(reactRootElement);

var shouldReuseMarkup = containerHasReactMarkup && !prevComponent;

var component = ReactMount._renderNewRootComponent(
nextElement,
container,
shouldReuseMarkup
).getPublicInstance();
if (callback) {
callback.call(component);
}
return component;
}

如果prevComponent为空也就是第一次进行挂载该ReactElement,直接添加即可,如果已经挂载过会调用shouldUpdateReactComponent(prevElement,nextElement)进行判断对组件进行update还是delete操作。
shouldUpdateReactComponent:如果prevElement类型是string或者number,并且nextElement也为string或者number时候返回true;如果prevElement和nextElement都是对象且key和type属相相同时,则返回prevElement._owner == nextElement._owner。
如果需要更新,则调用ReactMount…_updateRootComponent函数进行Reconciliation,并返回该组件;否则删除该组件,具体操作则是删除container的所有子元素。然后判断shouldReuseMarkup,对于初次挂载的ReactElement而言,该标记为false。最后通过调用_renderNewRootComponent方法将ReactElement渲染到DOM上,并获取对应的ReactComponent对象,最后执行回调并返回组件对象。
对于_renderNewRootComponent方法,通过调用instantiateReactComponent(nextElement, null)来实例化组件,并在ReactMount的缓存中注册组件,批量执行更新ReactUpdates.batchedUpdates,最终通过_mountImageIntoNode方法将虚拟节点插入到DOM中。
(3)ComponentDidMount()
组件被挂载到DOM后且只执行一次;可以获取组件内容的DOM对象并进行DOM操作;可以发送请求获取数据;可以通过this.setState()修改状态的值,修改后会进行页面的重新渲染。
3.运行阶段(Updating)
该阶段的函数执行多次(每当组件的props或者state改变的时候都会触发运行阶段的函数);
(1)ComponentWillReceiveProps(nextProps)
可以通过this.props获取到上一次的值,通过比较this.props和nextProps来判断是否属性发生变化,如果需要响应属性的变化可以在该方法中使用this.setState()处理状态。*在该函数调用this.setState将不会引起二次渲染。
(2)shouldComponentUpdate(nextProps,nextState)
根据这个方法的返回值决定是否重新渲染组件,返回值需布尔值,通过这个方法降低组件渲染的频率提升组件的性能。
(3)ComponentWillUpdate(nextProps,nextState)
组件将要跟新前执行,不能调用setState()否则会造成递归渲染
(4)render()同Mounting阶段的render
(5)ComponentDidUpdate(prevProps,prevState)
组件被跟新后调用,可以操作DOM
4.卸载阶段(Unmounting)
(1)ComponentWillUnmount()
在卸载组件的时候执行清理工作,eg:清理定时器,清除componentDidMount创建的DOM对象。
二、React V16.4生命周期

变更缘由
原来(React v16.0前)的生命周期在React v16推出的Fiber之后就不合适了,因为如果要开启async rendering,在render函数之前的所有函数,都有可能被执行多次。
原来(React v16.0前)的生命周期有哪些是在render前执行的呢?
componentWillMount
componentWillReceiveProps
shouldComponentUpdate
componentWillUpdate
如果开发者开了async rendering,而且又在以上这些render前执行的生命周期方法做AJAX请求的话,那AJAX将被无谓地多次调用。。。明显不是我们期望的结果。而且在componentWillMount里发起AJAX,不管多快得到结果也赶不上首次render,而且componentWillMount在服务器端渲染也会被调用到(当然,也许这是预期的结果),这样的IO操作放在componentDidMount里更合适。
禁止不能用比劝导开发者不要这样用的效果更好,所以除了shouldComponentUpdate,其他在render函数之前的所有函数(componentWillMount,componentWillReceiveProps,componentWillUpdate)都被getDerivedStateFromProps替代。
也就是用一个静态函数getDerivedStateFromProps来取代被deprecate的几个生命周期函数,就是强制开发者在render之前只做无副作用的操作,而且能做的操作局限在根据props和state决定新的state
React v16.0刚推出的时候,是增加了一个componentDidCatch生命周期函数,这只是一个增量式修改,完全不影响原有生命周期函数;但是,到了React v16.3,大改动来了,引入了两个新的生命周期函数。
新引入了两个新的生命周期函数:getDerivedStateFromProps,getSnapshotBeforeUpdate
getDerivedStateFromProps
getDerivedStateFromProps本来(React v16.3中)是只在创建和更新(由父组件引发部分),也就是不是不由父组件引发,那么getDerivedStateFromProps也不会被调用,如自身setState引发或者forceUpdate引发。
React v16.3 的生命周期图

React v16.3
这样的话理解起来有点乱,在React v16.4中改正了这一点,让getDerivedStateFromProps无论是Mounting还是Updating,也无论是因为什么引起的Updating,全部都会被调用,具体可看React v16.4 的生命周期图。
React v16.4后的getDerivedStateFromProps
static getDerivedStateFromProps(props, state) 在组件创建时和更新时的render方法之前调用,它应该返回一个对象来更新状态,或者返回null来不更新任何内容。
getSnapshotBeforeUpdate
getSnapshotBeforeUpdate() 被调用于render之后,可以读取但无法使用DOM的时候。它使您的组件可以在可能更改之前从DOM捕获一些信息(例如滚动位置)。此生命周期返回的任何值都将作为参数传递给componentDidUpdate()。
官网给的例子:

class ScrollingList extends React.Component {
constructor(props) {
super(props);
this.listRef = React.createRef();
}

getSnapshotBeforeUpdate(prevProps, prevState) {
//我们是否要添加新的 items 到列表?
// 捕捉滚动位置,以便我们可以稍后调整滚动.
if (prevProps.list.length < this.props.list.length) {
const list = this.listRef.current;
return list.scrollHeight - list.scrollTop;
}
return null;
}

componentDidUpdate(prevProps, prevState, snapshot) {
//如果我们有snapshot值, 我们已经添加了 新的items.
// 调整滚动以至于这些新的items 不会将旧items推出视图。
// (这边的snapshot是 getSnapshotBeforeUpdate方法的返回值)
if (snapshot !== null) {
const list = this.listRef.current;
list.scrollTop = list.scrollHeight - snapshot;
}
}

render() {
return (
<div ref={this.listRef}>{/* ...contents... */}</div>
);
}
}

参考:
程墨Morgan–React v16.3之后的组件生命周期函数
徐超–《React进阶之路》
Aermin–详解react生命周期(包括react16版)
ReactJS分析之入口函数render(网址:https://www.cnblogs.com/accordion/p/4501118.html)

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: