Архитектура React Fiber исходного кода React16

исходный код React.js

Оригинальный адрес:GitHub.com/hu J IA oh J/ Первоначально…

Исходный код этой статьи — это код основной ветки хранилища React, извлеченный 10 августа 2018 г.

В анализе исходного кода React много контента, и эта статья посвящена следующим двум вопросам:

  • JSX -> ? -> DOM
  • Как называются функции жизненного цикла React Component?

Перед началом анализа исходного кода сначала вкратце введите некоторые основные концепции реагирования

Основные понятия

Позиционирование React — это библиотека классов JavaScript для создания пользовательских интерфейсов, которая использует JavaScript для разработки компонентов пользовательского интерфейса и поддерживает несколько способов рендеринга компонентов и вывода пользовательских интерфейсов.

Существует три распространенных типа приложений React:

  • Реагировать на веб-приложение
  • Реагировать на родное приложение
  • Реагировать на рендеринг на стороне сервера

Эти три приложения соответствуют трем различным методам рендеринга:

  • Рендеринг веб-DOM
  • Рендеринг собственного представления на стороне клиента
  • рендеринг строк на стороне сервера

Давайте возьмем веб-приложение React в качестве примера, чтобы представить три основных компонента React:

  • Базовый модуль React (этот модуль определяет базовый API и связанный с компонентами контент React. Соответствует модулю «реакции», представленному при разработке страниц)
  • Модуль рендеринга (этот модуль использует разные методы рендеринга для разных типов приложений. Соответствует модулю «react-dom», представленному при разработке страниц)
  • Модуль согласования (также называемый модулем координации, этот модуль является основой двух вышеупомянутых модулей, а также находится в центре внимания этой статьи, в основном отвечает за координацию задач, управление функциями жизненного цикла и т. д.)

Прежде чем приступить к модулю согласования, краткое введение в каждый модуль:

Реагировать базовый модуль

const React = {
  Children: {...},

  createRef,
  Component,
  PureComponent,

  createContext,
  forwardRef,

  Fragment: REACT_FRAGMENT_TYPE,
  StrictMode: REACT_STRICT_MODE_TYPE,
  unstable_AsyncMode: REACT_ASYNC_MODE_TYPE,
  unstable_Profiler: REACT_PROFILER_TYPE,

  createElement,
  cloneElement,
  createFactory,
  isValidElement,

  version: ReactVersion,

  __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: ReactSharedInternals,
};

Как видно из приведенного выше исходного кода, базовый модуль React включает только базовые API и определения, связанные с компонентами. Например: createRef, Component и т. д.

Два момента, на которые стоит обратить внимание:

1. Реагировать.создатьэлемент

В обычной разработке мы используем синтаксис JSX, поэтому прямого контакта с методом React.creatElement у нас нет

Как мы все знаем, синтаксис JSX будет скомпилирован babel для вызова метода React.creatElement следующим образом:

И React.creatElement, наконец, возвращает React Element, структура данных выглядит следующим образом:

{
    ?typeof: REACT_ELEMENT_TYPE,
    type: type,
    key: key,
    ref: ref,
    props: props,
    _owner: owner,
}

можно разместить на странице<App/>Распечатайте его следующим образом:

2. Реагировать.компонент

Компоненты — это те, которые мы разрабатываем и используем чаще всего, мы можем просто посмотреть исходный код:

function Component(props, context, updater) {
  this.props = props;
  this.context = context;
  // If a component has string refs, we will assign a different object later.
  this.refs = emptyObject;
  // We initialize the default updater but the real one gets injected by the
  // renderer.
  this.updater = updater || ReactNoopUpdateQueue;
}
Component.prototype.isReactComponent = {};
Component.prototype.setState = function(partialState, callback) {
  this.updater.enqueueSetState(this, partialState, callback, 'setState');
};
Component.prototype.forceUpdate = function(callback) {
  this.updater.enqueueForceUpdate(this, callback, 'forceUpdate');
};

Из определения компонента мы видим, что наш обычно используемый метод setState должен вызвать updater.enqueueSetState, Взяв в качестве примера реакцию-дом, этот объект обновления будет вызывать конструктор компонента (это будет в более поздних вызовах функций жизненного цикла) упоминалось), присвоенный classComponentUpdater, исходный код выглядит следующим образом:

