react类组件生命周期函数

261 阅读3分钟
  • 类组件生命周期函数包括:
    • 初始化渲染阶段:
      • constructor
      • static getDerivedStateFromProps
      • render
      • componentDidMount
    • 重新渲染阶段:
      • static getDerivedStateFromProps
      • shouldComponentUpdate
      • render
      • getSnapshotBeforeUpdate
      • componentDidUpdate
    • 卸载阶段:
      • componentWillUnmount

constructor

  • 构造函数是在render阶段被调用,beginWork中判断,如果是类组件则调用updateClassComponent
  function updateClassComponent(
    current: Fiber | null,
    workInProgress: Fiber,
    Component: any,
    nextProps: any
  ) {
    const instance = workInProgress.stateNode;
    if (instance === null) {
      // 如果instance空,说明没有类组件实例,则实例化
      // Component是定义的类组件
      constructorClassInstance(workInProgress, Component, nextProps);
    }
  }
  
  function constructorClassInstance(workInProgress, ctor, props) {
    // 实例化类组件,并关联上fiber
    let instance = new ctor(props);
    workInProgress.stateNode = instance;
  }

static getDerivedStateFromProps

初次渲染和更新渲染,都会被调用,用于根据新的props生成新的state

  function mountClassInstance(
    workInProgress: Fiber,
    ctor: any,
    newProps: any
  ) {
    const getDerivedStateFromProps = ctor.getDerivedStateFromProps;
    if (typeof getDerivedStateFromProps === 'function') {
      const prevState = workInProgress.memoizedState;
      let partialState = getDerivedStateFromProps(nextProps, prevState);
    }
  }

mountClassInstanceconstructorClassInstance之后调用。

  function updateClassComponent(
    current: Fiber | null,
    workInProgress: Fiber,
    Component: any,
    nextProps: any
  ) {
    const instance = workInProgress.stateNode;
    if (instance === null) {
      // 初次渲染逻辑
    } else {
      // 更新渲染的逻辑,同样会调用getDerivedStateFromProps
      updateClassInstance(current, workInProgress, Component, nextProps);
    }
  }

不管是初次渲染,还是更新渲染,getDerivedStateFromProps都是在render阶段执行的。

componentDidMount

  function commitLayoutEffectOnFiber(
    current: Fiber | null,
    finishedWork: Fiber
  ) {
    switch(finishedWork.tag) {
      case ClassComponent: {
        const instance = finishedWork.stateNode;
        // 如果有update标记,就执行componentDidMount函数
        if (finishedWork.flags | update) {
          if (current === null) {
            instance.componentDidMount();
          }
        }
      }
    }
  }

commitLayoutEffectOnFiber是在commit阶段执行的,commit阶段分3步:

  function commitRootImpl() {
    // 1
    commitBeforeMutationEffects();
    // 2
    commitMutationEffects();
    // 3 commitLayoutEffectOnFiber就是在第3步中执行的
    commitLayouteEffects();
  }

那么update标识是什么时候设置的呢?

  function mountClassInstance(
    workInProgress: Fiber,
    ctor: any
  ) {
    const instance = workInProgress.stateNode;
    if (typeof instance.componentDidMount === 'function') {
      let fiberFlag = Update;
      workInProgress.flag |= fiberFlag;
    }
  }

所以componentDidMount是在commit阶段执行的,且在重新渲染之后才执行。同时需要render阶段一起配合才行。

