react-native组件生命周期在navigator上的表现

前言:

本文为初级内容,仅限于刚入门的爱好者看,大牛就不用关注了。关于react-native(RN)的组件生命周期,网上好多高手都已经讲过不少了。看过的的都知道,组件生命周期分为三大部分:挂载(mount)组件部分(包括初始化和挂载)、挂好之后的循环更新部分(即根据state和props的变化循环刷新组件显示)、卸载(unmount)组件部分。知道是一回事,但在具体实现中,这组件何时挂载,何时卸载呢?下面我就用具体实现中最常用的navigator的表现来加深下理解。

测试代码demo地址(RN版本1.7,仅写了ios上的方法):github
这里先偷一张别人的组件生命周期示意图(图片来自链接),很清晰的展示了各个阶段,还有相关的关联方法。

demo代码简介

我这个代码写得很简单,使用navigator组件,然后创建了3个页面(first,second,third),点击页面的按钮进行跳转,除了back采用的navigator.pop外,其他跳转都是使用的push方法。然后每个页面(包括初始化navigator的index.js)都把组件各个阶段可能调用的方法都写在里面进行log输出。如果嫌弃文字太多,可以直接看结论部分。

挂载组件部分

包括组件的初始化,即将加载,渲染(render)界面,加载完毕4个方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
constructor(props){
super(props);
console.log('first init');
}

componentWillMount(){
console.log('first componentWillMount is here');
}

render(){
console.log('first render is here')
}

componentDidMount() {
console.log('first componentDidMount is here');
}

循环更新部分

包括即将接受变动的属性、是否更新组件(这个必须返回true,否则后面就不用玩了)、即将更新组件、更新渲染、更新完毕这5个方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
componentWillReceiveProps(nextprops){
console.log('first componentWillReceiveProps is here');
}

shouldComponentUpdate(){
console.log('first shouldComponentUpdate is here');
return true;
}

componentWillUpdate(){
console.log('first componentWillUpdate is here');
}

render(){
console.log('first render is here')
}

componentDidUpdate(){
console.log('first componentDidUpdate is here');
}

卸载组件部分

只有即将卸载组件这一个方法。

1
2
3
componentWillUnmount(){
console.log('first componentWillUnmount is here');
}

开始初始化

编译运行,模拟器显示first页面,此时的输出为

1
2
3
4
5
6
7
8
index init
index componentWillMount is here
index render is here
first init
first componentWillMount is here
first render is here
first componentDidMount is here
index componentDidMount is here

编译运行,程序会先调用index初始化navigator,然后自动从navigator跳转到预定好的first页面。
我们可以从输出部分看出,首先是index初始化数据,然后index准备加载组件,然后index开始渲染(render),在这个渲染代码中开始执行navigator的相关代码,navigator开始启动first页面。
接下来是first组件的初始化、准备加载、渲染、加载完毕。
first加载完了,才轮到index的渲染代码返回完毕,所以最后是index组件加载完毕输出。

此时组件存活情况:

  • index组件:存活,等待更新。
  • first组件:存活,等待更新。
  • second组件:未初始化。
  • third组件:未初始化。

push跳转

我们点击first页面上的goto second按钮,通过push方法,跳转到下一页(second),此时的输出为

1
2
3
4
5
6
7
8
9
first componentWillReceiveProps is here
first shouldComponentUpdate is here
first componentWillUpdate is here
first render is here
second init
second componentWillMount is here
second render is here
first componentDidUpdate is here
second componentDidMount is here

我们从输出部分看出,首先是first页面走了一遍更新组件的方法,到渲染(render)的时候,开始进入push方法加载第二页(second)。
接下来就是second的初始化、即将加载、渲染。
然后是first的更新完毕。
最后是second的加载完毕。

最后为什么是first更新完毕再轮到second的加载完毕,这点我也不太清楚。有明白的高手解释解释吗?

此时组件存活情况:

  • index组件:存活,等待更新。
  • first组件:在更新一次后,进入存活等待更新状态。
  • second组件:初始化后,进入 存活等待更新状态。
  • third组件:未初始化。

然后我在第二页(second)上点击了跳转第三页(third)页面,输出的内容和上面是一样的,只是组件名称发生了变化(first改成了second,之前的second的改成了third),就不贴了。此时的组件存活情况:

  • index组件:存活,等待更新。
  • first组件: 存活,等待更新 。
  • second组件: 在更新一次后,进入存活等待更新状态。 。
  • third组件: 初始化后,进入 存活等待更新状态。

pop返回

我们在第三页(third)页面,点击back调用pop方法返回到第二页(second),此时的输出为:

1
2
3
4
5
6
second componentWillReceiveProps is here
second shouldComponentUpdate is here
second componentWillUpdate is here
second render is here
third componentWillUnmount is here
second componentDidUpdate is here

也就是说,点击back按钮之后,第二页(second)会进入更新组件流程,重新渲染界面,渲染之后,第三页(third)组件被卸载,最后是第二页更新完毕。

此时组件存活情况:

  • index组件:存活,等待更新。
  • first组件:存活,等待更新。
  • second组件:又更新一次,进入 存活等待更新状态。
  • third组件:被卸载,等待初始化。

如果再次pop,second组件也会被卸载,first组件会更新组件重新渲染。
此时的组件存活情况回到了first刚刚加载的情况。

结论

通过一番简单的测试后,我们可以得出关于navigator以及组件生命周期的几个结论:

  1. 首先初始化,navigator会保持一个类似“组件栈”的东西,里面保存了存活页面组件(此时是第一页)。栈的原理是后进先出,先push进来的,最后才会被pop出去。
  2. push方法会让当前页进入更新组件流程,在render的时候加载下一页组件,加载之后,当前页组件和下一页组件都存活。navigator组件栈添加这个“下一页”组件。
  3. pop方法,首先是让navigator看看自己的组件栈里面有没有“前一页”这个东西,如果有,让前一页进入更新组件流程,在render后卸载当前页组件,更新完毕后,前一页存活,当前页死掉。navigator组件栈里面去掉“当前页”组件。当然,如果组件栈里面就只有一页,那就啥都不干。
  4. 存活的组件的内容一直存在,如果你在push到下一页之前,通过setState或别的办法刷新了当前页,修改了当前页数据(比如我demo里面有个changeState的按钮会修改state.id)。然后push到下一页,再pop回来,这个修改了数据的状态还是存在的。
  5. 刚才那个保留状态仅针对pop方法,如果你是第一页修改数据,然后push到第二页,然后在第二页push回第一页。此时你看到的“第一页”是个新的组件,会走初始化加载组件的步骤。这个时候的navigator组件栈是这样的:第一页(修了数据)→第二页→第一页(新)。一直push的
  6. 只用push会导致中组件栈越来越大,没有任何好处,所以尽量避免导致无限push的情况发生。