本来想自己写一篇关于
React
组件生命周期的博客做个总结,但看到这篇 ReactJs component lifecycle methods — A deep dive ,觉得它写的还是蛮清晰易懂,肯定比我写的好,所以就不再自己去搜肠刮肚了,直接翻译过来。。。
本篇博客适用于 React 16.3
及以下版本。
React 16.3
版本对组件的生命周期进行了大幅的改变,所以如果你在使用 16.3 之后的版本,可以查看这篇博客。
React 和 它的用户界面
“ ReactJS
是一个用于构建用户界面的 JavaScript
库 ”,这是 React
的自我介绍。
什么是用户界面
用户通过在 UI
组件上的单击,悬停,按键或触发许多其他类型的事件来与应用程序交互。
所有UI
组件都在浏览器中生成,并在某个时间点被删除。
整个界面由唯一的上帝管理——也就是用户。
用户界面是一个自由的游乐场,用户可以在其中做任何事情,而 React 就是用来帮助我们建造这个游乐场。
生命周期函数是什么,有什么用?
我们周围的所有东西都有生命周期——它们出生,成长,并在某个时刻死去,例如一棵树,任何的软件应用,你自己,一个 div
容器或者浏览器里的 UI
组件,都会经历出生,更新和成长,直到死去。
生命周期函数,就是在生命周期的不同阶段可以调用的各种函数。
假设我们在写一个 YouTube 应用,很明显我们的应用将会通过网络来缓冲视频,同时也使用电源里的电量(我们暂且假设我们的应用只做这两件事情)。
如果用户在开始播放视频后,切换到另一个应用程序,那么作为一个优秀的程序员,我们应该确保以最有效的方式使用网络和电池资源。
所以,每当用户切换到另一个应用程序时,我们应该停止/暂停视频的缓冲,从而停止使用网络和电池。
这就是 React
的生命周期函数能够做到的,通过生命周期函数,开发者可以决定在 UI
界面特定的初始化,更新和销毁的时刻做些什么事情,从而开发出优质的应用。
深入理解组件的生命周期可以帮助你开发出更好的
React
用户界面。
React 组件的四个阶段
React
组件像世上的其他东西一样,有以下几个生命阶段:
- 初始化
Initialization
- 挂载
Mounting
- 更新
Update
- 销毁
Unmounting
下面这张图片直观的展示了 React
组件的各个生命阶段和生命周期函数:
为了更直观的解释生命周期钩子函数,我们将创建一个叫 Contra 的音乐播放器 React
应用。
让我们开始吧。
(一)初始化 Initialization
在这个阶段,React
设置组件的初始state
和defaultProps
,从而为组件即将到来的艰辛历程作准备。
Contra 音乐播放器在开始时是下面这样:
1 | class ContraMusicPlayer extends React.Component { |
组件在构造函数中初始化 state
,随后你可以使用 setState
来改变它。
defaultProps
类静态属性定义了组件的所有 props
的默认值,可以被外部传入的 props
值覆盖。
当使用类似 <ContraMusicPlayer />
来渲染 Contra 音乐播放器时,它将以 70% 的音量,暂停状态和 drak 暗色主题开始。
当使用类似 <ContraMusicPlayer theme="light" />
来渲染 Contra 音乐播放器时,它将以 70% 的音量,暂停状态和 light 亮色主题开始。
(二)挂载 Mounting
在准备好初始所需的 state
和 props
之后,我们的 React
组件已经准备好挂载到浏览器的 DOM
树中了。
这个阶段提供了挂载前和挂载后的钩子方法。在这个阶段有如下的方法会被调用:
componentWillMount 在
React
组件将要挂载到DOM
中时执行,也就是说,在这个方法之后,组件将会被挂载。所有你想在组件挂载之前做的事情,都应该定义在这个函数中。这个函数在整个生命周期中只会在初次渲染之前被调用一次。
提示:
componentWillMount
经常被用来初始化states
和props
,因此目前有比较大的争议它是否应该合并到组件类的构造函数中。render 挂载组件到浏览器中。它是个纯函数,也就是说,给它相同的输入,它总是返回相同的输出。
我们的音乐播放器应用的
render
方法大概像下面这样:JavaScript 1
2
3
4
5
6
7
8
9render() {
<div>
<PlayHeader>
<Status/>
<VolumeBar/>
<SeekBar/>
</PlayHeader>
</div>
}componentDidMount 是在组件已挂载到 DOM 后会被调用的钩子函数。
这个函数在整个生命周期中只会在初次渲染之后被调用一次。
在这个函数中,我们已经可以接触到
DOM
, 因此我们可以在这个函数中初始化一些需要操作DOM
的JS
库,例如D3
或者JQuery
。示例: 在我们的音乐播放器中,我们想绘制歌曲的波形,在这个方法中我们可以继承
D3
或者其他第三方JS
库。下面是一个在此钩子函数中引入和初始化
highcharts
的例子:JavaScript 1
2
3
4
5
6
7
8
9
10
11
12componentDidMount() {
if (this.props.modules) {
this.props.modules.forEach(function (module) {
module(Highcharts);
});
}
// Set container which the chart should render to.
this.chart = new Highcharts[this.props.type || "Chart"](
this.props.container,
this.props.options
);
}我们应该在哪里进行
API
调用?API
调用应该在componentDidMount
方法中, 可以参考这篇文章来了解更多关于如何进行API
调用的相关内容。
(三)更新 Update
这个阶段在 react 组件已经处于浏览器中并开始响应和接受新值进行更新的时候。
组件有两种被更新的方式,发送新的 props
和更新 state
。
让我们看看在调用setState
更新当前 state
时会触发哪些钩子函数:
shouldComponentUpdate 告诉
React
当组件接受到新的props
或者state
被更新时,React
是应该重新渲染组件或者跳过渲染 ?此函数提出了一个问题, 组件应该被更新吗?
因此此函数应该返回一个布尔值
true
或者false
,来指明组件应该被重新渲染还是跳过。默认的,此生命周期函数返回
true
。示例: 下面是一个只有当
props
的status
属性变化时才重新渲染组件的小例子:JavaScript 1
2
3
4shouldComponentUpdate(nextProps, nextState) {
let shouldUpdate = this.props.status !== nextProps.status;
return shouldUpdate;
}这个方法通常用于当渲染函数
render
是一个很重的方法时,你需要在某些时刻它不被自动调用。举个例子,比如在组件的一次渲染中,会生成上千个素数,我们就可以通过这个方法,来控制只在我们需要时进行组件的 render。
componentWillUpdate 在
shouldComponentUpdate
返回true
时被调用。此方法仅用来为即将进行的render
做准备,类似于componentWillMount
或者constructor
。如果在渲染某些项目和数据之前,需要进行一些运算,这个函数中是做这些的合适位置。
render 组件被渲染
componentDidUpdate 在更新后的组件被渲染到
DOM
之后调用。这个函数通常用于确定和触发第三方库的更新和重载。
下面这个列表,是当父组件传入了新的 props
时子组件中将会被调用的生命周期函数:
componentWillReceiveProps 当
props
已经改变同时不是首次render
时被调用。在某些时候
state
依赖于props
,因此当porps
改变时,state
也应该被同步改变。这个方法就是同步props
到state
逻辑的位置。state
不存在类似的方法,因为props
只在组件内部可读,永远不会依赖于组件的state
。
示例: 下面是一个当 props
变化时 state
保持同步的例子:
1 | componentWillReceiveProps(nextProps) { |
随后的生命周期钩子调用,和我们在上面提到当setState
时的生命周期函数调用完全一致:
shouldComponentUpdate
componentWillUpdate
render
componentDidUpdate
(四)销毁 Unmounting
在这个阶段,组件不再被需要,即将在 DOM
中被销毁。在此阶段以下方法将会被调用:
componentWillUnmount 此函数是生命周期函数的最后一个。
它在组件即将被从
DOM
中移除前被调用.示例: 在这个函数中,我们可以进行在组件被销毁之前执行相应的清理工作。例如退出登录,清除用户数据和认证
token
等。JavaScript 1
2
3
4
5componentWillUnmount() {
this.chart.destroy();
this.resetLocalStorage();
this.clearSession();
}