从摇篮到坟墓 -> 组件从诞生到成长再到消亡,这一过程发生了一些事儿,而这些一件件的事儿都会触发一个个生命周期方法!
响应组件生命周期有三种类型:挂载(Mounting)、更新(Updating )和卸载(Unmounting)
div 的例子:https://codesandbox.io/s/54mkyvl3r4
React 的例子:https://codesandbox.io/s/yqrkxpm01
图示:https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/
面试必考,但平时用得不多。平时用到它的知识点非常少,但面试的时候问到它的知识点就特别得多!
官网讲生命周期这一章节的标题是「State and Lifecycle」
State 我们已经非常熟悉了,但是 Lifecycle 是什么玩意儿呢?(不知道)
要讲清楚生命周期,那一开始就不讲 React,不然,把生命周期配合 React 来讲的话,生命周期就特别的模糊了。
总之,不讲 React,直接讲生命周期,就不模糊了。

我们研究生命周期的对象就是div -> 看看它的生老病死是怎样的……
💡:create 和 mount的区别?

我们创造的div没有在页面里边,只是出现在 JS 内存中!
而我们把div放到app里边,这就叫做mount -> 把div mount到app里边!
把一个东西放到另一个东西里边,这个过程就叫做「挂载」!
💡:React 里边有 props,这个原生例子不讲props……
💡:先填充,再挂载?

💡:更新div?
div.querySelector('button').onclick = () => {
state += 1
// update div
div.querySelector('p').innerText = state
}
💡:React 生命周期最重要的三个点?
💡:关于div的死亡?
div.remove? -> 这是div的自毁程序,把自己从页面里边移除!
但是,你还得移除一些绑定在div身上的事件监听,还有div自己所占的内存!
div.querySelectorAll("button")[1].onclick = () => {
div.querySelector("button").onclick = null;
div.querySelectorAll("button")[1].onclick = null;
div.remove();
div = null; // destroy div
};
💡:人有生老病死,div有生挂更新死……

方方说函数是没有生命周期的!
class组件才有生老病死!
💡:第一轮,挂载阶段?

💡:给用户一个点页面的机会,好让用户可以更新组件?
能在
componentDidMount前点击页面吗?不能!
💡:自己做一个控制台,让控制台的内容在页面中显示?
let div = document.createElement('div')
document.body.appendChild(div)
console.log = function(content) {
div.innerHTML += `${content}<br>`
}
💡:为啥要在创建的时候初始化状态?
初始化得越早越好!而最早的时间就是出生的那天……孩子一出生,初始化了 ta 的爸爸妈妈……
总之,组件一出生就给它一个state

💡:目前的代码?
let div = document.createElement("div");
document.body.appendChild(div);
console.log = function (content) {
div.innerHTML += `${content}<br>`;
};
class App extends React.Component {
add1() {
console.log("用户点击了");
this.setState({
n: this.state.n + 1,
flag: true
});
}
constructor() {
super();
this.state = {
n: 0,
flag: false
};
console.log("创建 App");
}
componentWillMount() {
console.log("将要 mount App");
}
render() {
console.log(`${this.state.flag ? '更新' : '填充'} App 的内容`);
return (
<div className="app">
{this.state.n}
<button
onClick={() => {
this.add1();
}}
>+1</button>
</div>
);
}
componentDidMount() {
console.log("mount App 完毕");
}
}
ReactDOM.render(<App />, document.getElementById("root"));
效果:

render就是在update,第一次填充(其实也叫更新),在render,第二次更新,也是render。
💡:我能否在更新App的内容之前做点事情呢?
React 提供了一个叫componentWillUpdate的钩子,而且更新之后也提供了一个叫componentDidUpdate的钩子:

💡:第一次的,钩子都带有 mount,第二次的,钩子都带有更新
💡:如果组件要死了?
如何让组件凉凉?
话说,儿子被凉凉之前,能否说一句遗言呢?
可以,这就需要用到componentWillUnmount这个钩子了,表示儿子马上从页面中被弄出来!

💡:爸爸给儿子传新的数据?
如何知道爸爸传数据给儿子? -> 使用componentWillReceiveProps(nextProps)这个钩子!

接下来就讲一下这 8 个生命周期分别如何地使用……

17 版后,一些钩子被废弃了:
如何使用各个生命周期?
一般 React 做的就这两个:
如果非要强加的话,这两个也算:
💡:请求数据放哪儿做?
理论上这 8 个钩子都行 -> 为什么都行? -> 因为请求数据更新跟组件关系并不大!
请求数据的原则:越早越好!
请求数据有两个比较好的时机:
constructoronClick(有些请求必须等用户某个动作之后才去请求)当然,你还可以在componentWillMount、render里边发,不过render按规范是纯函数,所以render就算了!
几乎同时执行?

