简单手写实现React的基本生命周期
继续学习写源码系列,本系列是学习系列。
- 简单手写实现 React 的 createElement 和 render,里面有准备工作,不再赘述。
- 简单手写实现 react 的函数组件
- 简单手写实现 react 的类组件
- 简单手写实现 React 类组件的 state 更新
- 简单手写实现 React 类组件的 state 批量更新
- 简单手写实现元素的 Ref
- 简单手写实现类组件的 Ref
- 简单手写实现函数组件的 Ref - forwardRef
本文的目标是,简单手写实现 React 基本生命周期。
TL;DR
- 类组件才有生命周期钩子,函数组件是副作用函数
- 挂载的,围绕render就好,挂载阶段的生命周期:componentWillMount 和 componentDidMount
- 更新的,围绕类里更新states和更新dom就好,更新阶段的生命周期:shouldComponentUpdate 和 componentWillUpdate
准备工作
先将index.js
修改
// import React from './source/react'; // import ReactDOM from './source/react-dom'; import React from 'react'; import ReactDOM from 'react-dom'; class Counter extends React.Component { static defaultProps = { name: '颜酱', }; constructor(props) { super(props); this.state = { number: 1 }; console.log('Counter 1.constructor'); } componentWillMount() { // 取本地的数据 同步的方式:采用渲染之前获取数据,只渲染一次 console.log('Counter 2.componentWillMount'); } componentDidMount() { console.log('Counter 4.componentDidMount'); } handleClick = () => { this.setState({ number: this.state.number + 1 }); }; shouldComponentUpdate(nextProps, nextState) { // 代表的是下一次的属性 和 下一次的状态。此函数种返回了false 就不会调用render方法了 console.log('Counter 5.shouldComponentUpdate'); return true; } componentWillUpdate() { // 这里不要用setState 可能会死循环 console.log('Counter 6.componentWillUpdate'); } componentDidUpdate() { console.log('Counter 7.componentDidUpdate'); } render() { console.log('Counter 3.render'); return ( <div> <p>{this.state.number}</p> <button onClick={this.handleClick}>+</button> </div> ); } } ReactDOM.render(<Counter />, document.getElementById('root'));
打开控制台,再点击加号
网络异常,图片无法展示
|
科普生命周期
以前吧,看生命周期和钩子,觉得高大上,不知然。
明白了,其实就相当于你写一个函数const fn = ()=>{console.log('讲话')}
,然后呢,拓展需求,需要再讲话前做点事,讲话后做点事,于是改装下函数
const fn = (beforeSay, afterSay) => { beforeSay && beforeSay(); console.log('讲话'); afterSay && afterSay(); };
这里beforeSay和afterSay相当于钩子函数了。
因此,生命周期和钩子,其实就是代码执行某个阶段,需要额外执行的函数而已。
再说说 React 的生命周期函数,React16.3 前,生命周期大约分为四阶段
网络异常,图片无法展示
|
- 初始化阶段:就是赋值 props 和 states
- 挂载阶段:将要挂载(componentWillMount)、挂载(render)、挂载完成(componentDidMount)
- 卸载阶段:将要卸载(componentWillUnmount)
- 更新阶段:分为两类
- states 更新的话,是否更新 DOM 前(shouldComponentUpdate)、将要更新(componentWillUpdate)、挂载更新(render)、挂载完成(componentDidMount)
- props 更新的话,接受 props(componentWillReceiveProps)、之后同 states 更新的阶段
生命周期是在类组件中,函数组件的话要用副作用函数(后期在解析),所以本期只是类组件的生命周期。
分析生命周期
不同的钩子,找到代码的相应阶段,让其执行就好。
- 初始化阶段,不用管,用户自己写到 constructor 里,自然就最先执行
- 挂载阶段:主要就是挂载前后,执行 componentWillMount 和 componentDidMount
- 更新阶段:这里先讨论 states 的更新,下一篇说 props,state 围绕更新前后,挂载前后
实现
1.实现挂载阶段的生命周期:componentWillMount 和 componentDidMount
找到解析类组件的部分,将函数执行加到合适的地方
// react-dom.js const handleClassType = (vdom) => { // ... // 生命周期componentWillMount instance.componentWillMount && instance.componentWillMount(); // 生命周期render,这个原本就在 const vdomReturn = instance.render(props); const dom = createElementDOM(vdomReturn); // 生命周期componentDidMount instance.componentDidMount && instance.componentDidMount(); return dom; };
实现更新阶段的生命周期:shouldComponentUpdate 和 componentWillUpdate
shouldComponentUpdate 这个函数比较特殊,如果返回值是 true,表示更新 DOM,否则就不更新 DOM。 componentWillUpdate 就是在更新 DOM 前执行。
这个逻辑在类内部实现。
// react.js updateComponent() { // 新状态的获取这边用一个方法计算出来,逻辑清晰 this.componentInstance.state = this.getState(); // 默认会更新 let isToUpdate = true // 生命周期 shouldComponentUpdate if(this.shouldComponentUpdate){ isToUpdate = this.shouldComponentUpdate(); } // 不是将要更新的话,到这里完结了,是的话就继续下面 if(!isToUpdate){ return } // 生命周期 componentWillUpdate this.componentWillUpdate() this.componentInstance.updateDom(); }
老规矩,index.js
打开自己文件的路径
正常运行,✌️~~~