HuangHongRui / Notebook

:pencil2: Yeah.. This's My NoteBook...:closed_book:
0 stars 0 forks source link

React[5] (组件生命周期) #40

Open HuangHongRui opened 7 years ago

HuangHongRui commented 7 years ago

为了理解React 的工作过程, 必须要了解 React 组件的生命周期。。。

每个组件在网页中也会被创建/更新和删除: React 严格定义了组件的生命周期,可能经历如下三个过程:

三种不同的过程,React库会依次调用组件的一些成员函数, 这些函数称为 生命周期函数

[装载过程] 当组件第一次被渲染时,依次调用的函数如下:

HuangHongRui commented 7 years ago
  1. constructor

注意,并不是每个组件都需要定义自己的构造函数。 无状态的React组件 是不需要定义构造函数的。

一个组件需要构造函数,为了如下目的:

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

HuangHongRui commented 7 years ago
  1. getInitialState 和 getDefaultProps

getInitialState 此函数的返回值会用来初始化组件的 this.state.(只出现在装载过程,整个生命周期过程中,这个函数只被调用一次) getDefaultProps 函数的返回值可作为 props 的初始值. 【两个方法在Es6的方法定义的React组件根本不会用到,而且都只有在用React.creatClass(废弃)方法创造组件类才会发生作用。】

HuangHongRui commented 7 years ago
  1. render

render 函数是组件中最重要的函数!!!

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

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

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

render函数应该是一个纯函数,完全根据 this.state 和 this.props 来决定返回的结果, 而且不要产生任何副作用副作用。 在render函数中去调用this.setState 毫无疑问是错误的 因为一个纯函数不应该引起状态的改变。

HuangHongRui commented 7 years ago

[ componentWillMount 和 componentDidMount ]

在装载过程中, componentWillMount 会在调用rnder函数之前被调用, componentDidMount会在调用render函数之后被调用, 这两个函数把render函数夹住,分别做render前后必要的工作。