shouldComponentUpdate

  function checkShouldComponentUpdate(
    workInProgress,
    ctor,
    oldProps,
    newProps,
    oldState,
    newState
  ) {
    const instance = workInProgress.stateNode;
    if (typeof instace.shouldComponentUpdate === 'function') {
      // 如果自定义shouldComponentUpdate,则返回结果
      let shouldUpdate = instance.shouldComponentUpdate(newProps, newState);
      return shouldUpdate;
    }
    // 默认返回true
    return true;
  }
  function updateClassInstance() {
    // 得到新的state
    if (typeof getDerivedStateFromProps === 'function') {
      // 如果有的话,执行getDerivedStateFromProps
      applyDerivedStateFromProps(workInProgress, ctor, newProps);
      newState = workInProgress.memoizedState;
    }
    // 再判断是否更新
    const shouldUpdate = checkShouldComponentUpdate(
      workInProgress,
      ctor,
      oldProps,
      newProps,
      oldState,
      newState
    )
  }

可见getDerivedStateFromPropscomponentShouldUpdate都是在render阶段执行的,且有先后顺序:先计算新状态,后判断是否需要更新。

getSnapshotBeforeUpdate

  function commitBeforeMutationEffectsOnFiber(finishedWork: Fiber) {
    const flags = finishedWork.flags;
    if ((flags & Snapshot) !== NoFlags) {
      switch(finishedWork.tag) {
        case ClassComponent: {
          const current = finishedWork.alternate;
          // current不为空说明才是更新,否则就是第一次渲染
        	if (current !== null) {
            const prevProps = current.memoizedProps;
            const prevState = current.memoizedState;
            const instance = finishedWork.stateNode;
            const snapshot = instance.getSnapshotBeforeUpdate(prevProps, prevState);
            // 得到的返回值会传递给componentDidUpdate
            instance.__reactInternalSnapshotBeforeUpdate = snapshot;
          }
        }
      }
    }
  }

commitBeforeMutationEffectsOnFiber会在commit阶段的commitBeforeMutation中执行。

  function updateClassInstance(
    workInProgress: Fiber
  ) {
    const instance = workInProgress.stateNode;
    if (typeof instance.getSnapshotBeforeUpdate === 'function') {
      // 如果有getSnapshotBeforeUpdate,那么设置标记
      workInProgress.flags |= Snapshot;
    }
  }

componentDidUpdate

componentDidUpdatecomponentDidMount被调用的地方是一样的。区别在于是否有current

  function commitLayoutEffectOnFiber(
    current: Fiber | null,
    finishedWork: Fiber
  ) {
    switch(finishedWork.tag) {
      case ClassComponent: {
        const instance = finishedWork.stateNode;
        // 如果有update标记,就执行componentDidMount函数
        if (finishedWork.flags | update) {
          if (current === null) {
            // 初次渲染
            instance.componentDidMount();
          } else {
            // 更新渲染
            instance.componentDidUpdate();
          }
        }
      }
    }
  }

componentDidUpdate同样需要在render阶段置Update标记,位置在updateClassInstance中。

componentWillUnmount

  function recursivelyTraverseDeletionEffects(
    finishedRoot: FiberRoot,
    nearestMountedAncestor: Fiber,
    parent: Fiber
  ) {
    let child = parent.child;
    while(child !== null) {
      // 处理当前子节点的componentWillUnmount函数
      commitDeletionEffectsOnFiber(finishedRoot, nearestMountedAncestor, child);
      // 处理当前子节点的兄弟节点
      child = child.parent;
    }
  }
  function commitDeletionEffectsOnFiber(
    finishedRoot,
    nearestMountedAncestor,
    deletedFiber
  ) {
    switch(deletedFiber.tag) {
      case ClassComponent: {
        const instance = deletedFiber.stateNode;
        // 如果定义了componentWillUnmount,则执行
        if (typeof instance.componentWillUnmount === 'function') {
          instance.componentWillUnmount();
        }
      }
      // 处理当前子节点的所有子节点
      recursivelyTraverseDeletionEffects(
        finishedRoot,
        nearestMountedAncestor,
        deletedFiber
      );
    }
  }

componentWillUnmount是从父组件到组件的调用顺序 componentWillUnmount处于commit阶段的commitMutationEffects中执行且不用在render阶段设置标记(componentDidMount``componentDidUpdate``getSnapshotBeforeUpdate是需要的)