前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >小前端读源码 - React16.7.0(渲染总结篇)

小前端读源码 - React16.7.0(渲染总结篇)

作者头像
LamHo
发布2022-09-26 10:38:39
3430
发布2022-09-26 10:38:39
举报
文章被收录于专栏:小前端看世界

读前须知

之前写了五篇关于React的渲染过程的阅读流程,发现其实很多事情都写得比较青涩难懂,当然也可能是我的写作水平问题,中间其实也没有去说一些生命周期的事情。所以将会用一篇比较长的总结文章去说明React16.7.0的代码流程。

个人建议不要单纯的看,结合源码一起看,会比较容易了解到里面的原理和意思。

之前的几篇文章链接:

  1. 小前端读源码 - React16.7.0(一) —— ReactElement的创建过程
  2. 小前端读源码 - React16.7.0(二) —— 创建根Fiber过程
  3. 小前端读源码 - React16.7.0(三) —— render阶段:Fiber树创建过程1
  4. 小前端读源码 - React16.7.0(四) —— render阶段:Fiber树创建过程2
  5. 小前端读源码 - React16.7.0(五) —— commit阶段:渲染DOM

基本概念:

  1. ReactElement
  2. Fiber
  3. current树和workInProgress树

什么是ReactElement?

ReactElement是通过babel编译后转换成的react.createElement调用后返回出来的数据结构。

通过$$typeof去标识为一个React元素,并且将对应的props,key,ref,添加上去,最后会以type描述该元素。这些都是通过react.createElement传入的参数决定的。

什么是Fiber节点?

从版本 16 开始, React 推出了内部实例树的新的实现方法,以及被称之为Fiber的算法。这是一种全新的虚拟DOM的实现。

出现它的原因是为17版本的异步渲染机制如果使用以前旧的虚拟DOM的方式无法实现。然后Fiber的出现彻底的改变了整个React的底层架构,所以以前我们所知道的React的很多原理都已经不再一样了。

在React中每一个组件都会先经过react.createElement转换成一个ReactElement,在通过对每一个组件的ReactElement生成一个对应的Fiber节点(包括根节点)。而这些节点将会以链表的方式存放在根Fiber下,形成一个Fiber节点树。

React内有两颗树

刚噶所有说到的,React内部会对每个ReactElement创建一个Fiber节点,从而形成一个Fiber树,而Fiber树分为两种,一种是代表当前页面中每个组件状态的树,我们称为current树,而另外一棵树是在更新节点的时候,React会根据当前的current树,以及一些修改过的参数生成一个workInProgress树,最终React会将workInProgress树渲染到页面中,并且在渲染后,workInProgress树就会变成current树。简单来说,current树是代表当前页面中组件的状态,而workInProgress树是代表之后需要渲染的组件状态。


Fiber生成流程

以下是一个DEMO,之后所有的内容都是基于这个DEMO去展开。

代码语言:javascript
复制
import React from 'react';
import ReactDOM from 'react-dom';

class Component1 extends React.Component {
    render() {
        return (
            <div>
                <p>text1</p>
                <p>text2</p>
            </div>
        )
    }
}

class Component2 extends React.Component {
    static defaultProps = {
        text: 'component2'
    }
    render() {
        return (<p>{this.props.text}</p>)
    }
}

class App extends React.Component {
    constructor() {
        super();
        this.state = {
            data: 1
        }
    }
    render() {
        return (
            <div>
                <button onClick={() => {this.setState({data: 2})}}>setState</button>
                <Component1/>
                <Component2/>
            </div>
        )
    }
}

ReactDOM.render(
    <App/>,
    document.getElementById('root')
)

首先经过JSX编译器后,每一个组件都会变成一个ReactElement。当然子组件并非一开始执行react.createElement的,只有根元素会在一开始传入ReactDOM.render的时候先转换成ReactElement对象。

  1. ReactDOM.render
  2. legacyRenderSubtreeIntoContainer
  3. legacyCreateRootFromDOMContainer

通过以上的函数创建了根Fiber节点。

container是传入的根元素的DOM对象。

代码语言:javascript
复制
var root = container._reactRootContainer;
root = container._reactRootContainer = legacyCreateRootFromDOMContainer(container, forceHydrate);

