Как разобраться в исходном коде React

Kate

Administrator
Команда форума
React самая популярная библиотека для построения пользовательских интерфейсов. Мы знаем про виртуальное дерево, движок fiber, процедуру reconcilation, хуки и другие прекрасные возможности react. Но как это работает на уровне исходного кода? Ответить на этот вопрос смогут очень небольшое количество программистов.

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

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

С чего начать?​

Конечно для того чтобы исследовать реакт необходимо быть пользователем этой библиотеки или хотя бы прочитать документацию.

Помимо документации следует посмотреть доклады, почитать статьи с более детальным описанием как это работает изнутри. Например по react fiber можно найти много докладов и обсуждений как это устроено.

Следующее грядущее крупное изменение в react - concurrent режим, что можно найти по нему?

Доклад Дэна Абрамова на JSConf Iceland 2018, где concurrent режим еще назывался asynchronous (за 3-4 года до реализации возможности):

Несколько скрытых статей в документации React, на которые нет ссылки из меню: https://reactjs.org/docs/concurrent-mode-intro.html (внутри ссылки на остальные статьи)

Когда изучили чего ожидать внутри репозитория, можно переходить к исследованию исходного кода.

Структура репозитория​

Открывая папку с исходниками react, мы увидим довольно простую структуру:

Корень репозитория react
Корень репозитория react
fixtures содержат небольшие приложения для отладки различных возможностей react.js

packages - основная директория с исходниками react, содержит более 35 пакетов, в которых хранится весь исходный код, основные из них:

  • react - содержит весь код для создания компонентов
  • react-reconciler - пакет для сравнения виртуальных деревьев react
  • react-dom и react-native-renderer библиотеки для рендера в различных средах, где может работать react
  • react-devtools* - несколько пакетов для отладки react приложений на более высоком уровне, чем просто javascript код

Сборка react​

В разделе contribution на сайт reactjs.org можно найти инструкцию как запустить react в режиме разработчика, как на существующем проекте, так и используя фикстуры, хранящиеся в репозитории react:

https://reactjs.org/docs/how-to-contribute.html#development-workflow

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

Отсутствующий раздел в русскоязычной документации react
Отсутствующий раздел в русскоязычной документации react
Воспользуемся следующей командой для сборки:

yarn build react/index,react-dom/index --type=UMD
И откроем файл:

fixtures/packaging/babel-standalone/dev.html
Для поддержки преобразований jsx используется самый простой путь - подключается babel через тэг script:

<html>
<body>
<script src="../../../build/node_modules/react/umd/react.development.js"></script>
<script src="../../../build/node_modules/react-dom/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/babel-standalone@6/babel.js"></script>
<div id="container"></div>
<script type="text/babel">
ReactDOM.render(
<h1>Hello World!</h1>,
document.getElementById('container')
);
</script>
</body>
</html>

Исходный код и отладка​

Перейдем непосредственно к отладке и разбору исходного кода.

Первый и вариант, который никогда не стоит забрасывать - хождение по исходному коду. React использует библиотеку для строгой типизации - flow, поэтому из ключевых участков кода можно почерпнуть информацию не только из javascript'а, но в том числе из типов. Также во многих местах содержатся объемные комментарии, описывающие не только текущее место, но и концептуально где это может использоваться и зачем.

Пара слов про flow​

Если вы просто разбираетесь как работает react, то глубоких знаний flow не нужно, достаточно просто понимать что за двоеточием тип, а дальше гуглить по ситуации.

Например по конструкции

{| описание типа |}
можно понять что она относится к объектам и найти информацию в разделе документации связанным с этим.

https://flow.org/en/docs/types/objects/

Абзац документации flow про точный объектный тип
Абзац документации flow про точный объектный тип
Если вы хотите принимать активное участие в разработке react, то без использования flow.js не обойтись. К сожалению flow стал довольно закрытым проектом, который использует, в основном, facebook для своих целей, об этом довольно завуалировано написал Vladan Djeric в описании куда движется flow:

https://medium.com/flow-type/clarity-on-flows-direction-and-open-source-engagement-e721a4eb4d8b

По flow.js есть хороший курс на youtube от Ильи Климова, который был заброшен, из-за смены вектора развития библиотеки. Последнее видео - ноябрь 2018, но во flow за более чем 2 года ничего принципиально не поменялось.


Before you continue to YouTube
consent.youtube.com

Отладка сверху вниз​

