实现一个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,所以可以在该方法中加载 componentWillMountcomponentWillReceiveProps 钩子。
  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),然后我们设置一个标识符来标识组件在什么阶段,以此来实现不同的生命周期方法。
附上本文代码

posted @ 2019-06-01 19:47  yangrenmu  阅读(468)  评论(0编辑  收藏  举报