const classComponentUpdater = {
  isMounted,
  enqueueSetState(inst, payload, callback) {
    ...
  },
  enqueueReplaceState(inst, payload, callback) {
    ...
  },
  enqueueForceUpdate(inst, callback) {
    ...
  },
};

Вы можете знать, что вызов setState в компоненте на самом деле вызывает метод classComponentUpdater.enqueueSetState, который является записью для запуска setState.

До сих пор был кратко представлен базовый модуль React, а модуль рендеринга представлен ниже:

Модуль рендеринга: react-dom

const ReactDOM: Object = {
  createPortal,
  findDOMNode(
    componentOrElement: Element | ?React$Component<any, any>,
  ): null | Element | Text {
    ...
  },
  hydrate(element: React$Node, container: DOMContainer, callback: ?Function) {
    return legacyRenderSubtreeIntoContainer(null, element, container, true, callback,);
  },

  render(element: React$Element<any>, container: DOMContainer, callback: ?Function,) {
    return legacyRenderSubtreeIntoContainer(null, element, container, false, callback,);
  },
  ...
};

Здесь мы можем обратить внимание на метод рендеринга.Все записи реагирующих веб-приложений будут вызывать ReactDOM.render(). Эта статья также начнется с ReactDOM.render() для анализа исходного кода.

Прежде чем приступить к анализу исходного кода, давайте представим суть этой статьи: модуль Reconciliation.

Модуль согласования

Модуль Согласования еще называют модулем согласования, и то, что мы говорили по темеReact Fiberзаключается в использовании алгоритма планирования в этом модуле

Алгоритм планирования React Fiber, также известный как Fiber Reconciler, — это новый алгоритм планирования, включенный в React 16. Это реконструкция основного алгоритма планирования (Stack Reconciler).

Stack Reconciler

Алгоритм планирования Stack Reconciler, использовавшийся до версии React 16, который рекурсивно обходит виртуальный DOM, трудно прервать и восстановить.Если задача обновления реакции выполняется слишком долго, она блокирует макет, анимацию и т. д., что может привести к к неудачам. Его стек вызовов выглядит следующим образом:

Fiber Reconciler

Позволяет завершать процесс рендеринга поэтапно, вместо того, чтобы выполнять его за один раз, и может вернуться к основному процессу, чтобы контролировать выполнение других задач в середине.Он имеет следующие новые функции:

  • Разделяемые, прерываемые задачи
  • Поэтапные задачи могут быть повторно использованы и расставлены по приоритетам
  • Вы можете переключать задачи вперед и назад между задачами родительского и дочернего компонентов.
  • Метод рендеринга может возвращать несколько элементов (то есть он может возвращать массив)
  • Поддержка границ исключений для обработки исключений

Его стек вызовов выглядит следующим образом:

Для сравнения нового и старого алгоритмов планирования React вы можете увидеть:zhuanlan.zhihu.com/p/37095662

Для более подробного ознакомления с концепцией React Fiber вы можете посмотреть:воооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооооо

Выше были представлены основные концепции React, а затем начнется анализ исходного кода~

Анализ исходного кода

Архитектура React Fiber представляет новую структуру данных: узлы Fiber.

Fiber

Структура данных оптоволоконного узла выглядит следующим образом:

export type Fiber = {|
  // Tag identifying the type of fiber.
  tag: TypeOfWork,
  // Unique identifier of this child.
  key: null | string,
  // The function/class/module associated with this fiber.
  type: any,
  // The local state associated with this fiber.
  stateNode: any,
  // Remaining fields belong to Fiber
  return: Fiber | null,
  // Singly Linked List Tree Structure.
  child: Fiber | null,
  sibling: Fiber | null,
  index: number,
  // The ref last used to attach this node.
  ref: null | (((handle: mixed) => void) & {_stringRef: ?string}) | RefObject,
  // Input is the data coming into process this fiber. Arguments. Props.
  pendingProps: any, // This type will be more specific once we overload the tag.
  memoizedProps: any, // The props used to create the output.
  // A queue of state updates and callbacks.
  updateQueue: UpdateQueue<any> | null,
  // The state used to create the output
  memoizedState: any,
  // A linked-list of contexts that this fiber depends on
  firstContextDependency: ContextDependency<mixed> | null,
  mode: TypeOfMode,
  // Effect
  effectTag: TypeOfSideEffect,
  // Singly linked list fast path to the next fiber with side-effects.
  nextEffect: Fiber | null,
  firstEffect: Fiber | null,
  lastEffect: Fiber | null,

  expirationTime: ExpirationTime,
  childExpirationTime: ExpirationTime,

  alternate: Fiber | null,
  actualDuration?: number,
  actualStartTime?: number,
  selfBaseDuration?: number,
  treeBaseDuration?: number,
|};