container是DOM对象,可以通过全局查找到容器DOM对象的_reactRootContainer获取到Fiber树(current树)。 root变量中带有一个_internalRoot属性,同样指向Fiber树(current树)


React的渲染流程可以分为两个主要的阶段。

  • render阶段
  • commit阶段

render阶段

render阶段开始是从renderRoot函数开始。在这个阶段之前,其实都是对根Fiber节点的一些初始化。但我觉得需要说明一下这个根Fiber节点的一个内容就是现在的根Fiber里面有一个updateQueue属性,里面包含了一下属性:

我们发现在根Fiber中的任务中firstUpdate中的element就是App的ReactElement对象。之后的workLoop就是通过获取这个属性来绑定根Fiber节点和App的Fiber节点的关系。

那么简单说一下这个updateQueue是哪里来的。

  1. root.render
  2. updateContainer
  3. updateContainerAtExpirationTime
  4. scheduleRootUpdate
  5. update = createUpdate(创建了一个update对象)
  6. update.payload = { element: element };
  7. enqueueUpdate(在这里将update对象赋值到根Fiber的updateQueue中)

可以参考我的第二篇React源码阅读的文章。

Lam:小前端读源码 - React16.7.0(二)

在React的渲染过程中,整个Fiber树是由一个workLoop函数循环构建出来的。代码如下:

在workLoop中主要经过几个关键的步骤:

  1. performUnitOfWork
  2. beginWork - 根据不同的类型的Fiber进行不同的操作
  3. completeUnitOfWork
  4. completeWork

之前说过根Fiber有一个比较特殊的updateQueue属性,其实就是在beginWork中判断到是根Fiber节点的话,会将updateQueue中的firstUpdate.element转换为Fiber节点,并赋值到根Fiber节点的child中。(App组件)

当前的组件结构:

以下是整个workLoop的渲染流程。

我们先看看performUnitOfWorkbeginWork这两个函数:

代码语言:javascript
复制
function performUnitOfWork(workInProgress) {
  // 省略代码....
  next = beginWork(current$$1, workInProgress, nextRenderExpirationTime);
  // 省略代码....
  if (next === null) {
    next = completeUnitOfWork(workInProgress);
  }
  // 省略代码....
  return next;
}

