14、React系列之 组件的生命周期

505 阅读8分钟

版权声明:本文为博主原创文章,未经博主允许不得转载。

PS:转载请注明出处 作者:TigerChain 地址:http://www.jianshu.com/p/e3d1ecfb6312 本文出自TigerChain简书 git 地址:github.com/githubchen0…

教程简介

  • 1、阅读对象

本篇教程适合初学者,老鸟直接略过,如果有误,欢迎指出,谢谢。

正文

1、什么是生命周期

生命周期指的是一个对象的生老病死。当然生命周期又为广义和狭义的。具体分为以下几类。

  • 1、动物的生命周期:从初生到死亡。
  • 2、产品的生命周期:从开始到淘汰。(一般指提市场寿命)
  • 3、语言或平台中某个组件的生命周期:比如 activity 的生命周期,指的就是 activity 的创建到销毁的过程。
  • 4、其它生命周周期

2、React 的生命周期

先看 React 的生命周期图,虽然你可能不理解,通过后面的学习再回头来看这幅图,就会恍然大悟。

ReactLifeCycler.jpeg

一、这里说的 React 的生命周期指的就是 React 组件的生命周期。React Component 通过定义几个函数来控制组件在生命周期每个阶段的功能。

总的来说 React 组件的生命周期有三种状态

1、mounting: 插入真实 DOM
2、updating: 正在被重新渲染 props 或 state 改变
3、unmounting: 卸载 移除真实 DOM

在 React 中为每个状态又分别提供了两种处理函数,就是 will 和 did , will 是进入状态之前调用的, did 是进入状态之后调用的.

总的来说,三种状态对应不同的函数

  • mounting 对应的函数
constructor()
componentWillMount()
render()
componentDidMount()
  • updating 对应的函数
componentWillReceiveProps()
shouldComponentUpdate()
componentWillUpdate()
render()
componentDidUpdate()
  • unmounting 对应的函数
componentWillUnmount()

PS:一个组件中 redner() 方法是必须的。

二、和组件生命周期相关的函数有

上面笼统的说了一下和组件生命周期相关的方法,我们下面逐一来说明。

  • 1、构造函数
constructor(props, context)

构造函数,在组件创建的时候调用一次。

  • 2、componentWillMount()
void componentWillMount()

在组件挂载之前( render()之前 )调用。可以用于加载 Loading 条了等操作。

  • 3、render
render() ;

render 方法顾名思义,就是渲染的意思,它有一个返回值,就是返回一个 React 元素(自定义组件或是呈现 DOM 组件的元素),当然如果你什么也不想返回就可以 return null 或 return false (组件就不显示了),render() 里面应该是纯净的 (不能在里面修改组件的状态,也不能在里面请求服务器等耗时操作[ componentDidMount 方法中进行耗时操作]),只是用来渲染组件。

  • 4、componentDidMount()
void componentDidMount()

在组件挂载之后调用。可以用于耗时操作(请求服务器或定时器等)。

  • 5、componentWillReceiveProps()
void componentWillReceiveProps(nextProps)

这里的 props 是父组件传递给子组件的。父组件发生 render 的时候就会调用子组件的 componentWillReceiveProps (不管 props 有没有更新,也不管父子组件之间有无数据交换),在这个回调函数里面,你可以根据属性的变化,通过调用this.setState()来更新你的组件状态,旧的属性还是可以通过this.props来获取,这里调用更新状态是安全的,并不会触发额外的render调用。

  • 6、shouldComponentUpdate()
bool shouldComponentUpdate(nextProps, nextState)

这个方法在组件挂载之后,当你调用 setState() 方法的时候都会调用此方法用来判断是否要重新渲染组件,如果返回 true 则需要重新渲染,如果是 false 则不会。我们可以在这个方法中处理只是数据改变,界面不改变的情况,用来优化渲染效率。

  • 7、componentWillUpdate()
void componentWillUpdate(nextProps, nextState)

顾名思义,就是组件更新前被调用的方法,具体是当 props 和 state 发生改变的时候就会执行此方法并且在 redner() 方法之前进行。这个方法也在 shouldComponentUpdate() 方法返回 true 的时候就会调用,返回 false 不会调用, 使用此方法为更新前做一些准备。初始化渲染的时候不会调用这个方法。

PS:注意一点,在这个方法中不能会用 this.setState() 方法来修改状态。当这个函数调用之后,就会把 nextProps 和 nextState 分别设置到 this.props 和 this.nextState 方法中,接着就会调用 render() 方法来渲染了。

  • 8、componentDidUpdate()