Собрали реакт, открыли, например, фикстуру fixtures/packaging/babel-standalone/dev.html в браузере, и начинаем отладку.

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

Функция render библиотеки react-dom
Функция render библиотеки react-dom

Отладка снизу вверх​

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

На вкладке Performance в Chrome нажимаем Start profiling and reload page, ждем пару секунд и останавливаем работу профайлера.

Профайлинг приложения, используещего babel.js через тег script
Профайлинг приложения, используещего babel.js через тег script
Среди кучи вызовов babel'я, видим небольшое дерево работы react.js, начинающееся с функции render.

Находим из интересующего поддерева самую нижнюю функцию, например для commitRoot, в данном случае, это функция insertOrAppendPlacementNodeIntoContainer, нажимаем на нее:

Нижняя функция в работе процедуры commmit библиотеки react
Нижняя функция в работе процедуры commmit библиотеки react
В блоке Summary видим ссылку на функцию, кликаем, попадаем на вкладку Source

Функция из react-dom библиотеки
Функция из react-dom библиотеки
Ставим брейкпоинт где-нибудь внутри, обновляем страницу. Это дает стек вызовов, по которому мы дошли до этого места:

Стек вызовов при комите react приложения
Стек вызовов при комите react приложения
legacy функция связана с тем, что уже написано много кода для react 18, и часть функций становятся устаревшими.

По такому стеку мы сразу видим что было вызвано, можем пройтись по функциям, и исследовать именно то что нужно. Выглядит не так страшно как могло казаться, а когда начинаешь разбираться функция за функцией, то почти везде код написан качественно, функции небольшие по размеру, легко читаются. И если есть понимание как работает fiber, полученное из статей и докладов, то понять что происходит не такая уж сложная задача.

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

Например создаем такой код:

<script type="text/babel">
const App = () => {
const [count, setCount] = React.useState(0);

return (
<div>
<h1>Count: {count}</h1>
<button onClick={() => setCount(count + 1)}>Increase</button>
<button onClick={() => setCount(count - 1)}>Decrease</button>
</div>
);
}

ReactDOM.render(
<App />,
document.getElementById('container')
);
</script>
Нажимаем Increase с запущеным Performance сбором информации и смотрим что получилось:

Performance при изменении стейта в react компонение
Performance при изменении стейта в react компонение
updateState просто вызывает updateReducer, который, в свою очередь, уже делает всю работу по изменению state. updateReducer не самая хорошая функция, примерно 150 строк, но при этом содержит много комментариев, облегчающих понимание работы.

Зачем вообще это нужно?​

Количество пользователей и разработчиков библиотеки react
Количество пользователей и разработчиков библиотеки react
Во первых, конечно, для интереса. Если вам не достаточно просто работать с библиотекой react как пользователь (программист-пользователь, который использует её в своем коде, но не разрабатывает), и интересно как это всё устроено изнутри, то можно войти в небольшое комьюнити разработчиков react.

Можно попытаться использовать такой путь для карьерного роста, из топ 10 react контрибьюторов, по количеству комитов - 8 работают в facebook. Хотя, конечно, комиты бывают разные.

14 комитов за 120 строк и за 4000
14 комитов за 120 строк и за 4000
Можно просто набратся опыта как разрабатываются и развиваются топовые библиотеки - законодатели мод в сфере front-end разработки, но для этого, конечно, надо участвовать в обсуждениях, глубоко вникать в ключевые изменения и активно контрибьютить.

Куда копать дальше и источники информации​

Конечно же основной источник информации - документация, после нее идут выступления и статьи основных разработчиков react, обычно это Dan Abramov, но также можно найти выступления и статьи от других контрибьюторов.

Обычно после каких-либо публикаций, энтузиасты разбираются в этих возможностях и делают доклады, например по теме concurrent в react уже можно найти несколько видео на просторах youtube.

Плейлист с анализом исходников react 17 и рисованием диаграмм как это работает от JSer (на английском): https://www.youtube.com/playlist?list=PLvx8w9g4qv_p-OS-XdbB3Ux_6DMXhAJC3

Просто исследовать - скучно​

Просто разбираться как что-то работает утомительно, и я советую как можно раньше перейти к активной фазе - написанию кода, для первых комитов специально создан тег в github - good first issue.

Работа над первым изменением позволит ковыряться не просто так, а с гораздо большим интересом.

Источник статьи: https://habr.com/ru/post/569564/
 
Сверху