function beginWork(current$$1, workInProgress, renderExpirationTime) {
  // 省略代码....
  switch (workInProgress.tag) {
    case IndeterminateComponent:
      {
        var elementType = workInProgress.elementType;
        return mountIndeterminateComponent(current$$1, workInProgress, elementType, renderExpirationTime);
      }
    case LazyComponent:
      {
        var _elementType = workInProgress.elementType;
        return mountLazyComponent(current$$1, workInProgress, _elementType, updateExpirationTime, renderExpirationTime);
      }
    case FunctionComponent:
      {
        var _Component = workInProgress.type;
        var unresolvedProps = workInProgress.pendingProps;
        var resolvedProps = workInProgress.elementType === _Component ? unresolvedProps : resolveDefaultProps(_Component, unresolvedProps);
        return updateFunctionComponent(current$$1, workInProgress, _Component, resolvedProps, renderExpirationTime);
      }
    case ClassComponent:
      {
        var _Component2 = workInProgress.type;
        var _unresolvedProps = workInProgress.pendingProps;
        var _resolvedProps = workInProgress.elementType === _Component2 ? _unresolvedProps : resolveDefaultProps(_Component2, _unresolvedProps);
        return updateClassComponent(current$$1, workInProgress, _Component2, _resolvedProps, renderExpirationTime);
      }
    case HostRoot:
      return updateHostRoot(current$$1, workInProgress, renderExpirationTime);
    case HostComponent:
      return updateHostComponent(current$$1, workInProgress, renderExpirationTime);
    case HostText:
      return updateHostText(current$$1, workInProgress);
    case SuspenseComponent:
      return updateSuspenseComponent(current$$1, workInProgress, renderExpirationTime);
    case HostPortal:
      return updatePortalComponent(current$$1, workInProgress, renderExpirationTime);
    case ForwardRef:
      {
        var type = workInProgress.type;
        var _unresolvedProps2 = workInProgress.pendingProps;
        var _resolvedProps2 = workInProgress.elementType === type ? _unresolvedProps2 : resolveDefaultProps(type, _unresolvedProps2);
        return updateForwardRef(current$$1, workInProgress, type, _resolvedProps2, renderExpirationTime);
      }
    case Fragment:
      return updateFragment(current$$1, workInProgress, renderExpirationTime);
    case Mode:
      return updateMode(current$$1, workInProgress, renderExpirationTime);
    case Profiler:
      return updateProfiler(current$$1, workInProgress, renderExpirationTime);
    case ContextProvider:
      return updateContextProvider(current$$1, workInProgress, renderExpirationTime);
    case ContextConsumer:
      return updateContextConsumer(current$$1, workInProgress, renderExpirationTime);
    case MemoComponent:
      {
        var _type2 = workInProgress.type;
        var _unresolvedProps3 = workInProgress.pendingProps;
        // Resolve outer props first, then resolve inner props.
        var _resolvedProps3 = resolveDefaultProps(_type2, _unresolvedProps3);
        {
          if (workInProgress.type !== workInProgress.elementType) {
            var outerPropTypes = _type2.propTypes;
            if (outerPropTypes) {
              checkPropTypes_1(outerPropTypes, _resolvedProps3, // Resolved for outer only
              'prop', getComponentName(_type2), getCurrentFiberStackInDev);
            }
          }
        }
        _resolvedProps3 = resolveDefaultProps(_type2.type, _resolvedProps3);
        return updateMemoComponent(current$$1, workInProgress, _type2, _resolvedProps3, updateExpirationTime, renderExpirationTime);
      }
    case SimpleMemoComponent:
      {
        return updateSimpleMemoComponent(current$$1, workInProgress, workInProgress.type, workInProgress.pendingProps, updateExpirationTime, renderExpirationTime);
      }
    case IncompleteClassComponent:
      {
        var _Component3 = workInProgress.type;
        var _unresolvedProps4 = workInProgress.pendingProps;
        var _resolvedProps4 = workInProgress.elementType === _Component3 ? _unresolvedProps4 : resolveDefaultProps(_Component3, _unresolvedProps4);
        return mountIncompleteClassComponent(current$$1, workInProgress, _Component3, _resolvedProps4, renderExpirationTime);
      }
    default:
      invariant(false, 'Unknown unit of work tag. This error is likely caused by a bug in React. Please file an issue.');
  }
}

performUnitOfWorkbeginWork中接受workInProgress树中的一个Fiber节点,然后将该Fiber节点传入beginWork函数中,最后将beginWork处理完后的Fiber节点的childFiber节点返回出去到workLoop中。

beginWork会根据不同的Fiber节点类型进行不同的处理。以DEMO为例,会使用到3种不同的FIber类型。

  • HostRoot
  • ClassComponent
  • HostComponent

从刚刚的GIF中可以看到,React对于Fiber的构建是深度优先的。在beginWork中,根据不同的Fiber类型进行处理后,都会将处理后的FIber的child返回出来并赋值到next。如果当前的组件已经到达末尾,那么将会返回null。

如果在performUnitOfWork中得到beginWork函数返回是null,并将赋值到next中,将会执行completeUnitOfWork。代表当前的这个组件已经到达末尾了。

代码语言:javascript
复制
function completeUnitOfWork(workInProgress) {
  while (true) {
    // 省略代码....
    var returnFiber = workInProgress.return;
    var siblingFiber = workInProgress.sibling;
    // 省略代码....
    nextUnitOfWork = completeWork(current$$1, workInProgress, nextRenderExpirationTime);
    // 省略代码....
    if (siblingFiber !== null) {
      // If there is more work to do in this returnFiber, do that next.
      return siblingFiber;
    } else if (returnFiber !== null) {
      // If there's no more work in this returnFiber. Complete the returnFiber.
      workInProgress = returnFiber;
      continue;
    } else {
      return null;
    }
  }
  // 省略代码....
  return null;
}

performUnitOfWork中会执行completeWork,但是这个函数我们放到之后再去说明。能进入到completeUnitOfWork函数中,就代表当前的Fiber节点已经没有任何子节点了。那么这个时候就会判断当前的Fiber节点是否存在兄弟节点,如果没有就会将当前workInProgress指向当前Fiber节点的return(当前Fiber节点的父级Fiber节点)。

  1. 有兄弟节点 —— 返回兄弟节点到workLoop重新进入循环
  2. 无兄弟节点 —— 修改workInProgress指向,指向到当前Fiber节点的父级Fiber节点,回到第一步。
  3. 到达根Fiber —— 根Fiber没有任何兄弟和父级,将会return null,结束workLoop,进入commit阶段。

