首页 男生 其他 深入浅出React和Redux

2.3.1 装载过程

深入浅出React和Redux 程墨 8114 2021-04-06 02:29

  您可以在百度里搜索“深入浅出React和Redux 艾草文学(www.321553.xyz)”查找最新章节!

  

  2.3.1 装载过程

  我们先来看装载过程,当组件第一次被渲染的时候,依次调用的函数是如下这些:

  ·constructor

  ·getInitialState

  ·getDefaultProps

  ·componentWillMount

  ·render

  ·componentDidMount

  我们来逐个地详细解释这些函数的功能。

  1.constructor

  我们先来看第一个constructor,也就是ES6中每个类的构造函数,要创造一个组件类的实例,当然会调用对应的构造函数。

  要注意,并不是每个组件都需要定义自己的构造函数。在后面的章节我们可以看到,无状态的React组件往往就不需要定义构造函数,一个React组件需要构造函数,往往是为了下面的目的:

  ·初始化state,因为组件生命周期中任何函数都可能要访问state,那么整个生命周期中第一个被调用的构造函数自然是初始化state最理想的地方;

  ·绑定成员函数的this环境。

  在ES6语法下,类的每个成员函数在执行时的this并不是和类实例自动绑定的。而在构造函数中,this就是当前组件实例,所以,为了方便将来的调用,往往在构造函数中将这个实例的特定函数绑定this为当前实例。

  以Counter组件为例,我们的构造函数有这样如下的代码:

  this.onClickIncrementButton = this.onClickIncrementButton.bind(this);

  this.onClickDecrementButton = this.onClickDecrementButton.bind(this);

  这两条语句的作用,就是通过bind方法让当前实例中onClickIncrementButton和onClickDecrementButton函数被调用时,this始终是指向当前组件实例。

  注意

  在某些教程中,大家还会看到另一种bind函数的方式,类似下面的语句:

  this.foo = ::this.foo;

  等同于下面的语句:

  this.foo = this.foo.bind(this);

  这里所使用的两个冒号的::操作符叫做bind操作符,虽然有babel插件支持这种写法,但是bind操作符可能不会成为ES标准语法的一部分,所以,虽然这种写法看起来很简洁,我们在本书中并不使用它。

  2.getInitialState和getDefaultProps

  getInitialState这个函数的返回值会用来初始化组件的this.state,但是,这个方法只有用React.createClass方法创造的组件类才会发生作用,本书中我们一直使用的ES6语法,所以这个函数根本不会产生作用。

  getDefaultProps函数的返回值可以作为props的初始值,和getInitialState一样,这个函数只在React.createClass方法创造的组件类才会用到。总之,实际上getInitialState和getDefaultProps两个方法在ES6的方法定义的React组件中根本不会用到。

  假如我们用React.createClass方法定义一个组件Sample,设定内部状态foo的初始值为字符串bar,同时设定一个叫sampleProp的prop初始值为数字值0,代码如下:

  const Sample = React.createClass({

  getInitialState: function() {

  return {foo: 'bar'};

  },

  getDefaultProps: function() {

  return {sampleProp: 0}

  })

  });

  用ES6的话,在构造函数中通过给this.state赋值完成状态的初始化,通过给类属性(注意是类属性,而不是类的实例对象属性)defaultProps赋值指定props初始值,达到的效果是完全一样的,代码如下:

  class Sample extends React.Component {

  constructor(props) {

  super(props);

  this.state = {foo: 'bar'};

  }

  });

  Sample.defaultProps = {

  return {sampleProp: 0}

  };

  React.createClass已经被Facebook官方逐渐废弃,但是在互联网上还能搜索到很多使用React.createClass的教材,虽然强烈建议不再要使用React.createClass,但是如果读者你真的要用的话,需要注意关于getInitialState只出现在装载过程中,也就是说在一个组件的整个生命周期过程中,这个函数只被调用一次,不要在里面放置预期会被多次执行的代码。

  3.render

  render函数无疑是React组件中最重要的函数,一个React组件可以忽略其他所有函数都不实现,但是一定要实现render函数,因为所有React组件的父类React.Component类对除render之外的生命周期函数都有默认实现。

  通常一个组件要发挥作用,总是要渲染一些东西,render函数并不做实际的渲染动作,它只是返回一个JSX描述的结构,最终由React来操作渲染过程。

  当然,某些特殊组件的作用不是渲染界面,或者,组件在某些情况下选择没有东西可画,那就让render函数返回一个null或者false,等于告诉React,这个组件这次不需要渲染任何DOM元素。

  需要注意,render函数应该是一个纯函数,完全根据this.state和this.props来决定返回的结果,而且不要产生任何副作用。在render函数中去调用this.setState毫无疑问是错误的,因为一个纯函数不应该引起状态的改变。我们在后面的章节会对render函数做详细的介绍。

  4.componentWillMount和componentDidMount

  在装载过程中,componentWillMount会在调用render函数之前被调用,component-DidMount会在调用render函数之后被调用,这两个函数就像是render函数的前哨和后卫,一前一后,把render函数夹住,正好分别做render前后必要的工作。

  不过,我们通常不用定义componentWillMount函数,顾名思义,componentWillMount发生在“将要装载”的时候,这个时候没有任何渲染出来的结果,即使调用this.setState修改状态也不会引发重新绘制,一切都迟了。换句话说,所有可以在这个component-WillMount中做的事情,都可以提前到constructor中间去做,可以认为这个函数存在的主要目的就是为了和componentDidMount对称。

  而componentWillMount的这个兄弟componentDidMount作用就大了。

  需要注意的是,render函数被调用完之后,componentDidMount函数并不是会被立刻调用,componentDidMount被调用的时候,render函数返回的东西已经引发了渲染,组件已经被“装载”到了DOM树上。

  我们还是以ControlPanel为例,在ControlPanel中有三个Counter组件,我们稍微修改Counter的代码,让装载过程中所有生命周期函数都用console.log输出函数名和caption的值,比如,componentWillMount函数的内容如下:

  componentWillMount() {

  console.log('enter componentWillMount ' + this.props.caption);

  }

  上面修改并没有添加任何功能,只是通过console.log输出一些内容,然后我们刷新网页,在浏览器的console里我们能够看见:

  enter constructor: First

  enter componentWillMount First

  enter render First

  enter constructor: Second

  enter componentWillMount Second

  enter render Second

  enter constructor: Third

  enter componentWillMount Third

  enter render Third

  enter componentDidMount First

  enter componentDidMount Second

  enter componentDidMount Third

  可以清楚地看到,虽然componentWillMount都是紧贴着自己组件的render函数之前被调用,componentDidMount可不是紧跟着render函数被调用,当所有三个组件的render函数都被调用之后,三个组件的componentDidMount才连在一起被调用。

  之所以会有上面的现象,是因为render函数本身并不往DOM树上渲染或者装载内容,它只是返回一个JSX表示的对象,然后由React库来根据返回对象决定如何渲染。而React库肯定是要把所有组件返回的结果综合起来,才能知道该如何产生对应的DOM修改。所以,只有React库调用三个Counter组件的render函数之后,才有可能完成装载,这时候才会依次调用各个组件的componentDidMount函数作为装载过程的收尾。

  componentWillMount和componentDidMount这对兄弟函数还有一个区别,就是component-WillMount可以在服务器端被调用,也可以在浏览器端被调用;而component-DidMount只能在浏览器端被调用,在服务器端使用React的时候不会被调用。

  到目前为止,我们构造的React应用例子都只在浏览器端使用React,所以看不出区别,但后面第12章关于“同构”应用的介绍时,我们会探讨在服务器端使用React的情况。

  至于为什么只有componentDidMount仅在浏览器端执行,这是一个实现上的决定,而不是设计时刻意为之。不过,如果非要有个解释的话,可以这么说,既然“装载”是一个创建组件并放到DOM树上的过程,那么,真正的“装载”是不可能在服务器端完成的,因为服务器端渲染并不会产生DOM树,通过React组件产生的只是一个纯粹的字符串而已。

  不管怎样,componentDidMount只在浏览器端执行,倒是给了我们开发者一个很好的位置去做只有浏览器端才做的逻辑,比如通过AJAX获取数据来填充组件的内容。

  在componentDidMount被调用的时候,组件已经被装载到DOM树上了,可以放心获取渲染出来的任何DOM。

  在实际开发过程中,可能会需要让React和其他UI库配合使用,比如,因为项目前期已经用jQuery开发了很多功能,需要继续使用这些基于jQuery的代码,有时候其他的UI库做某些功能比React更合适,比如d3.js已经支持了丰富的绘制图表的功能,在这些情况下,我们不得不考虑如何让React和其他UI库和平共处。

  以和jQuery配合为例,我们知道,React是用来取代jQuery的,但如果真的要让React和jQuery配合,就需要是利用componentDidMount函数,当componentDidMount被执行时,React组件对应的DOM已经存在,所有的事件处理函数也已经设置好,这时候就可以调用jQuery的代码,让jQuery代码在已经绘制的DOM基础上增强新的功能。

  在componentDidMount中调用jQuery代码只处理了装载过程,要和jQuery完全结合,又要考虑React的更新过程,就需要使用下面要讲的componentDidUpdate函数。 深入浅出React和Redux

目录
设置
手机
书架
书页
评论