Устройство кодовой базы

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

Если вы хотите внести свой вклад в React, то мы надеемся, что эта глава вам поможет.

Мы считаем, что вам не обязательно следовать этим соглашениям в ваших React-приложениях. Многие из этих соглашений существуют по историческим причинам и могут быть изменены со временем.

Top-Level Folders

После клонирования репозитория React вы увидите папки верхнего уровня:

  • packages содержит метаданные (такие как package.json) и исходный код (подпапка src) для каждого пакета из репозитория React. Большая часть работы с кодом происходит в подпапках src внутри каждого пакета.
  • fixtures содержит несколько небольших React-приложений для контрибьютеров.
  • build содержит скомпилированную версию React. Этого каталога нет в репозитории, но он появится после того, как вы соберёте проект в первый раз.

Документация расположена в отдельном репозитории.

Существуют ещё несколько вспомогательных верхнеуровневых папок. Но вы, вероятно, не столкнётесь с ними при изменении кода.

Тесты

У нас нет отдельной верхнеуровневой папки с юнит тестами. Вместо этого, мы помещаем их в папку __tests__, расположенную рядом с тестируемыми файлами.

Например, тесты для setInnerHTML.js расположены в __tests__/setInnerHTML-test.js.

Предупреждения и инварианты

Предупреждения в React выводятся через console.error.

if (__DEV__) {
  console.error('Что-то не так.');
}

Предупреждения включены только в режиме разработки и полностью вырезаны из продакшена. Если вам необходимо запретить выполнение какого-либо кода, тогда используйте модуль invariant:

var invariant = require('invariant');

invariant(
  2 + 2 === 4,
  'Ты не пройдёшь!'
);

Если условие внутри invariant равно false, тогда выбрасывается исключение.

«Инварианты» — это всего лишь способ сказать: «Условие всегда истинно». Вы можете думать о них как об утверждениях (assertion).

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

Режимы разработки и продакшена

Вы можете использовать псевдоглобальную переменную __DEV__, чтобы блок кода присутствовал только в режиме разработки.

На этапе компиляции добавится проверка process.env.NODE_ENV !== 'production', определяющая, должен ли данный блок кода присутствовать в сборке CommonJS.

Выражение будет равно true в неминифицированной сборке, а в минифицированной будет вырезан весь блок вместе с if.

if (__DEV__) {
  // Этот код будет выполнен только в режиме разработки.
}

Flow

Недавно мы представили статический анализатор Flow. Файлы, помеченные аннотацией @flow в заголовке после лицензии, будут подвергнуты проверке типов.

Мы принимаем пулл реквесты на дополнение аннотаций Flow в уже существующий код. Вот пример кода:

ReactRef.detachRefs = function(
  instance: ReactInstance,
  element: ReactElement | string | number | null | false,
): void {
  // ...
}

Новый код, если это возможно, должен использовать Flow. Вы можете выполнить yarn flow, чтобы произвести проверку типов.

Множество пакетов

React является монолитным репозиторием. Он содержит множество отдельных пакетов, чтобы изменения были согласованными, а проблемы решались в одном месте.

Ядро React

Ядро включает в себя весь верхнеуровневый API React, например:

  • React.createElement()
  • React.Component
  • React.Children

Ядро включает в себя только API необходимый для объявления компонентов. Оно не включает алгоритм согласования или какой-либо платформо-специфический код. Этот код находится в компонентах React DOM и React Native.

Код ядра расположен в папке packages/react. Он доступен в npm в виде пакета react. Соответствующая сборка для браузера экспортирует глобальную переменную React и называется react.js.

Рендереры

Изначально React создавался для DOM, но позже был адаптирован к другим платформам, таким как React Native. В этом разделе мы расскажем об используемых рендерерах.

Рендереры превращают React-дерево в платформо-специфический код.

Они расположены в каталоге packages/:

  • React DOM Renderer рендерит React-компоненты в DOM. Он реализует ReactDOM API и доступен как пакет react-dom из npm репозитория. Можно подключать как отдельный бандл react-dom.js, экспортирующий глобальную переменную ReactDOM.
  • React Native Renderer рендерит React-компоненты в нативные представления. Используется внутри React Native.
  • React Test Renderer рендерит React-компоненты в JSON-дерево. Используется при тестировании снимками через фреймворк Jest и доступен как пакет react-test-renderer в npm.

Мы начали поддерживать единственный неофициальный рендерер react-art, который раньше находился в отдельном GitHub-репозитории.

Примечание:

Технически react-native-renderer — это очень тонкий слой, который учит React взаимодействовать с React Native. Платформо-специфический код, управляющий нативными представлениями, расположен в репозитории React Native вместе с его компонентами.

Согласователи

Даже очень непохожим рендерерам, таким как React DOM и React Native, необходимо разделять много логики. В частности, реализации алгоритма согласования должны быть настолько похожими, чтобы декларативный рендеринг, пользовательские компоненты, состояние, методы жизненного цикла и рефы работали одинаково между различными платформами.

В качестве решения, различные рендереры имеют между собой общий код. В React мы называем эту часть «согласователь». Когда запланировано такое обновление, как setState(), согласователь вызывает render() в дереве компонентов и монтирует, обновляет либо размонтирует их.

Согласователи не являются отдельнымы пакетами, потому что не имеют открытого API. Вместо этого они используются исключительно такими рендерерами как React DOM и React Native.

Согласователь Stack

Согласователь «Stack» — это реализация, которая использовалась в React 15 и более ранних версиях. Мы перестали его использовать, но задокументировали в следующией главе.

Согласователь Fiber

В согласователе «Fiber» мы пытаемся исправить давно существующие ошибки и решить проблемы, появившиеся в согласователе Stack.

Его основными целями являются:

  • Разделение прерываемых задач на подзадачи.
  • Дать задачам приоритеты, иметь возможность перемещать их и переиспользовать.
  • Иметь возможность перемещаться вперёд и назад между родителями и детьми в разметке React.
  • Иметь возможность возвращать множество элементов из метода render().
  • Улучшенная обработка ошибок.

Вы можете узнать больше об архитектуре React Fiber здесь и здесь. Несмотря на то, что он включён в React 16, асинхронная функциональность по умолчанию не включена.

Исходный код расположен в папке packages/react-reconciler.

Система событий

Для большей кроссбраузерной совместимости в React реализован слой, инкапсулирующий работу с нативными событиями. Его исходный код расположен в каталоге packages/react-dom/src/events.

Что дальше?

В следующей главе вы познакомитесь с реализацией согласователя (версий React 15 и ранее) более подробно. Для нового согласователя мы ещё не писали документацию.