Commit阶段

从何时进入commit阶段呢?在renderRoot函数执行完workLoop之后,返回到performWorkOnRoot函数中执行completeRoot开始。

现在在root对象中有两颗树,一颗是current树(当前展示界面的Fiber树),一颗是finishedWork树(也叫workInProgress树,此树的作用就是本次render出来的Fiber树)。

current树

workInProgress树

因为当前的第一次渲染,所以current树种的child是为null,而workInProgress树经过了刚刚的render阶段,建立的一颗本次渲染的Fiber树,所以child不为null。

代码语言:javascript
复制
function performWorkOnRoot(root, expirationTime, isYieldy) {
  // 省略代码....
  renderRoot(root, isYieldy);
  finishedWork = root.finishedWork;
  if (finishedWork !== null) {
     // We've completed the root. Commit it.
     completeRoot(root, finishedWork, expirationTime);
  }
  // 省略代码....
}

因为经过renderRoot函数的render阶段后,root的finishedWork是一个完整的Fiber树,并且会进入completeRoot函数中,现在开始就是进入commit阶段了。

commitRoot是commit阶段的重要函数,所有的生命周期和新增、删除和更新组件都是通过commitRoot函数内执行的。

因为是第一次渲染,所以简单说明一下第一次渲染的函数调用流程:

  1. commitRoot
  2. commitAllHostEffects
  3. commitPlacement
  4. appendChildToContainer

这时候就会在页面中渲染出真实的DOM。最终会在commitRoot中将workInProgress树赋值到root的current树中,至此完成第一次渲染。

需要补充一些非常重要的属性的说明:

  • Fiber.firstEffect从哪里创建的?
  • 什么时候创建DOM对象的?
  • stateNode是什么?
  • nextEffect是什么?

Fiber.firstEffect

大家还记得completeUnitOfWork函数吗,在while的循环中,其实不断对returnFiber.firstEffect引用指向当前执行的Fiber节点的.firstEffect。并且对returnFiber.firstEffect赋值为当前的Fiber节点。

