资讯专栏INFORMATION COLUMN

浅析 React 生命周期

lansheng228 / 1357人阅读

摘要:在使用组件的进行组件实例化时,得到的便是其返回值。也就是说,如果其子组件的或发生改变时,只会取决于那个组件的方法的返回值。文章为本人原创,原文见本人个博浅析生命周期一浅析生命周期二

Overview

最近常有学习React相关的技术,写了几个React的小Demo,使用 React/Express 技术栈。实在太小,羞于拿出来细说。React 的确是一个值得追随的技术。但React体系实在庞大,我目前仅略知一二。这里要挑出来说的,是React的生命周期机制。Demo的学习过程中,对它的方便、易用之处实在是深有体会,在一些细节处也值得斟酌,在这里做一下记录,便于分享。

如果你接触过React,大概对rendercomponentWillMount等,会相对的熟悉,因为它们再常用不过。但用归用,其中的一些理论上的细节,往往容易在使用的过程中被忽略,使我们多敲了不少代码,心很累的 : )

通俗来讲,React 将组件 component 在web中的形成、修改和渲染等划分为若干个阶段,组成组件的生命周期。在一个完整的生命周期内,一个组件会经过若干个阶段,在特殊的阶段组件会调用一个特别的lifecycle method,即生命周期方法。如下:

constructor(props)

componentWillMount()

render()

componentDidMount()

componentWillReceiveProps(nextProps)

shouldComponentUpdate(nextProps, nextState)

componentWillUpdate(nextProps, nextState)

render( )* //理解上与3. render()略有不同,见下。

componentDidUpdate(prevProps, prevState )

componentWillUnmount( )

值得注意,这些生命周期是React 内置的,在特定条件下就会被调用。而开发者可以做的就是 override(重载)这些方法,以实现想要的功能。

constructor

constructor(props),组件形成时调用。

constructor 函数可理解为组件的构造函数,从组件的类(class) 实例化一个组件实例。这个函数在组件形成时被调用,是所有生命周期函数中最先执行的。在constructor函数内,如有必要,进行state的初始化以及绑定方法;否则可以省去constructor函数的声明。

有以下几点在开发时值得注意:

constructor 函数内,在执行任何statement之前,必须是super() 函数,如果有参数须将参数带上。这点跟Java很像。

在constructor 函数内,this.props 返回 undefined

不要在初试化state时引用props 里的值,否则每当props更新时,都需要在componentWillReceiveProps 函数内对state进行更新。(同时这也涉及到组件state选取的原则,如有需要请阅读Thinking in React)

class App extends Component {
  constructor(props) {
    super(props);//------------(1)
    console.log(this.props);// undefined ------------(2)
    //initialize the state
    this.state = {
      value: "",
      color: props.initialColor  // 不可取  ------------(3)
    }
    //bind methods
    this.handleClick = this.handleClick.bind(this);
  }
}
componentWillMount

componentWillMount(),在组件首次渲染(render)之前调用。

mount安装之意,我们可以理解为组件首次被加载在web中。因此每次页面加载/刷新,或者某个组件第一次加载进入web时可以调用componentWillMount( ) 函数。举个例子,在首次进入文章列表时时,可在 componentWillMount 对所有文章进行查询。这样,在render之前,就能拿到所有文章的数据,以便在render中使用。

在componentWillMount ( ) 函数内,若对this.state进行更新,无法触发重新渲染组件。

class PostList extends Component {
  //...
  //在componentWillMount 组件内获取所有博客列表
  componentWillMount(){
    axios.get("/posts")
         .then(res=>{
           //...
         });
  }
  //在 render 函数内将拿到的博客列表 渲染在页面中
  render(){
    //...
  }
}
Render

render()

render 即 渲染函数,是编写组件代码时,唯一一个必须的函数。该函数须有返回值,返回一个组件,即最终渲染出来的组件。在使用组件的class进行组件实例化时,得到的便是其返回值。

返回值有两种类型:

一个父标签,这个父标签内可以包含若干个子标签,在最外层标签必须只有一个。

false 或者 null,代表不渲染任何DOM

class App extends Component {
  //...
  render(){
    return (
      
//...
) } }

注意:在render函数中只做与返回组件相关的工作,勿在其中对state进行操作,可以保证每次调用render函数,返回的组件都是相同的。否则将加大项目维护成本。

另外,如果shouldComponentUpdate函数返回false,则不执行render函数。关于shouldComponentUpdate将在下面介绍。

componentDidMount

componentDidMount(),一旦组件首次加载完成,便会调用

