前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >更新时 Fiber 节点能否复用?

更新时 Fiber 节点能否复用?

作者头像
玖柒的小窝
发布2021-12-07 12:44:41
发布2021-12-07 12:44:41
52900
代码可运行
举报
文章被收录于专栏:各类技术文章~各类技术文章~
运行总次数:0
代码可运行

当产生更新时,workInProgressTreeFiber 节点有两种方式生成:

  1. re-render
  2. 复用 currentTreeFiber 节点

本文进行了以下探究:

更新时,能否复用 currentTreeFiber 节点这种情况。

beginWork

当调度更新时,会进入到 render 阶段,也就是产生 Fiber 的阶段,此时会调用到 beginWork 方法,该方法中对类组件和函数组件的处理如下:

代码语言:javascript
代码运行次数:0
运行
复制
function beginWork(
  current: Fiber | null,
  workInProgress: Fiber,
  renderLanes: Lanes,
){
    switch (workInProgress.tag) {
      case FunctionComponent: {
        const Component = workInProgress.type;
        const unresolvedProps = workInProgress.pendingProps;
        const resolvedProps =
          workInProgress.elementType === Component
            ? unresolvedProps
            : resolveDefaultProps(Component, unresolvedProps);
        return updateFunctionComponent(
          current,
          workInProgress,
          Component,
          resolvedProps,
          renderLanes,
        );
      }
      case ClassComponent: {
        const Component = workInProgress.type;
        const unresolvedProps = workInProgress.pendingProps;
        const resolvedProps =
          workInProgress.elementType === Component
            ? unresolvedProps
            : resolveDefaultProps(Component, unresolvedProps);
        return updateClassComponent(
          current,
          workInProgress,
          Component,
          resolvedProps,
          renderLanes,
        );
      }
    }
  }
复制代码

先看 ClassComponent 的产生 Fiber 的处理再看 FunctionComponent 吧。

ClassComponent

代码语言:javascript
代码运行次数:0
运行
复制
function updateClassComponent(
  current: Fiber | null,
  workInProgress: Fiber,
  Component: any,
  nextProps: any,
  renderLanes: Lanes,
) {
    shouldUpdate = updateClassInstance(
      current,
      workInProgress,
      Component,
      nextProps,
      renderLanes,
    );
    
    const nextUnitOfWork = finishClassComponent(
      current,
      workInProgress,
      Component,
      shouldUpdate,
      hasContext,
      renderLanes,
    );
    return nextUnitOfWork;
  }
复制代码

finishClassComponent 方法执行后会返回 Fiber 节点,而是否可复用 currentFiber,取决于 updateClassInstance 方法的执行结果。

来看下 updateClassInstance

代码语言:javascript
代码运行次数:0
运行
复制
function updateClassInstance(
  current: Fiber,
  workInProgress: Fiber,
  ctor: any,
  newProps: any,
  renderLanes: Lanes,
) {
      const shouldUpdate =
    checkHasForceUpdateAfterProcessing() ||
    checkShouldComponentUpdate(
      workInProgress,
      ctor,
      oldProps,
      newProps,
      oldState,
      newState,
      nextContext,
    );
    return shouldUpdate;
  }
复制代码

根据 updateClassInstance 方法里,判断是否需要更新的的 2 个条件:

  1. 是否有 forceUpdate
  2. shouldComponentUpdatecheckShouldComponentUpdate

可知,ClassComponentFiber 是否可复用,取决于 2 个条件:本次更新是否是调用 forceUpdate 产生的更新和 shouldComponentUpdate 方法中根据 props 判断当次是否需要更新

代码语言:javascript
代码运行次数:0
运行
复制
function checkShouldComponentUpdate(
  workInProgress,
  ctor,
  oldProps,
  newProps,
  oldState,
  newState,
  nextContext,
) {
  const instance = workInProgress.stateNode;
  if (typeof instance.shouldComponentUpdate === 'function') {
    const shouldUpdate = instance.shouldComponentUpdate(
      newProps,
      newState,
      nextContext,
    );

    return shouldUpdate;
  }

  if (ctor.prototype && ctor.prototype.isPureReactComponent) {
    return (
      !shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState)
    );
  }

  return true;
}
复制代码

根据 checkShouldComponentUpdate 的返回可知,当没有设置 shouldComponentUpdate 方法时,其效果等同于设置了 shouldComponentUpdate 但恒返回 true ,也就是 ClassComponent 会受各种因素而无故 re-render

下面来看 Functioncomponent 的产生 Fiber 节点的处理吧。

FunctionComponent

代码语言:javascript
代码运行次数:0
运行
复制
function updateFunctionComponent(
  current,
  workInProgress,
  Component,
  nextProps: any,
  renderLanes,
) {
    if (current !== null && !didReceiveUpdate) {
      bailoutHooks(current, workInProgress, renderLanes);
      return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
    }
}
复制代码

FunctionComponent 是否可复用 Fiber ,取决于 didReceiveUpdate 这个变量,这个变量在 beginWork 方法中被赋值:

代码语言:javascript
代码运行次数:0
运行
复制
function beginWork(
  current: Fiber | null,
  workInProgress: Fiber,
  renderLanes: Lanes,
){
    if (
      oldProps !== newProps ||
      hasLegacyContextChanged() ||
      (__DEV__ ? workInProgress.type !== current.type : false)
    ) {
      didReceiveUpdate = true;
    } else if (!includesSomeLane(renderLanes, updateLanes)) {
      // 当优先度不足够时进入此判断
      didReceiveUpdate = false;
      switch(workInProgress.tag) {
            // ...
        } 
        // 有可能会执行到这,如果上面的 switch 没有 return 语句的话
        return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
    } else {
      if ((current.flags & ForceUpdateForLegacySuspense) !== NoFlags) {
        didReceiveUpdate = true;
      } else {
        didReceiveUpdate = false;
      }
    }
}
复制代码

由以上可知,当 FunctionComponentpropscontext(当处于 dev 时,需要判断元素类型 type )未变化,且本次更新的优先级足够时,didReceiveUpdate 变量会设置为 false,在接下来的 updateFunctionComponent 方法的执行后会返回可复用的 Fiber 节点。

总结

更新时,workInProgressTree 能否复用 currentTreeFiber 节点取决于:

  1. ClassComponent
    1. 本次更新不是调用 forceUpdate 来更新的
    2. shouldComponentUpdate 中对更新的属性进行判断来决定本次更新是不需更新的
  2. FunctionComponent
    1. dev 时,元素类型 type 不变
    2. props 不变
    3. context 没有更新
    4. 本次优先级足够

参考

[ beginWork / updateFunctionComponent / updateClassComponent / finishClassComponent] github.com/facebook/re…

[updateClassInstance / checkShouldComponentUpdate ] github.com/facebook/re…

[checkHasForceUpdateAfterProcessing] github.com/facebook/re…

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • beginWork
  • ClassComponent
  • FunctionComponent
  • 总结
  • 参考
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档