代码语言:javascript
复制
function completeUnitOfWork(workInProgress) {
  while (true) {
    var returnFiber = workInProgress.return;
    var siblingFiber = workInProgress.sibling;
    if (returnFiber !== null && (returnFiber.effectTag & Incomplete) === NoEffect) {
        if (returnFiber.firstEffect === null) {
          returnFiber.firstEffect = workInProgress.firstEffect;
        }
        if (workInProgress.lastEffect !== null) {
          if (returnFiber.lastEffect !== null) {
            returnFiber.lastEffect.nextEffect = workInProgress.firstEffect;
          }
          returnFiber.lastEffect = workInProgress.lastEffect;
        }

        var effectTag = workInProgress.effectTag;
        // Skip both NoWork and PerformedWork tags when creating the effect list.
        // PerformedWork effect is read by React DevTools but shouldn't be committed.
        if (effectTag > PerformedWork) {
          if (returnFiber.lastEffect !== null) {
            returnFiber.lastEffect.nextEffect = workInProgress;
          } else {
            returnFiber.firstEffect = workInProgress;
          }
          returnFiber.lastEffect = workInProgress;
        }
    }

    if (siblingFiber !== null) {
        // If there is more work to do in this returnFiber, do that next.
        return siblingFiber;
    } else if (returnFiber !== null) {
        // If there's no more work in this returnFiber. Complete the returnFiber.
        workInProgress = returnFiber;
        continue;
    } else {
        // We've reached the root.
        return null;
    }
}

最终root.finishedWork.firstEffect指向的是App的Fiber节点。

什么时候创建DOM对象的?

当一个节点分支到了末尾时候,将会把当前的Fiber节点传入completeUnitOfWork中,调用顺序如下:

  1. completeUnitOfWork
  2. completeWork
  3. createInstance
  4. createElement

最终赋值到当前Fiber节点的stateNode属性中。举个例子,在DEMO中,button原生的Fiber节点最终经过上述的执行顺序后,DOM对象将存在于Fiber.stateNode中。

completeWork调用完createInstance获取DOM对象后还调用了一个叫appendAllChildren的函数。

代码语言:javascript
复制
function (parent, workInProgress, needsVisibilityToggle, isHidden) {
    // We only have the top Fiber that was created but we need recurse down its
    // children to find all the terminal nodes.
    var node = workInProgress.child;
    while (node !== null) {
      if (node.tag === HostComponent || node.tag === HostText) {
        appendInitialChild(parent, node.stateNode);
      } else if (node.tag === HostPortal) {
        // If we have a portal child, then we don't want to traverse
        // down its children. Instead, we'll get insertions from each child in
        // the portal directly.
      } else if (node.child !== null) {
        node.child.return = node;
        node = node.child;
        continue;
      }
      if (node === workInProgress) {
        return;
      }
      while (node.sibling === null) {
        if (node.return === null || node.return === workInProgress) {
          return;
        }
        node = node.return;
      }
      node.sibling.return = node.return;
      node = node.sibling;
    }
  };
  1. 当前Fiber没有child —— 跳出
  2. 当前Fiber有child —— 如果child是一个DOM对象,将append到当前Fiber的DOM对象中,否则尝试获取child的child是否存在,存在会一直循环下去直到child为null
  3. 当前child无兄弟节点 —— 跳出
  4. 当前child有兄弟节点 —— 回到第二步

最终的workInProgress树的stateNode结构:

stateNode是什么?

stateNode就是当前的FIber对应的实际DOM对象的一个应用。可以理解为当前Fiber渲染出来的DOM就是stateNode中的DOM对象。

nextEffect是什么?

nextEffect是一个全局变量,在函数commitRoot中会将当前的workInProgress树的firstEffect,也就是AppFiber赋值到全局的nextEffect中,并在commitAllHostEffects函数中获取并用作渲染。


接下来说说生命周期,因为React实现了新的Fiber架构,为了以后的异步渲染,有部分生命周期会被移除以及添加了新的生命周期。

在官网中也有介绍新的一些生命周期API和一些会废除的生命周期API。

React.Component – React

梳理了一下16.7版本推介使用的生命周期图:

下面我们来简单看看每个生命周期会在那些地方去触发的。

getDerivedStateFromProps

render阶段中,在beginWork中的执行mountClassInstance中会有这么一段代码:

代码语言:javascript
复制
var getDerivedStateFromProps = ctor.getDerivedStateFromProps;
if (typeof getDerivedStateFromProps === 'function') {
  applyDerivedStateFromProps(workInProgress, ctor, getDerivedStateFromProps, newProps);
  instance.state = workInProgress.memoizedState;
}

applyDerivedStateFromProps代码如下:

代码语言:javascript
复制
function applyDerivedStateFromProps(workInProgress, ctor, getDerivedStateFromProps, nextProps) {
  var prevState = workInProgress.memoizedState;

  {
    if (debugRenderPhaseSideEffects || debugRenderPhaseSideEffectsForStrictMode && workInProgress.mode & StrictMode) {
      // Invoke the function an extra time to help detect side-effects.
      getDerivedStateFromProps(nextProps, prevState);
    }
  }

  var partialState = getDerivedStateFromProps(nextProps, prevState);

  {
    warnOnUndefinedDerivedState(ctor, partialState);
  }
  // Merge the partial state and the previous state.
  // 对getDerivedStateFromProps返回的state和现在的state进行合并
  var memoizedState = partialState === null || partialState === undefined ? prevState : _assign({}, prevState, partialState);
  workInProgress.memoizedState = memoizedState;

  // Once the update queue is empty, persist the derived state onto the
  // base state.
  var updateQueue = workInProgress.updateQueue;
  if (updateQueue !== null && workInProgress.expirationTime === NoWork) {
    updateQueue.baseState = memoizedState;
  }
}

render

render阶段的最后一个生命周期API,要记住执行render并不代表已经渲染到真实的DOM,因为render生命周期只是代表render阶段结束而已。在beginWork函数中最终会调用finishClassComponent函数中,在finishClassComponent函数中会有以下的代码:

代码语言:javascript
复制
var instance = workInProgress.stateNode;
instance.render();

componentDidMount

commit阶段中,在调用commitAllHostEffects函数后将会渲染DOM完毕,在之后会调用commitAllLifeCycles函数,通过该函数会调用componentDidMount。

代码语言:javascript
复制
var instance = finishedWork.stateNode;
instance.componentDidMount();

从代码中可以看到,确实componentDidMount是能获取到这次更新的DOM节点。

shouldComponentUpdate

render阶段,在非第一次渲染的情况下,beginWork会调用updateClassInstance函数,并返回一个shouldUpdate 传入到finishClassComponent函数中。

代码语言:javascript
复制
shouldUpdate = updateClassInstance(current$$1, workInProgress, Component, nextProps, renderExpirationTime);
var nextUnitOfWork = finishClassComponent(current$$1, workInProgress, Component, shouldUpdate, hasContext, renderExpirationTime);

finishClassComponent函数中会调用checkShouldComponentUpdate函数,返回一个布尔值。

代码语言:javascript
复制
function checkShouldComponentUpdate(workInProgress, ctor, oldProps, newProps, oldState, newState, nextContext) {
  var instance = workInProgress.stateNode;
  if (typeof instance.shouldComponentUpdate === 'function') {
    startPhaseTimer(workInProgress, 'shouldComponentUpdate');
    var shouldUpdate = instance.shouldComponentUpdate(newProps, newState, nextContext);
    stopPhaseTimer();

    {
      !(shouldUpdate !== undefined) ? warningWithoutStack$1(false, '%s.shouldComponentUpdate(): Returned undefined instead of a ' + 'boolean value. Make sure to return true or false.', getComponentName(ctor) || 'Component') : void 0;
    }

    return shouldUpdate;
  }

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

  return true;
}

从代码看到,默认会返回true,如果我们的instance中有shouldComponentUpdate将会返回执行后返回的布尔值。

finishClassComponent函数中会接收一个shouldUpdate参数,会对该参数做判断。

代码语言:javascript
复制
if (!shouldUpdate && !didCaptureError) {
  // Context providers should defer to sCU for rendering
  if (hasContext) {
    invalidateContextProvider(workInProgress, Component, false);
  }

  return bailoutOnAlreadyFinishedWork(current$$1, workInProgress, renderExpirationTime);
}

如果shouldUpdate为false,将不会往下执行并返回null,直接结束render阶段。因为经过Diff算法最终全局中的nextEffect为null,导致并不会执行commit阶段,所以也不会有commit阶段触发的生命周期。

getSnapshotBeforeUpdate

从生命周期的流程图可以看出,getSnapshotBeforeUpdate是属于在render生命周期执行后和真正渲染DOM之前的节点触发的,那就意味着getSnapshotBeforeUpdate生命周期还是可以获取到这次更新之前的DOM元素的。

在commit阶段是在commitRoot函数执行的时候开始的,在这个函数内,会执行3个函数,分别是:

  1. commitBeforeMutationLifecycles - commit阶段渲染DOM前的生命周期
  2. commitAllHostEffects - commit阶段渲染DOM
  3. commitAllLifeCycles - commit阶段渲染DOM后的生命周期

所以getSnapshotBeforeUpdate是在commitAllLifeCycles中执行的。

componentDidUpdate

componentDidUpdate和componentDidMount是一样的,都是在渲染完DOM后在commitAllLifeCycles函数中执行的,因为React会判断到这次并非第一次渲染,所以会执行componentDidUpdate而不是componentDidMount。


之后打算还会出以下几篇文章:

  1. setState源码阅读
  2. 合成事件源码阅读
  3. diff算法源码阅读
  4. key源码阅读

如果觉得这篇文章好不错,点个关注呗。

小前端的一点阅读记录,有不对的地方勿喷,请留言指导,非常感谢!

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019-01-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

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

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 读前须知
  • 什么是ReactElement?
  • 什么是Fiber节点?
  • React内有两颗树
  • Fiber生成流程
  • render阶段
  • Commit阶段
  • Fiber.firstEffect
  • 什么时候创建DOM对象的?
  • stateNode是什么?
  • nextEffect是什么?
  • getDerivedStateFromProps
  • render
  • componentDidMount
  • shouldComponentUpdate
  • getSnapshotBeforeUpdate
  • componentDidUpdate
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档