通常不定义componentWillMount函数, 因为将要装载的时候,这时没有任何渲染出来的结果,即使调用this.setState修改状态也不会引发重新绘制,一切都迟了 换句话说,所有在componentWillMount中做的事情,都可提到constructor中去做。(可认为它的出现存在只是为了和componentDidCount对称

HuangHongRui commented 7 years ago

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

Counter 中的代码为例,其console.log出的一些内容:

image

可看到,componentWillMount都紧贴着组件的render之前被调用, 而componentDidMount是当所有组件render函数调用之后, 所有(三个)组件的componentDidMount再一起被调用。

上面的现象,是因 rende 函数并不往 DOM 树上渲染或者装载内容它只返回一个 JSX 表示的对象!!! 再由 React 库来根据返回的对象决定如何渲染!

【而 React 库肯定要把所有组件返回的结果综合起来,才能知道如何产生对应的DOM修改。所有只有React库调用三个Counter组件的函数之后,才有可能完成装载,这时猜依次调用各个组件的componentDidMount函数作为装载函数作为收尾。。

componentDidMount只能在浏览器被调用 / 而componentWillMount 还可在服务器端被调用。。

既然装载是一个创建组件并放到DOM树上的过程,真正的装载是不可能在服务器端完成的,因为服务器端渲染不会产生DOM树,通过React组件产生的只是一个纯粹的字符串。

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

实际开发过程,可能会让React和其他UI库配合使用,例如jQuery等, 有时某些UI库在做某些功能比React更合适。 如果真要让React和jQuery配合,需要利用componentDidMount函数, 当该函数被执行时,组件对应的DOM已存在,所有的事件处理函数也已设置好, 此时可调用jQuery代码,让jQuery代码在已绘制的DOM基础上增强新功能。

componentDidMount中调用jQuery代码只处理了装载过程,要和jQuery完全结合, 需要再考虑React的更新过程——componentDidUpdate函数

HuangHongRui commented 7 years ago

[ 更新过程 ]

当 props 或 state 被修改时,会引起组件的更新过程。

更新过程一次调用下面的生面周期函数:

componentWillReceiveProps
shouldComponentUpdate
componentWillUpdate
render
componentDidUpdate

不是所有的更新过程都会执行全部函数。

HuangHongRui commented 7 years ago
  1. componentWillReceiveProps( nextProps ) 只要是父组件的render函数被调用,在render函数里面被渲染的子组件就会经历更新过程! 不管父组件传给子组件的props有无改变,都会触发子组件的componentWillReceiveProps函数。

通过this.setState 方法触发的更新不会调用 componentWillReceiveProps这个函数, 因为该函数适合根据新的props值(参数nextProps)来计算出是不是要更新内部状态state。

更新内部状态的方法是this.setState,如果调用导致componentWillReceiveProps再次被调用,那就是死循环了。

每个React组件都可通过 forceUpdate 函数强行引发一次重新绘制。

componentWillReceiveProps 函数有必要吧传入的参数 nextProps 和 this.props 作必要对比。 nextProps 代表这一次渲染传入的Props值 this.Props代表上次渲染时的Props值 只有两者有变化的时候才有必要调用this.setState更新内部内容.

**React的组件组合中, 可以只渲染一个子组件,其他组件不需要渲染,这是提高React性能的重要方式。

HuangHongRui commented 7 years ago
  1. shouldComponentUpdate(nextProps, nextState)

除了 render 函数,shouldComponentUpdate 是 React 组件生命周期中最重要的一个函数了。

render 重要是因为 render决定了该渲染生命, 而shouldComponentUpdate重要是因它决定一个组件什么时候不需要渲染.

rendershouldComponentUpdate是React生命周期中2个唯一要求返回结果的函数。 render返回结果将用于构造DOM对象 shouldComponentUpdate`返回一个布尔值,告诉React库这个组件在此次更新过程中是否继续。

更新过程中,React 首先调用 shouldComponentUpdate 如果返回true 那么继续更新然后调用render,如果返回false,那就停止更新过程,不会引发后续渲染。

重要性: 因为只要使用恰当,能够大大提高React的性能。

shouldComponentUpdate根据propsstate值两参数,加上this.propsthis.state来判断并返回结果。

如果给组件添加该函数,会默认实现简单返回true,就是每次更新过程都会重新渲染,这是求稳方式。 若要追求更高新能,那需要定制这个函数。

  shouldComponentUpdate(nextProps, nextState) {
    return (nextProps.caption !== this.props.caption) ||
      (nextState.count !== this.state.count)
  }

只有当 caption 或者 state 中的 count 值改变, 才会返回true

执行shouldComponentUpdate时,this.state依然是this.setState函数执行之前的值,实际上要做的就是在 nextProps nextState this.propsthis.state 中互相对比

在实际开发中遇到潘达复杂的组件,这种情况下避免没必要的渲染会大大提高性能。

HuangHongRui commented 7 years ago
  1. componentWillUpdatecomponentDidUpdate

和装载过程不同,当服务器使用React渲染时这一对函数中的Did函数,并不只是在浏览器端才执行,无论更新过程发生在服务器端还是浏览器端,该函数都会被调用。。。

前面的 componentDidMount 函数执行其他UI库的代码,当React组件被更新时,原有内容被重新绘制,此时就需要在componentDidUpdate函数再次调用UI库的代码。。

*使用服务器端渲染时,基本不会经历更新过程,因服务器端只需要产出HTML字符串,一个装载过程就足够产出HTML了,正常情况下服务器不会调用componentDidUpdate

HuangHongRui commented 7 years ago

[ 卸载过程 ]

React组件的卸载过程涉及函数——componentWillUnmount

当React 组件要从 DOM树上 删除掉前触发。适合做些请理性工作。

componentWillUnmount 的工作 跟 componentDidMount 有关, 例如componentDidMount使用非React方法创造一些DOM元素,如果不管可能会造成内存泄露, 这时就需要componentWillUnmount来把这些Dom清理掉...

起到一个收尾的作用

HuangHongRui commented 7 years ago

[ React 组件 state 和 prop 的局限 ]

数据管理问题

如果父组件 和 子组件 数据发生了重复,带来一个问题就是如何保证充足的数据一致。

如何处理这种问题呢?

把数据源放在React组件之外形成全局状态。。

Flux 和 Redux 中的 store