Схема структуры дерева Fiber (структура связанного списка) выглядит следующим образом:

Процесс вызова исходной функции

Давайте посмотрим на картинку:

Рендеринг компонента React делится на два этапа: согласование и рендеринг. Как видно из рисунка:

  • Этап примирения - это этап операции Virtual DOM, который соответствует новым алгоритмам планирования, который должен найти работу, которая должна быть обновлена.
  • Этап рендеринга — это этап рендеринга, на котором выполняется обновление в разных приложениях с использованием разных методов рендеринга для рендеринга.

Как упоминалось во введении к основным понятиям выше, модуль react-dom отвечает за рендеринг веб-приложения React, так что же именно делает модуль Reconciliation (модуль координации)?

Работу модуля «Выверка» можно разделить на две части:

1. Примирение

Проще говоря, это найти работу, которую необходимо обновить, и выяснить работу по обновлению, которую необходимо выполнить с помощью Diff Fiber Tree. Это процесс расчета js. Результаты расчета могут быть кэшированы, процесс расчета может быть прерван, и выполнение может быть возобновлено.

Поэтому, когда выше был представлен алгоритм планирования Fiber Reconciler, было упомянуто, что новый алгоритм имеет новые характеристики разделяемых и прерываемых задач, потому что эта часть работы представляет собой чистый процесс вычислений js, поэтому его можно кэшировать, прерывать и восстановлен

2. зафиксировать

Отправьте обновление и вызовите соответствующий модуль рендеринга (react-dom) для рендеринга.Во избежание тряски страницы процесс синхронный и не может быть прерван

Давайте посмотрим на конкретный процесс вызова функции этих двух этапов.

этап примирения

Возьмем метод ReactDOM.render() в качестве входа, чтобы увидеть процесс вызова функции на этапе согласования:

Как видно из рисунка, я разделил этот этап на три части, которые разделены красными линиями. Кратко подытожим работу следующих трех частей:

1. Первая часть начинается с метода ReactDOM.render(), преобразует полученный элемент React в узел Fiber, устанавливает для него приоритет, записывает обновление и т. д. Эта часть в основном посвящена подготовке данных.

2. Вторая часть в основном состоит из трех функций: scheduleWork, requestWork, PerformWork, то есть трилогия организации работы, подачи заявки на работу и формальной работы. В этой части реализована новая функция асинхронного вызова React 16.

3. Третья часть представляет собой большой цикл, проходящий через все узлы Fiber, вычисляющий всю работу по обновлению с помощью алгоритма Diff и выводящийEffectListОн используется на этапе фиксации. Сердцем этой части является функция beginWork.

первая часть

Первая часть относительно проста и не будет подробно здесь описываться, друзья могут прочитать исходный код самостоятельно~

Часть II: Координация задач

Трилогия: scheduleWork, requestWork, PerformWork (организация работы, подача заявки на работу, официальная работа)

В функции requestWork в трилогии она будет определять, является ли текущая задача синхронной или асинхронной (на данный момент функция асинхронного вызова React все еще находится в стадии разработки и не была открыта для использования. Последующее содержание этой статьи занимает синхронные задачи в качестве примера), а затем через разные методы вызывать task. Синхронная задача напрямую вызывает функцию PerformWork для немедленного выполнения, в то время как асинхронная задача будет выполняться позже, так как же планируется выполнение асинхронной задачи?

Существует два способа планирования асинхронных задач, которые в основном оцениваются по приоритету задачи. Есть два основных способа:

1. анимация (анимация): будет вызываться API requestAnimationFrame, чтобы сказать браузеру, чтобы он вызывал эту задачу, чтобы обновить анимацию перед следующей перерисовкой

