Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

实现一个react系列三:生命周期 #11

Open
yangrenmu opened this issue Jun 1, 2019 · 0 comments
Open

实现一个react系列三:生命周期 #11

yangrenmu opened this issue Jun 1, 2019 · 0 comments
Labels

Comments

@yangrenmu
Copy link
Owner

yangrenmu commented Jun 1, 2019

前言

本文主要参考了从零开始实现一个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),然后我们设置一个标识符来标识组件在什么阶段,以此来实现不同的生命周期方法。
附上本文代码

@yangrenmu yangrenmu added the react label Jun 1, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant