实现一个react系列三:生命周期
前言
本文主要参考了从零开始实现一个React和从 0 到 1 实现React
上一篇实现一个react系列二:渲染组件中说了组件是如何渲染的,本本来介绍下react
中的生命周期。
注意,本文主要理解是去react
中的生命周期,并没有去实现react
中最新的生命周期。
生命周期
react
中生命周期是组件在不同时期运行的函数。如下图所示:(图片来源)
主要三个阶段:挂载阶段、更新阶段和卸载阶段。
实现
先将函数定义的组件和类定义的组件都转为类定义的组件,方便统一处理。
if (typeof vdom.tag === 'function') {
// 将组件都转为类定义的组件,
const component = createComponent(vdom, vdom.attrs)
// 更新组件 props
setComponentProps(component, vdom.attrs)
return component.base
}
- createComponent
const createComponent = (vdom, props) => {
let component
// 类定义的组件,直接返回实例
if (vdom.tag.prototype && vdom.tag.prototype.render) {
component = new vdom.tag(props)
} else {
// 函数定义的组件,添加 render 方法,为了获取函数中 jsx 转化的虚拟 dom
component = new vdom.tag(props)
component.render = function () {
return vdom.tag(props)
}
}
return component
}
- setComponentProps:更新 props
该方法用来加载及更新props
,此时,组件还没有render
,所以可以在该方法中加载componentWillMount
、componentWillReceiveProps
钩子。
const setComponentProps = (component, props) => {
// 第一次渲染时,挂载 componentWillMount 钩子
if (!component.base && component.componentWillMount) {
component.componentWillMount()
} else if (component.base && component.componentWillReceiveProps) {
// 更新阶段,props 改变时,挂载 componentWillReceiveProps 钩子
component.componentWillReceiveProps(props)
}
component.props = props
// 渲染组件
renderComponent(component)
}
- renderComponent:渲染组件
渲染组件,setState
时直接调用该方法。
const renderComponent = (component) => {
// 更新阶段,挂载 shouldComponentUpdate 钩子
if (component.base && component.shouldComponentUpdate) {
const bool = component.shouldComponentUpdate(component.props, component.state)
if (!bool && bool !== undefined) {
return false
}
}
// 更新阶段,挂载 componentWillUpdate 钩子
if (component.base && component.componentWillUpdate) {
component.componentWillUpdate()
}
// 获得虚拟dom
const rendered = component.render()
// 设置是否是更新阶段的标识符,同时也是一个真实的dom节点
const base = _render(rendered)
// 更新阶段,挂载 componentDidUpdate 钩子
if (component.base && component.componentDidUpdate) {
component.componentDidUpdate()
} else if (component && component.componentDidMount) {
// 挂载阶段,挂载 componentDidMount 钩子
component.componentDidMount()
}
if (component.base && component.base.parentNode) {
// setState 时,新的 dom 替换掉之前的 dom
component.base.parentNode.replaceChild(base, component.base)
}
component.base = base
}
下面我们来实验下效果如何。
import React from "./react"
import ReactDom from "./reactDom"
class World extends React.Component {
render() {
return <div>{this.props.count}</div>
}
}
class Hello extends React.Component {
constructor(props) {
super(props)
this.state = {
count: 0
}
}
componentWillMount() {
console.log("componentWillMount")
}
componentDidMount() {
console.log("componentDidMount")
}
shouldComponentUpdate(nextProps, nextState) {
console.log("shouldComponentUpdate", nextProps, nextState)
}
componentWillUpdate() {
console.log("componentWillUpdate")
}
componentDidUpdate() {
console.log("componentDidUpdate")
}
addCount() {
const { count } = this.state
this.setState({
count: count + 1
})
}
render() {
console.log("render")
return (
<div ha="lou">
<World count={this.state.count} />
<button onClick={this.addCount.bind(this)}> + </button>
</div>
)
}
}
ReactDom.render(<Hello />, document.getElementById("root"))
结果如下,大致样子是有了。
小结
react
的生命周期分为三个阶段,挂载阶段、更新阶段和卸载阶段。在实现时,我们先将函数定义的组件和类定义的组件都转成类定义的组件,(主要是将函数定义的组件,添加一个render
方法,获取到虚拟dom
),然后我们设置一个标识符来标识组件在什么阶段,以此来实现不同的生命周期方法。
附上本文代码