原文地址:《React 的 lifecycle(生命周期)》
在进行lifecycle前,先来说一下 React 的虚拟DOM(virtual Dom Tree)。
首先了解这一点:我们看到的每一个网页在数据结构上是一个 DOM Tree。html是根节点,然后 head、body…树枝不断的增多,再经过浏览器的渲染,就成了我们看到网页。在之前的Web开发,都是直接操作的这个 DOM Tree。而使用 React 开发,我们并不直接和这个实际的 DOM Tree 打交道,更多的是和 React virtual Dom 打交道。
我们看到的每个 React 应用,都是由 React virtual Dom 对 DOM Tree 映射而成的。也就是说,每一个 React 应用,首先都是 构建出它自己的 virtual Dom,然后是 React 通过其算法,以 Virtual Dom 为基础,构建出 DOM Tree(看到的网页)。
图示:(配图来源网络)
由上面的解释,也就可以理解为什么每个 React Component 的 Render 必须返回一个根节点。如果返回两个甚至多个节点,那应该选哪个节点挂在“树枝”上呢?
而 React Component 的 lifecycle(生命周期) 就是指 Component 被挂载到 虚拟DOM(Tree)、在 虚拟DOM(Tree)上存活状态 和 从 虚拟DOM(Tree)上卸载的过程。
Component Mounting(组件挂载)
组件挂载,就是组件被插入虚拟DOM。
ReactDOM.render(
element,
container,
[callback]
)
ReactDOM.render(<TodoApp />, document.getElementById('app'));
If the optional callback is provided, it will be executed after the component is rendered or updated.
React 使用 ReactDOM.render 方法将 React Component(React 根组件)挂载到 HTML DOM 上。在这个过程中,React 实现了 virtual Dom 到 HTML DOM 的映射。
TodoApp
是 React virtual Dom 的根组件。TodoApp
及其所使用到的 React Component 形成 React virtual Dom,这样每一个 Component 就被挂载到了 React virtual Tree 上,开始其 lifecycle(生命周期)。
每个被挂载到 virtual Tree 的 Component,都可以说是“存活”在 virtual Tree 上。相应的 React 对其 Component 的挂载,抽象出以下方法:
- constructor()
constructor(props) - componentWillMount()
- render()
- componentDidMount()
These methods are called when an instance of a component is being created and inserted into the DOM;
constructor
constructor()
是 Component 的构造函数,其接受的参数是 Component 的 Props(属性)。constructor()
(构造函数)会为 Component 设置 State,也就说在 Component “出生”之时,就决定了它自己能够干什么。虽然由于 JS 的灵活性,State 能够在 Component 的生命周期中增加或减少,但还是不要这么做的好。
当 Component 被构建出来之后,它就要被挂载到 virtual Tree 上–componentWillMount()
。
componentWillMount
componentWillMount() is invoked immediately before mounting occurs. It is called before render(), therefore setting state synchronously in this method will not trigger a re-rendering. Avoid introducing any side-effects or subscriptions in this method.
This is the only lifecycle hook called on server rendering. Generally, we recommend using the constructor() instead.
当 Component 被挂载到 virtual tree 上后,就要执行 render()
方法。Component 通过 render()
方法将其要映射的 HTML DOM 告诉其父组件,完成 virtual tree 向 HTML DOM Tree 的映射。
render
render()
被调用后,React Component 还会通过 componentDidMount()
告诉我们组件已经挂载到 virtual tree 上。
componentDidMount
componentDidMount()
is invoked immediately after a component is mounted. Initialization that requires DOM nodes should go here. If you need to load data from a remote endpoint, this is a good place to instantiate the network request. Setting state in this method will trigger a re-rendering.,
除了render()
方法,constructor()
、componentWillMount()
、componentDidMount()
方法在 Component 生命周期中都只会被调用一次。
Component Updating(组件更新)
An update can be caused by changes to props or state. These methods are called when a component is being re-rendered:
当 Component 挂载到 virtual tree 后。要想 Component 发生改变,就必须使 Component 的 State 或者 Props 发生改变。这里有个小坑:这里 State、Props 发生改变,是指 State 或者 Props 的成员值的内存地址发生改变。
componentWillReceiveProps
当 Component Props 发生改变,就会触发 componentWillReceiveProps(nextProps)
。这里的接受的参数叫 nextProps
。React 给的名字很有意思:nextProps
以及下面的prevProps
, 和当前的 props
作区分。只是用名称就做了 时间上 的解释。
当 Component 接受到新的 Props 时,在 componentWillReceiveProps
函数会触发,Component 就可以根据新的 Props 做出相应的动作–根据 Props 设置 State 之类的。
shouldComponentUpdate()
shouldComponentUpdate(nextProps, nextState)
shouldComponentUpdate() is invoked before rendering when new props or state are being received. Defaults to true. This method is not called for the initial render or when forceUpdate() is used.
Returning false does not prevent child components from re-rendering when their state changes.
React 给了 shouldComponentUpdate
方法,可以让开发者根据 nextProps、nextState和 Props、State做对比,自行选择 Component 是不是需要重新渲染(render)。所以, Props、State 改变并不一定会引起 Component UI 的变化,Component UI 改变,Props 或
State 则一定改变。这里标明是 UI 的改变。Props、State 改变,Component 就会改变,不过可以使用 shouldComponentUpdate
方法来选择是否对 Dom 做映射。
componentWillUpdate()
componentWillUpdate(nextProps, nextState)
componentWillUpdate() is invoked immediately before rendering when new props or state are being received. Use this as an opportunity to perform preparation before an update occurs. This method is not called for the initial render.
Note that you cannot call this.setState() here. If you need to update state in response to a prop change, use componentWillReceiveProps() instead.
render()
shouldComponentUpdate 返回 true 后,React Component 会再次执行 render 方法,来进行 virtual Dom 到 HTML DOM 的映射。从而使我们获得不同 UI。
componentDidUpdate()
componentDidUpdate(prevProps, prevState)
componentDidUpdate() is invoked immediately after updating occurs. This method is not called for the initial render.
Use this as an opportunity to operate on the DOM when the component has been updated. This is also a good place to do network requests as long as you compare the current props to previous props (e.g. a network request may not be necessary if the props have not changed).
componentDidUpdate() will not be invoked if shouldComponentUpdate() returns false.
Component Unmounting(组件卸载)
最后就是 Component 在 virtual Dom tree 上被卸载–componentWillUnmount()
。那 Component 什么时候被卸载(销毁)呢?当 Component 不再和 virtual Dom tree 有任何联系时,Component 就会被卸载,Component “切断”与 virtual Dom Tree 的联系时,componentWillUnmount()
就会被调用。
componentWillUnmount() is invoked immediately before a component is unmounted and destroyed. Perform any necessary cleanup in this method, such as invalidating timers, canceling network requests, or cleaning up any DOM elements that were created in componentDidMount
This method is called when a component is being removed from the DOM:
lifecycle && Typescript
interface ComponentLifecycle<P, S> {
componentWillMount?(): void;
componentDidMount?(): void;
componentWillReceiveProps?(nextProps: Readonly<P>, nextContext: any): void;
shouldComponentUpdate?(nextProps: Readonly<P>, nextState: Readonly<S>, nextContext: any): boolean;
componentWillUpdate?(nextProps: Readonly<P>, nextState: Readonly<S>, nextContext: any): void;
componentDidUpdate?(prevProps: Readonly<P>, prevState: Readonly<S>, prevContext: any): void;
componentWillUnmount?(): void;
}
使用 Typescript 开发 React,可以让我们很清楚的知道函数参数、类型和其返回值。可以帮助我们避免开发时错误。有效的帮助团队快速的构建 健壮的 React APP。