在 React 中,关于每一次由状况改动导致页面视图的改动,都会阅历两个阶段:render 阶段
、commit 阶段
。
只要 class 组件才有生命周期,因为 class 组件会创立对应的实例,而函数组件不会。组件实例从被创立到被毁掉的过程称为组件的生命周期。
由 class 组件创立的实例具有生命周期,它的 render 函数在 render 阶段履行,并在此阶段进行 DOM 节点的 diff(diff 算法便是在此阶段进行的),找出需求改动的 DOM 操作。然后在 commit 阶段将对应的 DOM 操作提交至视图中。
而 class 组件实例的一切生命周期函数,都会在 render 阶段和 commit 阶段履行。
注:赤色为 React 17 现已抛弃的生命周期钩子,绿色为新增的生命周期钩子
在初次烘托页面时,会调用 Mount 相关生命周期钩子;在之后的页面烘托中,会调用 Update 相关生命周期钩子。所以与 Mount 相关的生命周期钩子只会被调用一次。
render 阶段
render 阶段会履行众多生命周期钩子,例如:在初次烘托时履行 constructor、getDerivedStateFromProps、componentWillMount、render,在更新时履行 componentWillReceiveProps、shouldComponentUpdate、componentWillUpdate、render,在烘托阶段捕捉到了子孙组件中的错误时会履行 getDerivedStateFromError。
接下来,看看这些生命周期钩子的调用机遇,以及它们的效果。
constructor
该办法只会履行一次,调用该办法会回来一个组件实例。
在初始化阶段履行,可直接对 this.state
赋值。其他生命周期函数中只能经过 this.setState
修改 state,不能直接为 this.state
赋值。
运用场景:
一般在 constructor 中做一些组件的初始化工作,例如:初始化组件的 state。
componentWillReceiveProps
在已挂载组件接纳到新的 props 之前调用。你能够在这个函数中比较新旧 props,并根据新旧 props 更改 state。可是它会损坏 props 数据的单一数据源。
在初次烘托组件时,不会调用此生命周期钩子;运用 this.setState
触发组件更新时,也不会调用此生命周期钩子。
不过要留意:假如是父组件烘托导致了组件的重新烘托,即使传给该组件的 props 没变,该组件中的这个生命周期函数也会被调用。
咱们一般不运用此生命周期函数,因为它一般会损坏数据源的单一性。
getDerivedStateFromProps
它是一个静态办法,接纳 propsprops 和 statestate 两个参数。它会在调用 render 办法之前被调用,不管是在初始挂载时仍是在后续组件更新时都会被调用。
它的调用机遇和 componentWillMount、componentWillUpdate、componentWillReceiveProps 相同都是在 render 办法被调用之前,它能够作为 componentWillMount、componentWillUpdate 和 componentWillReceiveProps 的代替方案。
当然,它的效果不止如此,它能够回来一个对象,用来更新 state,就像它的姓名相同,从 props 中获取衍生的 state。假如不需求更新 state 则能够回来 null。
需求留意的是:这个生命周期函数是类的静态办法,并不是原型中的办法,所以在其内部运用 this 访问到的不是组件实例。
此生命周期钩子不常用,假如能够的话,咱们也尽可能不会运用它。
关于为什么要抛弃 componentWillMount、componentWillUpdate、componentWillReceiveProps 三个生命周期钩子而新增 getDerivedStateFromProps 生命周期钩子,后边会阐明原因。
shouldComponentUpdate
在组件准备更新之前调用,可是初次烘托或许运用 forceUpdate 函数时不会被调用。跟它的姓名相同,它用来判别一个组件是否应该更新。
默许情况下,当组件的 props 或许 state 变化时,都会导致组件更新。它在 render 办法之前履行,假如它的回来值为 false,则不会更新组件,也不会履行后边的 render 办法。
它接纳两个参数,nextProps 和 nextState,即下一次更新的 props 和下一次更新的 state。咱们能够将 this.props
和 nextProps 比较,以及将 this.state
与 nextState 比较,并回来 false,让组件越过更新。不过留意:它并不会阻挠子组件因为 state 改动而导致的更新。
运用场景:
这个生命周期办法一般用来做功能优化。
componentWillMount(UNSAFE)
在组件挂载至 DOM 之前调用,并且只会调用一次。它在 render 办法之前调用,因此在 componentWillMount 中调用 this.setState
不会触发额外的烘托。
这个生命周期钩子运用频率较小,因为咱们一般在 constructor 中初始化 state,在 componentDidMount 中引进副效果或许订阅内容。
componentWillUpdate(UNSAFE)
在组件行将更新之前履行,假如 shouldComponentUpdate 函数回来 false,则不会调用 componentWillUpdate 办法。
这个生命周期钩子和 componentWillMount 相似,履行的机遇是相同的,只不过 componentWillMount 在组件初次烘托时履行,而 componentWillUpdate 在组件后续更新时履行。这两个生命周期函数都不经常运用。
render
render 办法是类组件中仅有有必要完成的办法,它的回来值将作为页面烘托的视图。render 函数应该为纯函数,也便是关于相同的 state 和 props,它总是回来相同的烘托成果。
render 函数被调用时,会回来以下四种类型之一:
- React 元素:一般为 JSX 语法。例如:
<div />
、<MyComponent>
等等。 - 数组或许 fragments:render 办法能够经过数组回来多个元素。
- Portals:烘托子节点至不同的子树中。
- 字符串或许数值:会作为文本节点被烘托。
- boolean 类型或许 null:什么都不烘托。
需求留意的是:假如 shouldComponentUpdate 生命周期钩子回来 false,则 render 办法(render 阶段后续生命周期钩子)不会履行。
commit 阶段
commit 阶段在初次烘托时会履行 componentDidMount,在组件更新时会履行 getSnapshotBeforeUpdate 和 componentDidUpdate。
componentDidMount
该生命周期办法会在组件挂载之后履行,也只会履行一次,也便是将组件对应的 DOM 插入 DOM 树中之后调用。它会在浏览器更新视图之前调用,假如在 componentDidMount 中直接调用 this.setState
,它会触发额外的烘托,会再一次调用 render 函数,可是浏览器中视图的更新只会履行一次。
运用场景:
依赖于 DOM 的初始化操作应该放在这儿,此外,咱们一般在这个生命周期办法中发送网络恳求、添加订阅等。
getSnapshotBeforeUpdate
此生命周期函数在最近一次烘托提交至 DOM 树之前履行,此刻 DOM 树还未改动,咱们能够在这儿获取 DOM 改动前的信息,例如:更新前 DOM 的翻滚方位。
它接纳两个参数,分别是:prevProps、prevState,上一个状况的 props 和上一个状况的 state。它的回来值将会传递给 componentDidUpdate 生命周期钩子的第三个参数。
运用场景:
需求获取更新前 DOM 的信息时。例如:需求以特别办法处理翻滚方位的谈天线程等。
componentDidUpdate
在组件更新后立即调用,初次烘托不会调用该办法。它的履行机遇和 componentDidMount 共同,仅仅 componentDidMount 在初次烘托时调用,而 componentDidUpdate 在后续的组件更新时调用。能够在这个生命周期中直接调用 this.setState
,可是有必要包裹在一个条件语句中,否则会导致死循环。
componentDidUpdate 接纳三个参数,分别是 prevProps、prevState、snapshot,即:前一个状况的 props,前一个状况的 state、getSnapshotBeforeUpdate 的回来值。
假如组件完成了 getSnapshotBeforeUpdate 生命周期函数,则 getSnapshotBeforeUpdate 的回来值将作为 componentDidUpdate 的第三个参数。
运用场景:
在这个生命周期办法中,能够对 DOM 进行操作或许进行网络恳求。
componentWillUnmount
这个生命周期函数会在组件卸载以及毁掉之前调用。
运用场景:
一般用来履行组件的整理操作,例如:铲除 timer、撤销网络恳求、铲除订阅等。
为什么抛弃三个生命周期函数
React 在 16.3 版别中:
- 将 componentWillMount、componentWillReceiveProps、componentWillUpdate 三个生命周期钩子加上了 UNSAFE 前缀,变为 UNSAFE_componentWillMount、UNSAFE_componentWillReceiveProps 和 UNSAFE_componentWillUpdate。
- 并引进了一个新的生命周期钩子:getDerivedStateFromProps。
并在 17.0 以及之后的版别中:
- 删除了 componentWillMount、componentWillReceiveProps、componentWillUpdate 这三个生命周期钩子。
- 不过 UNSAFE_componentWillMount、UNSAFE_componentWillReceiveProps 和 UNSAFE_componentWillUpdate 仍是能够用的。
为什么要抛弃这三个生命周期钩子?它们有哪些问题呢?React 又是怎么处理的呢?
咱们知道 React 的更新流程分为:render 阶段和 commit 阶段。componentWillMount、componentWillReceiveProps、componentWillUpdate 这三个生命周期钩子都是在 render 阶段履行的。
在 fiber 架构被运用之前,render 阶段是不能被打断的。当页面逐步复杂之后,就有可能会阻塞页面的烘托,所以 React 推出了 fiber 架构。在运用 fiber 架构之后,低优先级使命的 render 阶段能够被高优先级使命打断。
而这导致的问题便是:在 render 阶段履行的生命周期函数可能被履行屡次。像 componentWillMount、componentWillReceiveProps、componentWillUpdate 这三个生命周期钩子,假如咱们在其间履行一些具有副效果的操作,例如发送网络恳求,就有可能导致一个相同的网络恳求被履行屡次,这明显不是咱们想看到的。
而 React 又没法强迫开发者不去这样做,因为怎么样运用 React 是开发者的自在,所以 React 就新增了一个静态的生命周期 getDerivedStateFromProps,来处理这个问题。
用一个静态函数 getDerivedStateFromProps 来代替被抛弃的几个生命周期函数,这样开发者就无法经过 this 获取到组件的实例,也不能发送网络恳求以及调用 this.setState
。它便是强制开发者在 render 之前只做无副效果的操作,间接强制咱们无法进行这些不合理不标准的操作,然后防止对生命周期的滥用。
父子组件生命周期函数调用次序
接下来咱们来探求一下 React 中父子组件生命周期函数的履行次序。由不同的原因导致的组件烘托,React 会履行不同的生命周期函数。例如:在初次烘托的时候,会履行与 mount 相关的生命周期函数;触发子组件的 this.setState
只会调用子组件中与 update 相关的生命周期函数;触发父组件的 this.setState
则会调用父子组件中与 update 相关的生命周期函数等。
为了探求父子组件以及不同子组件之间生命周期函数的履行次序,我初始化了三个组件分别是父组件 AppApp、子组件 Child1Child1、子组件 Child2Child2。它们的结构如下:
初次烘托
在初次烘托中,咱们探求图中 Mount 阶段生命周期钩子的履行次序。
注:赤色为 React 17 现已抛弃的生命周期钩子,绿色为新增的生命周期钩子
因为被抛弃的生命周期钩子和新增的生命周期钩子不能一起出现在代码中,所以咱们分情况进行:
旧的生命周期函数调用次序
import React from 'react';
class App extends React.Component {
constructor(props) {
super(props);
console.log('App constructor');
}
UNSAFE_componentWillMount() {
console.log('App componentWillMount');
}
componentDidMount() {
console.log('App componentDidMount');
}
render() {
console.log('App render');
return (
<div>
<Child order={1} />
<Child order={2} />
</div>
)
}
}
class Child extends React.Component {
constructor(props) {
super(props);
console.log(`Child${this.props.order} constructor`);
}
UNSAFE_componentWillMount() {
console.log(`Child${this.props.order} componentWillMount`);
}
componentDidMount() {
console.log(`Child${this.props.order} componentDidMount`);
}
render() {
console.log(`Child${this.props.order} render`);
return (
<div>
Child{this.props.order}
</div>
)
}
}
export default App;
其履行成果如下:
其间 constructor、componentWillMount、render 为 render
阶段履行的生命周期函数,componentDidMount 为 commit
阶段履行的生命周期函数。
- 首要顺次履行父组件 render 阶段的生命周期函数;
- 然后顺次履行子组件 render 阶段的生命周期函数;
- 最终穿插履行子组件和父组件 commit 阶段的生命周期函数。
React Fiber 树的构建、更新相似于树的先序遍历(深度优先搜索)。在“递归”时,履行 render 阶段的生命周期函数;在“回溯”时,履行 commit 阶段的生命周期函数。
关于 render 阶段的生命周期函数,其次序是 父组件
-> 子组件
;而关于 commit 阶段的生命周期函数,其次序是 子组件
-> 父组件
。
需求留意的是:这儿的履行次序并不是真实的树的先序遍历。在“回溯”时,是穿插履行各子组件和父组件 commit 阶段的生命周期函数。
新的生命周期函数调用次序
import React from 'react';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0,
}
console.log('App constructor');
}
static getDerivedStateFromProps() {
console.log('App static getDerivedStateFromProps');
return null;
}
componentDidMount() {
console.log('App componentDidMount');
}
render() {
console.log('App render');
return (
<div>
<Child order={1} />
<Child order={2} />
</div>
)
}
}
class Child extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0,
};
console.log(`Child${this.props.order} constructor`);
}
static getDerivedStateFromProps(props) {
console.log(`Child${props.order} static getDerivedStateFromProps`);
return null;
}
componentDidMount() {
console.log(`Child${this.props.order} componentDidMount`);
}
render() {
console.log(`Child${this.props.order} render`);
return (
<div>
Child{this.props.order}
</div>
)
}
}
export default App;
其履行成果如下:
能够看到,在初次烘托时,getDerivedStateFromProps 的履行次序基本上代替了 componentWillMount 的履行次序。
可是需求留意:getDerivedStateFromProps 是一个静态办法,不能经过 this 获取到组件实例,假如咱们要拿到组件的 props 和 state,有必要要经过参数才干拿到。而在 componentWillMount 中,则是经过 this.props 拿到 props。
子组件状况改动
接下来,咱们分别为父组件和子组件加上 onClick 事件,当点击子组件对应的文字时,让子组件更新,调用其 this.setState
办法,再来看看各生命周期的履行次序。
旧的生命周期函数调用次序
import React from 'react';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0,
}
console.log('App constructor');
}
UNSAFE_componentWillReceiveProps() {
console.log('App componentWillReceiveProps');
}
UNSAFE_componentWillUpdate() {
console.log('App componentWillUpdate');
}
shouldComponentUpdate(nextProps) {
console.log('App shouldComponentUpdate');
return true;
}
componentDidUpdate() {
console.log('App componentDidUpdate');
}
render() {
console.log('App render');
return (
<div>
<div onClick={() => this.setState((count) => ({ count: count + 1 }))}>App</div>
<Child order={1} />
<Child order={2} />
</div>
)
}
}
class Child extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0,
};
console.log(`Child${this.props.order} constructor`);
}
UNSAFE_componentWillReceiveProps(nextProps) {
console.log(`Child${nextProps.order} componentWillReceiveProps`);
}
UNSAFE_componentWillUpdate() {
console.log(`Child${this.props.order} componentWillUpdate`);
}
componentDidMount() {
console.log(`Child${this.props.order} componentDidMount`);
}
shouldComponentUpdate(nextProps) {
console.log(`Child${nextProps.order} shouldComponentUpdate`);
return true;
}
componentDidUpdate() {
console.log(`Child${this.props.order} componentDidUpdate`);
}
render() {
console.log(`Child${this.props.order} render`);
return (
<div onClick={() => this.setState((count) => ({ count: count + 1 }))}>
Child{this.props.order}
</div>
)
}
}
export default App;
当点击文字 Child1 时,其履行成果如下:
当点击文字 Child2 时,其履行成果如下:
上面的成果中,并没有履行 componentWillReceiveProps 生命周期函数,因为运用 this.setState
触发组件更新时,并不会调用此生命周期钩子,只要 props 改动或许父组件更新导致子组件重新烘托时,才会履行这个生命周期钩子,看它的姓名也知道它仅和 props 有关。
- 因为组件的 state 改动导致组件更新不会履行
componentWillReceiveProps
; - 履行
shouldComponent
判别组件是否需求更新,需求则履行后续生命周期函数,否则不履行后续生命周期函数; - 在烘托之前,履行
componentWillUpdate
; - 履行烘托办法
render
; - 将更改提交至 DOM 树之后,履行
componentDidUpdate
;
留意:这儿没有运用 getSnapshotBeforeUpdate 这个新增的生命周期函数,因为新增的生命周期函数与被抛弃的生命周期函数一起写入代码中,React 会报错。
新的生命周期函数调用次序
import React from 'react';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0,
}
console.log('App constructor');
}
static getDerivedStateFromProps() {
console.log('App static getDerivedStateFromProps');
return null;
}
shouldComponentUpdate(nextProps) {
console.log('App shouldComponentUpdate');
return true;
}
getSnapshotBeforeUpdate(prevProps) {
console.log('App getSnapshotBeforeUpdate');
return null;
}
componentDidUpdate() {
console.log('App componentDidUpdate');
}
render() {
console.log('App render');
return (
<div>
<div onClick={() => this.setState((count) => ({ count: count + 1 }))}>App</div>
<Child order={1} />
<Child order={2} />
</div>
)
}
}
class Child extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0,
};
console.log(`Child${this.props.order} constructor`);
}
static getDerivedStateFromProps(props) {
console.log(`Child${props.order} static getDerivedStateFromProps`);
return null;
}
shouldComponentUpdate(nextProps) {
console.log(`Child${nextProps.order} shouldComponentUpdate`);
return true;
}
getSnapshotBeforeUpdate(prevProps) {
console.log(`Child${prevProps.order} getSnapshotBeforeUpdate`);
return null;
}
componentDidUpdate() {
console.log(`Child${this.props.order} componentDidUpdate`);
}
render() {
console.log(`Child${this.props.order} render`);
return (
<div onClick={() => this.setState((count) => ({ count: count + 1 }))}>
Child{this.props.order}
</div>
)
}
}
export default App;
当点击文字 Child1 时,其履行成果如下:
当点击文字 Child2 时,其履行成果如下:
能够看到,子组件的状况产生改动,只会履行该子组件对应的生命周期函数,而不会履行其父组件或其兄弟组件的生命周期函数。
- 首要履行
getDerivedStateFromProps
,在这儿能够根据 props 更新 state; - 然后判别该组件是否需求更新,即履行
shouldComponentUpdate
; - 需求更新则履行
render
函数以及后续生命周期函数,否则越过后边生命周期函数的履行; - 在将更改提交至 DOM 树之前履行
getSnapshotBeforeUpdate
,在这儿能够获取 DOM 被更改前的最终一次快照; - 最终在将更改提交至 DOM 树之后履行
componentDidUpdate
。
父组件状况改动
知道了子组件更新时,生命周期函数的履行次序。咱们点击父组件中对应文字,让其调用父组件的 this.setState
办法,触发父组件和子组件的重新烘托,看看父子组件生命周期函数的履行次序。
旧的生命周期函数调用次序
import React from 'react';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0,
}
console.log('App constructor');
}
UNSAFE_componentWillReceiveProps() {
console.log('App componentWillReceiveProps');
}
UNSAFE_componentWillUpdate() {
console.log('App componentWillUpdate');
}
shouldComponentUpdate(nextProps) {
console.log('App shouldComponentUpdate');
return true;
}
componentDidUpdate() {
console.log('App componentDidUpdate');
}
render() {
console.log('App render');
return (
<div>
<div onClick={() => this.setState((count) => ({ count: count + 1 }))}>App</div>
<Child order={1} />
<Child order={2} />
</div>
)
}
}
class Child extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0,
};
console.log(`Child${this.props.order} constructor`);
}
UNSAFE_componentWillReceiveProps(nextProps) {
console.log(`Child${nextProps.order} componentWillReceiveProps`);
}
UNSAFE_componentWillUpdate() {
console.log(`Child${this.props.order} componentWillUpdate`);
}
shouldComponentUpdate(nextProps) {
console.log(`Child${nextProps.order} shouldComponentUpdate`);
return true;
}
componentDidUpdate() {
console.log(`Child${this.props.order} componentDidUpdate`);
}
render() {
console.log(`Child${this.props.order} render`);
return (
<div onClick={() => this.setState((count) => ({ count: count + 1 }))}>
Child{this.props.order}
</div>
)
}
}
export default App;
父子组件生命周期函数调用次序为:
咱们知道 componentWillReceiveProps、shouldComponentUpdate、componentWillUpdate、render 这四个生命周期函数是在 render 阶段调用的,而 componentDidUpdate 生命周期函数是在 commit 阶段调用的。
它们的履行次序和初次烘托中得到的结论相同,仍是满意如下特色:
- 首要顺次履行父组件 render 阶段的生命周期函数;
- 然后顺次履行子组件 render 阶段的生命周期函数;
- 最终穿插履行子组件和父组件 commit 阶段的生命周期函数。
由所以在父组件中调用 this.setState
办法触发的更新,并不会履行它的 componentWillReceiveProps 生命周期函数,而因为父组件更新导致的子组件更新,是会履行子组件的 componentWillReceiveProps 生命周期函数的,这点也在子组件状况改动中提到了。
新的生命周期函数调用次序
import React from 'react';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0,
}
console.log('App constructor');
}
static getDerivedStateFromProps() {
console.log('App static getDerivedStateFromProps');
return null;
}
shouldComponentUpdate(nextProps) {
console.log('App shouldComponentUpdate');
return true;
}
getSnapshotBeforeUpdate(prevProps) {
console.log('App getSnapshotBeforeUpdate');
return null;
}
componentDidUpdate() {
console.log('App componentDidUpdate');
}
render() {
console.log('App render');
return (
<div>
<div onClick={() => this.setState((count) => ({ count: count + 1 }))}>App</div>
<Child order={1} />
<Child order={2} />
</div>
)
}
}
class Child extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0,
};
console.log(`Child${this.props.order} constructor`);
}
static getDerivedStateFromProps(props) {
console.log(`Child${props.order} static getDerivedStateFromProps`);
return null;
}
shouldComponentUpdate(nextProps) {
console.log(`Child${nextProps.order} shouldComponentUpdate`);
return true;
}
getSnapshotBeforeUpdate(prevProps) {
console.log(`Child${prevProps.order} getSnapshotBeforeUpdate`);
return null;
}
componentDidUpdate() {
console.log(`Child${this.props.order} componentDidUpdate`);
}
render() {
console.log(`Child${this.props.order} render`);
return (
<div onClick={() => this.setState((count) => ({ count: count + 1 }))}>
Child{this.props.order}
</div>
)
}
}
export default App;
父子组件生命周期函数调用次序为:
能够看到,换成 getDerivedStateFromProps 后,不管是不是经过调用 this.setState
导致的组件更新,都会履行 getDerivedStateFromProps 生命周期函数。
这儿仍是符合前面说的规则:
- 首要顺次履行父组件 render 阶段的生命周期函数;
- 然后顺次履行子组件 render 阶段的生命周期函数;
- 最终穿插履行子组件和父组件 commit 阶段的生命周期函数。
关于其履行次序原因的了解
为什么在 commit 阶段要先履行父组件的 getSnapshotBeforeUpdate,再履行子组件的 componentDidUpdate?而在 render 阶段却是先履行父组件的 render,再履行子组件的 constructor 呢?
因为 getSnapshotBeforeUpdate 是为了获取 DOM 更新前的一次快照,而 componentDidUpdate 是在 DOM 更新之后履行的。天然要在 DOM 更新之前才干获取每一个组件的 DOM 快照,在 DOM 更新之后才干调用 componentDidUpdate。
假如现已调用了 componentDidUpdate,阐明 DOM 现已更新完了,此刻再调用 getSnapshotBeforeUpdate 还能获取 DOM 更新前的快照吗?明显不可!
至于先履行父组件的 render 再履行子组件的 constructor 是因为:先履行父组件的 render 函数之后,才知道父组件有哪些子组件,接着才干调用对应子组件的 constructor 去结构子组件。这一切都是如此的合理!
参阅文章
- React 组件的生命周期
- Lifecycle, state, getDerivedStateFromProps and Hooks
- 关于react16.7版别之后为什么要废弃几个生命周期的考虑
- 卡教师的硬核React面试题
- React v16.3之后的组件生命周期函数
- 谈谈对 React 新旧生命周期的了解
- React生命周期之——父子组件触发次序
- react生命周期履行次序测验