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

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

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

教程简介

  • 1、阅读对象

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

正文

1、什么是生命周期

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

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

2、React 的生命周期

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

ReactLifeCycler.jpeg

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

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

1、mounting: 插入真实 DOM </br>
2、updating: 正在被重新渲染 props 或 state 改变 </br>
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 地址

https://github.com/tigerchain/react-lesson/tree/master/lesson02/11-lifecycle

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容