如果需要对渲染出来的DOM节点做任何操作,可以在此处进行。(提示: this.refs 可获取真实DOM)。

在该组件内设置state将会导致组件被重新渲染。

class App extends Component {
  //..
  componentDidMount(){
    //将会触发组件重新渲染
    this.setState({
      value: "100"
    }):
    //对节点进行操作
      this.refs.div.appendChild(newChild);
  }
  
}

上面对 React生命周期函数中的constructor / componentWillMount / render / componentDidMount 四个函数进行了介绍。下面将继续介绍另外5个方法。在此之前,先总结一下,下面列表中列出的3.render()8.render()的在逻辑上的区别和联系。先上一个列表。

constructor(props)

componentWillMount( )

render( )

componentDidMount( )

componentWillReceiveProps(nextProps)

shouldComponentUpdate(nextProps, nextState)

componentWillUpdate(nextProps, nextState)

render()*

componentDidUpdate(prevProps, prevState)

componentWillUnmount()

「两个」render( )方法的区别

3.render( ) 与 8.render( )*

实质上,这两个方法毫无区别。但这里为什么要提及它们之间的区别呢?其实,它们只是同一函数 render( ) 在组件生命周期的两个不同阶段的不同理解而已。

前一个 render( ) 方法指在组件第一次被加载进入页面时,调用的 render( ) 方法;后一个则指除去第一次,之后调用的 render( ) 方法。

因此,我们更愿意称第一次的 render( ) 方法为 mount( 安装 ),称后一个 render( ) 方法为 re-render ( 重新渲染 ) 。这也是为什么组件首次 render 前后的方法名中带有mount一词的缘故了。

这是 React 的伎俩,或者设计哲学吧。怎么认为都行,我认为很有趣?

下面介绍的方法,都是围绕第二个 render( ) ,即重新渲染 re-render 展开的。

componentWillReceiveProps

componentWillReceiveProps(nextprops)已加载的组件在 props 发生变化时调用。

如果需要通过监听 props 的改变来修改 state 的值,则可以通过重载该函数实现。

需要注意,在有些情况下,组件的 props 未发生改变也会调用该函数。因此如果在该函数内的逻辑,只是想捕获当前 props 与 接收的 nextProps 的不同来做出一些操作,则最好先将 props 与 nextProps 进行比较。

1.在mounting阶段,即首次 render ,不调用 componentWillReceiveProps 方法。理解了两个 render( ) 的不同,便知道这里是为什么了。

2.this.setState({…}) 不触发 componentWillReceiveProps 方法。因为该方法只监听 this.props 的改变,不关心 this.state 值的变化。

class App extends Component {
  componentWillReceiveProps(nextProps){
    //接收的颜色 与 当前颜色不同时
    if (this.props.color !== nextProps.color){
      ...
    }
  }
}
shouldComponentUpdate

shouldComponentUpdate(nextProps, nextState)

返回 true orfalse

要不要更新(重新渲染)组件?浅显易懂。这个方法的返回值决定了,当 props 或者 state 值发生变化时,组件是否重新渲染。两种情况:

返回true,重新渲染。紧接着,继续执行 componentWillUpdate()render()componentDidUpdate()

false,不重新渲染。不再执行任何生命周期函数函数(亦不执行该组件的 render( ) 函数)。但是,并不妨碍其子组件。也就是说,如果其子组件的 props 或 state 发生改变时,只会取决于那个组件的 shouleComponentUpdate ( ) 方法的返回值。道理虽懂,但遇到是可能会犯迷糊,因为开发中常常会遇见组件嵌套的情况,父子组件之间传递同一套 props 或 state,一来二去,谁更新谁不更新,容易迷糊,需要仔细咯。

在绝大部分情况下,当 props 或 state 改变时,都是需要重新渲染组件的。

注意,根据 React 官方 的说法,就算 shouldComponentUpdate( ) 方法返回 false,组件也会重新渲染。需要随时注意官方文档的变化。

class PostList extends Component {
  shouldComponentUpdate(nextProps, nextState){
    //return true;默认
    return false;// 不更新组件
  }
}
componentWillUpdate

componentWillUpdate(nextProps, nextState),当 shouldComponentUpdate( ) 方法返回 true 后调用。

这个方法提供了一个为重新渲染作准备的机会,意思是要在这里,趁接下来的 render( ) 方法重新渲染之前,完成该完成的操作。这个方法在 mount 阶段不会被调用,只在 re-render 阶段被调用。

注意,不要在该方法内调用 this.setState({…}),如有需要,请在 componentWillReceiveProps( ) 方法中完成。养成良好的编程规范。

