引发组件重新渲染的条件:
1,state 状态变化,组件会重新渲染。
2,父组件属性变化,也就是父组件给子组件的 props 发生变化,也会引起重新渲染。
3,强制渲染也可以引起重新渲染。
在上一篇博客,我们了解了第一种情况,这一篇继续。
首先,我们先写一对父子组件:点击父组件的按钮,改变 a 的值,并给子组件传过去。
首先,在刚出来的时候,会有一次初始渲染。
这个是子组件 render 里面的 console。
然后当按钮被点击的时候,你可以看到,子组件的数字确实会变,变的同时右边的控制台也一直在输出“渲染了”。
这个就属于上面的第二种情况,props 发生改变的时候,也会重新渲染。
第三种,就是强制渲染。这个没啥要求,只要你需要,都可以让它强制更新渲染。
force 就是强制的意思,update 是更新的意思。
forceUpdate 它是属于,完全重新的把这个组件给渲染一遍。
说白了,就是不管你到底有没有更新的必要,我就是要你更新。
但是我们一般都不会去用,也不推荐。
因为 render 之所以不是时刻都在执行,目的就是为了节约资源。而在没什么特别的理由前提下,我们是没必要去瞎更新的,这样会打乱 React 自身的渲染计划,也会导致性能的下降,所以极少使用。
接下来我们就来说下组件的生命周期钩子函数。
钩子函数其实很类似于我们平常所说的事件,比如我给页面加个事件 window.onload = function(){ ... },就是当页面加载完成的时候,你要执行这个回调函数。
钩子函数也是如此,它要的是在发生某种事情的时候,你要去调我的这个函数,这就是钩子函数。
钩子函数是存在在每一个组件当中的,那么我们一起来看看它到底怎么用。
首先,它分成三个大的阶段:
1,创建阶段 Mount。原来没有这个组件,现在给它创建出来了。
2,更新阶段 Update。比如说状态发生变化,props 发生变化,或者做了强制渲染,都会导致更新的发生。
3,卸载,销毁的阶段 Unmount。
创建和卸载都是一瞬间的事,基本上来说,存在时间最久的还是更新。
实际开发中,生命周期钩子函数中 componentDidMount 和 componentDidUpdate 用的最多。
那么接下来,就让我们一起来看下这些钩子函数:
首先,我们先来一个简单的组件,看下最先开始的创建。
创建阶段 Mount:
1,先执行 constructor。
不管你是不是一个组件,不管要进行什么工作,首先你是一个类,那么就得遵循类最基本的要求,所以它会最先得到执行。因为如果没有它的话,这个类根本就创建不出来。所以这时还不算是个组件,只是 class 自身的初始化。
2,getDerivedStateFromProps:检查需要更新的状态。
简单来说,它并不是一个生命周期钩子函数,而是一个检查函数,它的作用就是检查更新。它需要检查一下你的状态变没变,然后你哪些状态变了,哪些没变。所以一般我们是不去碰它的,知道有这么个操作就行。
3,执行第一次的 render:初始渲染。
4,触发钩子函数 componentDidMount:组件创建完成,并且已经成功的挂载到 DOM 结构中。(对组件做一些初始化的操作,获取数据)
然后我们在来看下更新的操作:
点击按钮,改变状态 a 的值,自加一。
然后我们添加 shouldComponentUpdate 之后再来看看:
shouldComponentUpdate 有两个参数 nextProps,nextState。就是接下来如果重新渲染了,会变成什么样的属性和状态。并且 shouldComponentUpdate 的返回值决定是否更新。
我们设置下次状态里面 a 的值,如果 %3 为 0 的时候,就更新。说白了就是每三次更新一次。
可以看到,前 2 次点击按钮的时候,a 的值都是 0,第 3 次点击的时候,a 的值才变成 3。
更新阶段 Update:
1,getDerivedStateFromProps:检查需要更新的状态。
首先在更新的时候,如果它是无脑更新所有的东西,那么 React 的性能就会非常的差,所以它的第一步也是 getDerivedStateFromProps。它的作用是检查 state,props 有没有更新,以及哪些更新了,哪些没更新,它需要去检查这些东西。
2,shouldComponentUpdate:即将开始更新组件,可以阻止更新发生。
这个组件应不应该更新,到目前为止还是个问号,它在这里需要去确定这个组件是否应该更新。在某些特殊的情况下,我们可以去阻止它的更新。就比如你打游戏拿 5 杀的时候,突然一个弹框告诉你 windows 要升级了,它是用来询问你的,可升可不升。
所以 shouldComponentUpdate 可以帮助我们去拒绝一些没有意义的更新。一般来说,shouldComponentUpdate 在小的组件里面用不上,但是对于一些大的组件,尤其是有数据交互东西,就非常的有用,它可以根据条件来更新,防止一些死循环。
3,render:如果应该更新,才更新。
这里需要注意的是,render 更新的仅仅是虚拟 DOM,真实的 DOM 还没更新。所以它需要在 getSnapshotBeforeUpdate 中抓住机会,它需要去把 DOM 元素中的状态给做一个保留。
4,getSnapshotBeforeUpdate:从 DOM 中获取状态,已保证更新前后状态一致。
getSnapshotBeforeUpdate 翻译过来就是,在更新之前它要去获取一个快照。它其实是在正式的更新之前,它需要去保存一些 DOM 的状态,比如现在这个滚动条滚到哪个位置了,用户输入了哪些东西,其他东西都是怎么设置的,在哪个位置等等,让这些东西都得到一个保留。保留完了之后,等将来这个 DOM 元素更新了之后,它还要在恢复这个现场。这就是为什么在 React 里面,它能够自动的去维护滚动状态的原因。
5,更新真正的 DOM 元素。
6,componentDidUpdate:更新完之后,会触发钩子函数。这时组件已经渲染完成。(尽量避免在 componentDidUpdate 中更新 state,不然容易造成死循环)
最后我们再来看看销毁:点击按钮,显示隐藏子组件。
销毁阶段 Unmount:
1,componentWillUnmount:组件销毁,销毁操作无法阻止。这个基本也用不上。
componentWillUnmount 不保证一定发生。比如用户直接把浏览器关了,更极端点,用户重启,关机,或者干脆主机炸了,这时候根本就来不及发生 componentWillUnmount。所以为了数据的安全着想,不要把任何的数据操作往它里面放。