2. Другие асинхронные задачи: API requestIdleCallback будет вызываться, чтобы указать браузеру вызывать задачи последовательно в течение периода простоя браузера, что позволяет разработчикам выполнять фоновые или низкоприоритетные задачи в основном цикле событий и не будет нацеливаться на анимацию. ключевые события, такие как взаимодействие с пользователем, оказывают влияние

Вышеупомянутые два API являются собственными API. Для более глубокого понимания вы можете взглянуть на:requestAnimationFrame,requestIdleCallback

Нативный requestIdleCallback имеет проблемы с совместимостью, поэтому React сам разработал модуль ReactScheduler для реализации этой функции.

Задача синхронизации будет использоваться в качестве примера в дальнейшем, поэтому мы начинаем знакомить с основной функцией третьей части: beginWork

Часть 3: начинающие

Как видно из приведенной выше блок-схемы вызова функции, beginWork вызывается в большом цикле и возвращает дочерние узлы текущего узла.

function beginWork(
  current: Fiber | null,
  workInProgress: Fiber,
  renderExpirationTime: ExpirationTime,
): Fiber | null {
  const updateExpirationTime = workInProgress.expirationTime;
  if (!hasLegacyContextChanged() && (updateExpirationTime === NoWork || updateExpirationTime > renderExpirationTime)) {
    switch (workInProgress.tag) {
      case HostRoot:
        ...
      case HostComponent:
       ...
      case ClassComponent:
        pushLegacyContextProvider(workInProgress);
        break;
      case HostPortal:
        ...
      case ContextProvider:
        ...
      case Profiler:
        ...
    }
    return bailoutOnAlreadyFinishedWork(current, workInProgress, renderExpirationTime,);
  }

  // Before entering the begin phase, clear the expiration time.
  workInProgress.expirationTime = NoWork;

  switch (workInProgress.tag) {
    case IndeterminateComponent:
      return mountIndeterminateComponent(current, workInProgress, renderExpirationTime,);
    case FunctionalComponent:
      return updateFunctionalComponent(current, workInProgress, renderExpirationTime,);
    case ClassComponent:
      return updateClassComponent(current, workInProgress, renderExpirationTime,);
    case HostRoot:
      return updateHostRoot(current, workInProgress, renderExpirationTime);
    case HostComponent:
      return updateHostComponent(current, workInProgress, renderExpirationTime);
    case HostText:
      return updateHostText(current, workInProgress);
    case PlaceholderComponent:
      return updatePlaceholderComponent(current, workInProgress, renderExpirationTime,);
    case HostPortal:
      return updatePortalComponent(current, workInProgress, renderExpirationTime,);
    case ForwardRef:
      return updateForwardRef(current, workInProgress, renderExpirationTime);
    case Fragment:
      return updateFragment(current, workInProgress, renderExpirationTime);
    case Mode:
      return updateMode(current, workInProgress, renderExpirationTime);
    case Profiler:
      return updateProfiler(current, workInProgress, renderExpirationTime);
    case ContextProvider:
      return updateContextProvider(current, workInProgress, renderExpirationTime,);
    case ContextConsumer:
      return updateContextConsumer(current, workInProgress, renderExpirationTime,);
    default:
      ...
  }
}

Во-первых, давайте представим технологию двойной буферизации архитектуры React Fiber:

Как видно из рисунка выше, существует два дерева Fiber: текущее и workInProgress.Они связаны альтернативным атрибутом на каждом узле Fiber.Вы можете просмотреть метод createWorkInProgress в исходном коде ReactFiber.js следующим образом:

export function createWorkInProgress(
  current: Fiber,
  pendingProps: any,
  expirationTime: ExpirationTime,
): Fiber {
  let workInProgress = current.alternate;
  if (workInProgress === null) {
    workInProgress = createFiber(current.tag, pendingProps, current.key, current.mode,);
    ...
    workInProgress.alternate = current;
    current.alternate = workInProgress;
  } else {
    workInProgress.effectTag = NoEffect;
    workInProgress.nextEffect = null;
    workInProgress.firstEffect = null;
    workInProgress.lastEffect = null;
    ...
  }
  ...
  return workInProgress;
}

Приведенный выше код упрощен, можно обнаружить, что current и workInProgress содержат ссылки друг на друга. Как видно из рисунка выше, все обновления выполняются на workInProgress, после завершения обновления текущий указатель указывает на workInProgress, тем самым отбрасывая старое дерево волокон.