void componentDidUpdate()

组件渲染后就会调用 componentDidUpdate() 方法,但是首次 render() 的时候是不会调用的,首次 render() 的时候调用的是 componentDidMount() 方法。

componentWillMount()、componentDidMount 和 componentWillUpdate()、componentDidUpdate 是一一对应的,前者是在挂载的时候会被调用,但是后者是每次更新渲染之后都会调用。

  • 9、componentWillUnmount()
void componentWillUnmount()

组件被卸载的时候调用,一般情况下在 componentWillUnmount() 说法中清除注册的事件,比如取消定时器,网络请求等。

3、React 中的更新方式

总的来说,在 React 中,想要 redner 有以下四种方式。

假使 shouldComponentUpdate 都是返回 true 的情况下

  • 1、首次初化 render
  • 2、调用 this.setState() 方法 (我们知道它是异步的,不是每调用一次 setState 都会触发一次 render ,React 有时可能会合并操作,再一次进行 render)
  • 3、props 发生改变,但是一般不建议直接调用 this.setProps()
  • 4、手动调用 this.forceUpdate(不推荐这样使用)

到目前为止,我们大体对 React 组件的生命周期有一个了解了,下面我们通过 Demo 来直观的感觉一下。

4、实例演示组件的生命周期

经过上面的学习,我们大体对 React 组件的生命周期有了一定的认识,下面我们写一个 Demo 来验证一下。

我们可以使用前面学过的知识使用 yarn + webpack + ES6 来创建项目,如果不懂这些知识,可以看看前面相关的章节。以下 Demo 是在 mac 环境下开发的。

1、打开命令行,新建 lifecycle 目录

mkdir lifecycle

2、进入到 lifecycle 文件夹下,并新建 public 和 app 目录。

cd lifecycle
mkdir public
mkdir app

3、分别在 app 中 和 public 中新建 index.html 和 main.js

# index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>React-LifeCycle</title>
</head>
<body>

<div id="container"></div>
<script src="bundle.js"></script>
</body>
</html>

# main.js

import React from 'react' ;
import ReactDOM from 'react-dom' ;

import LifeCycle from './LifeCycle.js' ;

/**
 * 定义一个父组件
 */
class Main extends React.Component{

  constructor(props){
    super(props) ;
    this.state = {
      name:'junjun'
    }
  }

   render(){
     return(
       <LifeCycle
         umout={this.unmoutComponent}
         name={this.state.name}
         testComponentWillReceiveProps={this.changeState.bind(this)}
       />
     ) ;
   }

   /**
    * 卸载组件
    */
   unmoutComponent(){
     // 这里卸载父组件也会导致卸载子组件
     ReactDOM.unmountComponentAtNode(document.getElementById("container"));
   }

/**
 * 通过修改 state 来修改 props 用来测试 componentWillReceiveProps 方法
 */
   changeState(){
     this.setState({
       name:'TigerChain11111'
     })
   }

}


ReactDOM.render(
  <Main />,
  document.getElementById('container')
) ;

在这里我一口气写完了,注释非常详细,如果你学习过前面章节的知识,那么这个很容易看懂。

4、在 app 中再新建 LifeCycle.js

# LifeCycle.js

import React, { Component, PropTypes } from 'react';

/**
 * 定义一个生命周期的组件
 */

export default class LifeCycle extends Component {

  constructor(props) {
    super(props);

    console.log("~~ Initial render ~~");
    console.log("~~ constructor ~~");

    alert("~~ Initial render ~~") ;
    alert("~~ constructor ~~") ;
    this.state = {
      name:'TigerChain'
    }
  }
  /**
   * 在挂载之前调用
   */
  componentWillMount(){
    console.log("~~ componentWillMout ~~");

    alert("~~ componentWillMout ~~") ;
  }

  render() {
    console.log("~~ render ~~");
    alert("~~ render ~~") ;
    return (
      <div>
        LifeCycle Demo <p/>
        <button onClick={this._changeProps.bind(this)}>changeProps</button><p/>

        <button onClick={this._setState.bind(this)}>setState</button><p/>

        <button onClick={this._forceWithUpdate.bind(this)}>forceWithUpdate</button><p/>

        <button onClick={this._unmout.bind(this)}>unmout</button><p/>

        <button onClick={this.koukou.bind(this)}>parentChangeProps</button> <p/>

    </div>);
  }
  /**
   * 测试 ComponentWillReceiveProps 方法
   */
  koukou(){
    this.props.testComponentWillReceiveProps() ;
  }