至于,其它刚刚讲到的剩余钩子,都可以发送请求,注意,这里强调的仅仅是发请求这件事……
总之,要么在constructor发,要么在事件处理函数onClick里边发! -> 越早发越好!
💡:state是什么时候产生的?
state跟组件不是一个东西,state它是组件的一个附属品
在render前都那些钩子都可以初始化state,但是官方推荐你在constructor里边初始化……
何以见得?

最明显的地方,事件处理函数 -> onClick
话说,我们能否在其它钩子里边更新呢?
如constructor
会报错:告诉你不能在mount之前更改状态
还有不能在render里边更改状态,不然会死循环,导致爆栈!
在componentDidMount里边setState -> 这个setState只会执行一次,因为组件只会mount一次。
componentWillUpdate -> 这会死循环,爆栈……因为组件之所以更新是因为state变了,所以你不能在这个钩子里边setState
同理,componentDidUpdate也是如此,也会死循环!
componentWillUnmount -> 咩有意义,就像是临死前赚一块钱花这样没啥意义……注意,你还有 1s 就凉凉了(什么也做不了),可不是还有一天啊!总之,你不能想着临死前打一局 LOL 这样…… -> 简单写一封遗书,或说一句话就够了!千万不要做任何更新自己的事儿!
componentWillReceiveProps -> 这个可以setState,它可以记录「爸爸说了几次话」…… -> 用得比较多
总之,有常用的 3 处地方可以放setState:
componentDidMountcomponentWillReceiveProps方方是如何得出这几点的? -> 很简单,一个个排查出来的!有逻辑错误的就不能放!
一般都放在元素标签上啊!除此之外就没地方了!
注意,也不要用原生的 JS 做事件监听!
shouldComponentUpdate(nextProps, nextState)
让这个钩子返回false值,这样不管你如何setState都不会更新组件!
一些测试例子:
偶数更新:

初始值是0,用户点击了,此时将要更新为1,那就不更新呗!(注意这是页面咩有任何变化,但是内存中的状态已经变成1了),用户再点击,此时将要更新为2,那就更新页面呗!
避免不必要的更新:
我们写了一个不出现在页面里边的x状态,我们更新了这个x,显然这就需要去render了,可是页面用到的状态并咩有发生改变啊!所以我们显然并不需要去更新……

这个钩子一般不用,一般都用在非常特殊的情况!
面试题:

如果问你例子,那就说上边的例子,即:一个不显示在 UI 上的状态变了,就得判断一下显示在 UI 上的状态是否变了,如果变了,那就返回true,render组件,否则就false,不更新!
在面试 React 前,就把 React 的相关面试题过一遍就好了!
componentDidMount(方方不是很认同),这是看了几个面试题归纳博客得出来的说法 -> 不能放在constructor里边是因为你不能在这儿setState,而不能放在componentWillMount是为了性能的需要,因为用了 fiber! -> 这是用了排除法,根据请求越早越好,把最早的逐步排除,那么最终无法排除的就是正确的答案了!setState之后,组件会调用哪几个生命周期函数? -> shouldComponentUpdate -> componentWillUpdate -> render -> componentDidUpdate看着面试题不断地测试就好了!背一下为什么这样做的原因就行了!
➹:关于 React 的几道面试题 - Kevin’s Notes
总结一下面试题:
shouldComponentUpdate 为什么那么重要?
componentDidMountconstructor里边不能使用setState(方方表示不成立,也许是因为组件要用到请求过来的数据吧,不过,这真得是强制性的吗?)componentWillMount在 fiber 里边会多次被调用,造成不稳定!(道听途说,也不知道为什么,即便深问起来,面试官也不知道为什么 fiber 会造成不稳定)render -> 保证是纯函数,所以最后就自然而然地落到了componentDidMount这个钩子里边了!setState的时候,会调用哪几个钩子?
shouldComponentUpdate -> componentWillUpdate -> render -> componentDidUpdate(其实就是两个,一个是看要不要更新,另一个就是更新,更新又有之前和之后)➹:React 生命周期 - WindrunnerMax - 博客园
➹:大话 react 生命周期 2019:react-v16.3 新生命周期总结 - SegmentFault 思否(★)
➹:React 生命周期详解 - SegmentFault 思否
➹:React Lifecycle Methods - A Deep Dive - Programming with Mosh
➹:附实例!图解 React 的生命周期及执行顺序_Mathilda91 的博客-CSDN 博客