Из исходного кода beginWork он в основном разделен на две части: одна — обработка контекста, а другая — вызов соответствующего метода обновления в соответствии с типом тега объекта волокна. Здесь мы сосредоточимся на второй части. Во второй части мы возьмем в качестве примера тип ClassComponent и поговорим о том, что делается в функции updateClassComponent?

Есть две основные части: вызов функции жизненного цикла и алгоритм Diff.

Вызовы функций жизненного цикла

Блок-схема выглядит следующим образом:

current имеет значение null, что означает, что текущее обновление — это первый раз, когда компонент визуализируется.

1. Вызвать convertClassInstance для создания экземпляра компонента, в основном вызываяconstructorКонструктор и инъекция classComponentUpdater (это инъекция обновления, упомянутая во введении к React Component в начале статьи)

2, mountClassInstance должен вызыватьgetDerivedStateFromPropsФункции жизненного цикла (v16) иUNSAFE_componentWillMountфункция жизненного цикла

текущий не нулевой, вызовите метод updateClassInstance

1. Если старый и новый реквизит несовместимы, он будет вызванUNSAFE_componentWillReceivePropsфункция жизненного цикла

2. Затем позвонитеshouldComponentUpdateФункция жизненного цикла, получить значение shouldUpdate, если эта функция жизненного цикла не определена, значение по умолчанию равно true (будь то повторный рендеринг), если shouldUpdate равно true, она будет вызванаUNSAFE_componentWillUpdateфункция жизненного цикла

Наконец, вызовите метод FinishClassComponent, так что же делается в функции FinishClassComponent? Блок-схема выглядит следующим образом:

Если shouldUpdate имеет значение false, это означает, что обновление не требуется, вернитесь напрямую

Если shouldUpdate истинно, вызовитеrenderметод, который возвращает новый дочерний узел

Если это первая визуализация, вызовите mountChildFibers, чтобы создать экземпляр Fiber дочерних узлов.

В противном случае вызовите reconcileChildFibers, чтобы сравнить старые и новые дочерние узлы.

На данный момент функция updateClassComponent в основном выполняет функцию жизненного цикла компонента.Давайте поговорим об алгоритме Diff, который необходимо использовать для сравнения новых и старых дочерних узлов.

Электюс алгоритм

В функции reconcileChildFibers исходный код выглядит следующим образом:

function reconcileChildFibers(
    returnFiber: Fiber,
    currentFirstChild: Fiber | null,
    newChild: any,
    expirationTime: ExpirationTime,
    ): Fiber | null {
    const isUnkeyedTopLevelFragment = typeof newChild === 'object' && newChild !== null && newChild.type === REACT_FRAGMENT_TYPE && newChild.key === null;
    if (isUnkeyedTopLevelFragment) {
        newChild = newChild.props.children;
    }
    const isObject = typeof newChild === 'object' && newChild !== null;
    if (isObject) {
        switch (newChild.?typeof) {
        case REACT_ELEMENT_TYPE:
            return placeSingleChild(
                reconcileSingleElement(returnFiber, currentFirstChild, newChild, expirationTime,),
            );
        case REACT_PORTAL_TYPE:
            return placeSingleChild(
                reconcileSinglePortal(returnFiber, currentFirstChild, newChild, expirationTime,),
            );
        }
    }
    if (typeof newChild === 'string' || typeof newChild === 'number') {
        return placeSingleChild(
            reconcileSingleTextNode(returnFiber, currentFirstChild, '' + newChild, expirationTime,),
        );
    }
    if (isArray(newChild)) {
        return reconcileChildrenArray(returnFiber, currentFirstChild, newChild, expirationTime,);
    }
    if (getIteratorFn(newChild)) {
        return reconcileChildrenIterator(returnFiber, currentFirstChild, newChild, expirationTime,);
    }
    if (isObject) {
        throwOnInvalidObjectType(returnFiber, newChild);
    }
    // Remaining cases are all treated as empty.
    return deleteRemainingChildren(returnFiber, currentFirstChild);
}

Функция reconcileChildFibers в основном вызывает различные алгоритмы Diff в соответствии с типом newChild:

1. Для одного элемента вызовите reconcileSingleElement.