  /**
   * 在挂载之后调用
   */
  componentDidMount(){
    console.log("~~ componentDidMout ~~");
    alert("~~ componentDidMout ~~") ;
  }

  /**
   * 组件挂载之后 当调用 setState() 的时候  如果此方法返回 true ,则会重新渲染,否则不会
   */
  shouldComponentUpdate(nextProps, nextState){
      console.log("~~ shouldComponentUpdate ~~");
      console.log("shouldComponentUpdate nextState",nextState);
      alert("~~ shouldComponentUpdate ~~"+nextState) ;
      return true ;
  }

  /**
   * props 改变的时候调用
   */
  componentWillReceiveProps(nextProps){
    console.log("~~ componentWillReceiveProps ~~");
    alert("~~ componentWillReceiveProps ~~") ;
  }
  /**
   * shouldComponentUpdate 返回 true 的时候 将要更新
   */
  componentWillUpdate(nextProps, nextState){
    console.log("componentWillUpdate nextState",nextState);
    console.log(this.state.name);
    console.log("~~ componentWillUpdate ~~");

    alert("~~ componentWillUpdate ~~") ;
  }

  /**
   * 组件已经更新
   */
  componentDidUpdate(){
    console.log("~~ componentDidUpdate ~~");
    alert("~~ componentDidUpdate ~~") ;
  }

  /**
   * 组件将要卸载
   */
  componentWillUnmount(){
    console.log("~~ componentWillUnmount ~~");
    alert("~~ componentWillUnmount ~~") ;
  }

  /**
   * 组件卸载的方法
   */
  _unmout(){
    this.props.umout();
  }

  _forceWithUpdate(){
    this.forceUpdate();
  }
  /**
   * 修改属性
   */
  _changeProps(){
    this.setState({
      name:'TigerChain1'
    })
  }

  /**
   * 修改 state 方法
   */
  _setState(){
    var that = this ;
    if((that.state.name === "TigerChain1") || (that.state.name="TigerChain")){
      that.setState({
        name:'TigerChainJun'
      });
    }
  }
}

我们在这里把组件生命周期的 demo 就写完了,具体可以看注释。

5、添加 react react-dom babel 等插件依赖

yarn add react react-dom babel-core babel-loader babel-preset-es2015 babel-preset-react babel-preset-stage-0 webpack webpack-dev-server --dev

这里也不多说,如果不懂的话,可以查看 webpack 这一节。

6、在项目根目录新建 .babelrc 文件并输入以下内容

{
  "presets": ["react", "es2015","stage-0"]
}

7、在根目录新建 webpack.config.js 并配置

# webpack.config.js

module.exports = {
  entry: __dirname+"/app/main.js",
  output:{
    path:__dirname + "/public",
    filename:"bundle.js"
  },
  module: {
    loaders: [
      //babel配置
    {
      test:/\.js$/,
      exclude: /node_modules/,
      loader: 'babel-loader'
    }
  ]
  },
  devServer:{
    contentBase: "./public",//本地服务器所加载的页面所在的目录
    historyApiFallback: true,//不跳转
    inline: true//实时刷新
  }
}

8、配置脚本 package.json

{
  "name": "11-lifecycle",
  "version": "1.0.0",
  "main": "index.js",
  "author": "TigerChain",
  "license": "ISC",
  "scripts":{
    "start":"webpack-dev-server --progress --port 8888"
  },
  "devDependencies": {
    "babel-core": "^6.24.0",
    "babel-loader": "^6.4.1",
    "babel-preset-es2015": "^6.24.0",
    "babel-preset-react": "^6.23.0",
    "babel-preset-stage-0": "^6.22.0",
    "react": "^15.4.2",
    "react-dom": "^15.4.2",
    "webpack": "^2.3.2",
    "webpack-dev-server": "^2.4.2"
  }
}

9、运行查看 yarn start 并在浏览器中输入 localhost:8888 如果不出什么问题,那么我们就可以看到如下界面

lifecycle.gif

图中就完整的展示了 React 组件的生命周期。我们回过头再去看前面的生命周期图,就能很好的理解了。请大家务必照着 demo 敲一遍代码,以便加深理解。(老鸟直接略过)

到此为止,我们的 React 组件的生命周期就讲完了。

如果喜欢,就点个喜欢吧。

Demo 地址

github.com/githubchen0…