class App extends Component {
  componentWillUpdate(nextProps, nextState){
    var isLate = this.nextProps.isLate;
    if(isLate){
      //...
    } else {
      //...
    }
  }
}
componentDidUpdate

componentDidUpdate(prevProps, preState),一旦组件首次更新(重新渲染)完成时调用。

因此像 componentDidMount( ) 一样,如果需要对渲染出来的DOM节点做任何操作,可以在此处进行。(提示: this.refs 可获取真实DOM)。

在该组件内设置state将会导致组件被重新渲染。

class App extends Component {
  //..
  componentDidUpdate(){
    //将会触发组件重新渲染
    this.setState({
      value: "100"
    });
    //对节点进行操作
    this.refs.div.appendChild(newChild);
  }
  
}
componentWillUnmount

componentWillUnmount(),在组件即将被卸载(或销毁)之前调用。

在这个方法中,适合做一些清理善后工作。例如清楚timer,取消网络请求,或者清除在 componentDidMount 或 componentDidUpdate 中生成的相关 DOM 节点。

总结

mountre-render 的是有区别的。

mount阶段使用前一部分的四个方法( constructor / componentWillMount / render / componentDidMount),围绕组件首次加载而调用;

后一部分 re-render 相关的,使用 componentWillReceiveProps / shouldComponentUpdate / componentWillUpdate / render / componentDidUpdate ,围绕组件重新渲染而调用。

我总结了一张流程图和一个表格,以表示这些周期函数之间的关系,以及在何种情况下会调用这些函数。

注意:componentWillUnmount 方法未包含其中。

mount props 变化 state 变化
constructor componentWillReceiveProps shouldComponentUpdate
componentWillMount shouldComponentUpdate (return true) ⏬ / 结束
render (return true) ⏬ / 结束 componentWillUpdate
componentDidMount componentWillUpdate render
/ render componentDidUpdate
/ componentDidUpdate /

完。

文章为本人原创,原文见本人个博:
浅析「React」生命周期(一)
浅析「React」生命周期(二)

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/81485.html

相关文章

  • React-生命周期杂记

    摘要:前言自从发布之后,更新速度日新月异,而生命周期也随之改变,虽然原有的一些生命周期函数面临废弃,但理解其背后更新的机制也是一种学习在这里根据官方文档以及社区上其他优秀的文章进行一个对于生命周期的总结,大致上分为以下三个模块新老生命周期的区别为 前言 自从React发布Fiber之后,更新速度日新月异,而生命周期也随之改变,虽然原有的一些生命周期函数面临废弃,但理解其背后更新的机制也是一种...

    KoreyLee 评论0 收藏0
  • 【V8引擎】浅析Chrome V8引擎中的垃圾回收机制和内存泄露优化策略

    摘要:一前言的垃圾回收机制使用垃圾回收机制来自动管理内存。垃圾回收器只会针对新生代内存区老生代指针区以及老生代数据区进行垃圾回收。分别对新生代和老生代使用不同的垃圾回收算法来提升垃圾回收的效率。 V8 实现了准确式 GC,GC 算法采用了分代式垃圾回收机制。因此,V8 将内存(堆)分为新生代和老生代两部分。 一、前言 V8的垃圾回收机制:JavaScript使用垃圾回收机制来自动管理内存。垃...

    qingshanli1988 评论0 收藏0
  • 库,组件,框架 - 收藏集 - 掘金

    摘要:哈哈,我理解,架构就是骨架,如下图所示译年月个有趣的和库前端掘金我们创办的使命是让你及时的了解开发中最新最酷的趋势。 翻译 | 上手 Webpack ? 这篇就够了! - 掘金译者:小 boy (沪江前端开发工程师) 本文原创,转载请注明作者及出处。 原文地址:https://www.smashingmagazine.... JavaSrip... 读 Zepto 源码之代码结构 - ...

    tommego 评论0 收藏0
  • 2017-06-13 前端日报

    摘要:前端日报点关注,不迷路精选前端团队工作流迁移记译新语法私有属性知乎专栏前端每周清单大前端技术生命周期模型发布面向生产环境的前端性能优化模块实现入门浅析知乎专栏中文一个线下沙龙中国最大的前端技术社区单页面博客从前端到后端基于 2017-06-13 前端日报 点关注,不迷路:-P 精选 ESLint v4.0.0 released - ESLint - Pluggable JavaScri...

    曹金海 评论0 收藏0

发表评论

0条评论

lansheng228

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<