2. Для одного элемента Portal вызовите reconcileSinglePortal.

3, строка или число, вызов reconcileSingleTextNode

4. Массив (новинка в React 16), вызов reconcileChildrenArray

В первых трех случаях добавьте effectTag: Placement на новый дочерний узел, который помечен как операция обновления, и теги этих операций будут использоваться на этапе фиксации. Ниже в качестве примера используется один элемент, чтобы рассказать о конкретном алгоритме Diff.

Исходный код функции reconcileSingleElement выглядит следующим образом:

function reconcileSingleElement(
    returnFiber: Fiber,
    currentFirstChild: Fiber | null,
    element: ReactElement,
    expirationTime: ExpirationTime,
    ): Fiber {
    const key = element.key;
    let child = currentFirstChild;
    while (child !== null) {
        // 判断key是否相等
        if (child.key === key) {
            if (child.tag === Fragment ? element.type === REACT_FRAGMENT_TYPE : child.type === element.type) {
                // key相等且type相等,删除旧子节点的兄弟节点,复用旧节点并返回
                deleteRemainingChildren(returnFiber, child.sibling);
                const existing = useFiber(child, element.type === REACT_FRAGMENT_TYPE ? element.props.children : element.props, expirationTime,);
                existing.ref = coerceRef(returnFiber, child, element);
                existing.return = returnFiber;
                return existing;
            } else {
                // key相等但type不相等,删除旧子节点及兄弟节点,跳出循环
                deleteRemainingChildren(returnFiber, child);
                break;
            }
        } else {
            // key不相等,删除此旧子节点,继续循环
            deleteChild(returnFiber, child);
        }
        // 继续遍历此旧子节点的兄弟节点
        child = child.sibling;
    }
    // 不能复用,则直接新建Fiber实例,并返回
    if (element.type === REACT_FRAGMENT_TYPE) {
        const created = createFiberFromFragment(element.props.children, returnFiber.mode, expirationTime,
        element.key,);
        created.return = returnFiber;
        return created;
    } else {
        const created = createFiberFromElement(element, returnFiber.mode, expirationTime,);
        created.ref = coerceRef(returnFiber, currentFirstChild, element);
        created.return = returnFiber;
        return created;
    }
}

Конкретный процесс четко прописан в комментариях к коду, поэтому здесь я не буду его подробно раскрывать. Однако мы можем посмотреть, что делается в deleteChild (удаление дочерних узлов), исходный код выглядит следующим образом:

function deleteChild(returnFiber: Fiber, childToDelete: Fiber): void {
    if (!shouldTrackSideEffects) {
        return;
    }
    const last = returnFiber.lastEffect;
    if (last !== null) {
        last.nextEffect = childToDelete;
        returnFiber.lastEffect = childToDelete;
    } else {
        returnFiber.firstEffect = returnFiber.lastEffect = childToDelete;
    }
    childToDelete.nextEffect = null;
    childToDelete.effectTag = Deletion;
}

Видно, что когда deleteChild удаляет дочерний узел, он на самом деле не удаляет объект, а поддерживает EffectList (структуру связанного списка) через атрибуты firstEffect, lastEffect, nextEffect и помечает текущую операцию удаления через effectTag, которая будет используется на этапе фиксации.

Выше показан весь процесс функции beginWork.Известно, что после обхода дерева Fiber с помощью алгоритма Diff можно создать список эффектов и использовать его на этапе фиксации.

этап фиксации

Схема вызова функции выглядит следующим образом:

Что делает фаза фиксации, так это получает EffectList, созданный на этапе согласования, то есть всю работу по обновлению, отправляет эту работу по обновлению и вызывает модуль рендеринга (react-dom) для рендеринга пользовательского интерфейса.

effectTag

Как упоминалось ранее, фаза фиксации будет определять тип операции с помощью тега effectTag, поэтому давайте сначала рассмотрим типы effectTag:

