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](https://habrastorage.org/getpro/habr/upload_files/370/d61/20d/370d6120d6f63c7112e6eb5fb5324930.png)
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](https://habrastorage.org/getpro/habr/upload_files/830/5b1/137/8305b1137eabeb0db174bed857d14254.png)
Воспользуемся следующей командой для сборки:
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 про точный объектный тип](https://habrastorage.org/getpro/habr/upload_files/1e2/d70/01f/1e2d7001f6b5bd581500f705ac45e787.png)
Если вы хотите принимать активное участие в разработке 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 года ничего принципиально не поменялось.
Отладка сверху вниз
Собрали реакт, открыли, например, фикстуру fixtures/packaging/babel-standalone/dev.html
в браузере, и начинаем отладку.
Отладкой сверху вниз я условно называю нахождение в исходном коде функции render из react-dom, простановки в ней брейкпоинта и идти вниз по функциям, разбираясь шаг за шагом что происходит, при этом не видя всей картины.
![Функция render библиотеки react-dom Функция render библиотеки react-dom](https://habrastorage.org/getpro/habr/upload_files/fa7/ab6/cee/fa7ab6cee6138ea87541f8215f725e3c.png)
Отладка снизу вверх
Есть более интересный вариант разбирания цепочки вызовов, чем идти шаг за шагом вниз. Условно назвал этот метод отладкой внизу вверх.
На вкладке Performance в Chrome нажимаем Start profiling and reload page
, ждем пару секунд и останавливаем работу профайлера.
![Профайлинг приложения, используещего babel.js через тег script Профайлинг приложения, используещего babel.js через тег script](https://habrastorage.org/getpro/habr/upload_files/e44/df3/7f4/e44df37f4b48155789333841fafee379.png)
Среди кучи вызовов babel'я, видим небольшое дерево работы react.js, начинающееся с функции render.
Находим из интересующего поддерева самую нижнюю функцию, например для commitRoot, в данном случае, это функция insertOrAppendPlacementNodeIntoContainer
, нажимаем на нее:
![Нижняя функция в работе процедуры commmit библиотеки react Нижняя функция в работе процедуры commmit библиотеки react](https://habrastorage.org/getpro/habr/upload_files/254/4ab/56b/2544ab56b66b4a0281013afa1df53329.png)
В блоке Summary видим ссылку на функцию, кликаем, попадаем на вкладку Source
![Функция из react-dom библиотеки Функция из react-dom библиотеки](https://habrastorage.org/getpro/habr/upload_files/17b/213/597/17b213597b2f60df3a873c5fed96770d.png)
Ставим брейкпоинт где-нибудь внутри, обновляем страницу. Это дает стек вызовов, по которому мы дошли до этого места:
![Стек вызовов при комите react приложения Стек вызовов при комите react приложения](https://habrastorage.org/getpro/habr/upload_files/995/7a0/942/9957a0942dd9ad0469504730cbb1e484.png)
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 компонение](https://habrastorage.org/getpro/habr/upload_files/7b2/846/306/7b28463060ada8e2af780894ab64b055.png)
updateState просто вызывает updateReducer, который, в свою очередь, уже делает всю работу по изменению state. updateReducer не самая хорошая функция, примерно 150 строк, но при этом содержит много комментариев, облегчающих понимание работы.
Зачем вообще это нужно?
![Количество пользователей и разработчиков библиотеки react Количество пользователей и разработчиков библиотеки react](https://habrastorage.org/getpro/habr/upload_files/496/f00/c19/496f00c194e75cf1d2bf170a638bbf38.png)
Во первых, конечно, для интереса. Если вам не достаточно просто работать с библиотекой react как пользователь (программист-пользователь, который использует её в своем коде, но не разрабатывает), и интересно как это всё устроено изнутри, то можно войти в небольшое комьюнити разработчиков react.
Можно попытаться использовать такой путь для карьерного роста, из топ 10 react контрибьюторов, по количеству комитов - 8 работают в facebook. Хотя, конечно, комиты бывают разные.
![14 комитов за 120 строк и за 4000 14 комитов за 120 строк и за 4000](https://habrastorage.org/getpro/habr/upload_files/aab/cae/6bc/aabcae6bcbc5ad1e9f424a2e6dda64f5.png)
Можно просто набратся опыта как разрабатываются и развиваются топовые библиотеки - законодатели мод в сфере 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
.
Работа над первым изменением позволит ковыряться не просто так, а с гораздо большим интересом.
Надеюсь было полезно и интересно. Спасибо за внимание!
olegkusov
Хороший материал, спасибо ! Есть ещё сайт https://pomb.us/build-your-own-react/ для понимания основных концепций.