// Don't change these two values. They're used by React Dev Tools.
export const NoEffect = /*              */ 0b00000000000;
export const PerformedWork = /*         */ 0b00000000001;
// You can change the rest (and add more).
export const Placement = /*             */ 0b00000000010;
export const Update = /*                */ 0b00000000100;
export const PlacementAndUpdate = /*    */ 0b00000000110;
export const Deletion = /*              */ 0b00000001000;
export const ContentReset = /*          */ 0b00000010000;
export const Callback = /*              */ 0b00000100000;
export const DidCapture = /*            */ 0b00001000000;
export const Ref = /*                   */ 0b00010000000;
export const Snapshot = /*              */ 0b00100000000;
// Update & Callback & Ref & Snapshot
export const LifecycleEffectMask = /*   */ 0b00110100100;
// Union of all host effects
export const HostEffectMask = /*        */ 0b00111111111;
export const Incomplete = /*            */ 0b01000000000;
export const ShouldCapture = /*         */ 0b10000000000;

можно увидеть:

1. Тип effectTag представлен двоичными битами и может многократно накладываться друг на друга.

2. Сопоставьте тип effectTag по битовой операции

Из приведенной выше блок-схемы видно, что на этапе фиксации есть три важные функции:

1. коммитBeforeMutationLifecycles

Эта функция в основном для сохранения снижения текущего DOM, выполняетсяgetSnapshotBeforeUpdateфункция жизненного цикла

2. фиксация всех эффектов хоста

Отправьте все обновления и рендер, исходный код выглядит следующим образом:

function commitAllHostEffects() {
  while (nextEffect !== null) {
    recordEffect();
    const effectTag = nextEffect.effectTag;
    if (effectTag & ContentReset) {
      commitResetTextContent(nextEffect);
    }
    if (effectTag & Ref) {
      const current = nextEffect.alternate;
      if (current !== null) {
        commitDetachRef(current);
      }
    }
    let primaryEffectTag = effectTag & (Placement | Update | Deletion);
    switch (primaryEffectTag) {
      case Placement: {
        commitPlacement(nextEffect);
        nextEffect.effectTag &= ~Placement;
        break;
      }
      case PlacementAndUpdate: {
        commitPlacement(nextEffect);
        nextEffect.effectTag &= ~Placement;
        const current = nextEffect.alternate;
        commitWork(current, nextEffect);
        break;
      }
      case Update: {
        const current = nextEffect.alternate;
        commitWork(current, nextEffect);
        break;
      }
      case Deletion: {
        commitDeletion(nextEffect);
        break;
      }
    }
    nextEffect = nextEffect.nextEffect;
  }
}

Как видно из исходного кода, эта функция в основном проходит по EffectList, вызывает соответствующий метод фиксации в соответствии с effectTag, а затем вызывает метод, предоставленный react-dom, для работы с DOM для рендеринга пользовательского интерфейса. ДОМ следующие:

{
  getPublicInstance,
  supportsMutation,
  supportsPersistence,
  commitMount,
  commitUpdate,
  resetTextContent,
  commitTextUpdate,
  appendChild,
  appendChildToContainer,
  insertBefore,
  insertInContainerBefore,
  removeChild,
  removeChildFromContainer,
  replaceContainerChildren,
  createContainerChildSet,
}

Обратите внимание, что когда вызывается метод фиксации операции удаления, он будет выполнятьсяcomponentWillUnmountфункция жизненного цикла

В этом методе работа по отправке обновления и рендерингу пользовательского интерфейса в основном завершена.

3. совершить все жизненные циклы

Эта функция в основном выполняет соответствующую обработку в соответствии с типом волоконного узла.В качестве примера возьмем ClassComponent, после завершения рендеринга пользовательского интерфейса будут выполнены последующие функции жизненного цикла:

1. Определяем рендерить ли в первый раз, если да то выполняемcomponentDidMountфункция жизненного цикла

2. В противном случае выполнитьcomponentDidUpdateфункция жизненного цикла

Выше приведен весь процесс этапа фиксации

На этом весь процесс нашего исходного кода и так далее также завершен, давайте подытожим весь процесс вызова функции:

Суммировать

Наконец, мы возвращаемся к двум вопросам, которые мы задали в начале:

  • JSX -> ? -> DOM
  • Как называются функции жизненного цикла React Component?

Вам не кажется, что весь процесс очень ясен~~~

В приложении сводная таблица функций жизненного цикла:

напиши в конце

Выше я делюсь исходным кодом React16, надеюсь, он будет полезен тем, кто в этом нуждается~~~

Если вам понравилась моя статья, вы можете перейтимой личный блогнажмите звездочку ⭐️