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
fixtures содержат небольшие приложения для отладки различных возможностей react.js
packages - основная директория с исходниками react, содержит более 35 пакетов, в которых хранится весь исходный код, основные из них:
https://reactjs.org/docs/how-to-contribute.html#development-workflow
Интересно что информация в русской и англоязычных версиях документации отличается:
Отсутствующий раздел в русскоязычной документации 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'а, но в том числе из типов. Также во многих местах содержатся объемные комментарии, описывающие не только текущее место, но и концептуально где это может использоваться и зачем.
Например по конструкции
{| описание типа |}
можно понять что она относится к объектам и найти информацию в разделе документации связанным с этим.
https://flow.org/en/docs/types/objects/
Абзац документации 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
Отладкой сверху вниз я условно называю нахождение в исходном коде функции render из react-dom, простановки в ней брейкпоинта и идти вниз по функциям, разбираясь шаг за шагом что происходит, при этом не видя всей картины.
Функция render библиотеки react-dom
На вкладке Performance в Chrome нажимаем Start profiling and reload page, ждем пару секунд и останавливаем работу профайлера.
Профайлинг приложения, используещего babel.js через тег script
Среди кучи вызовов babel'я, видим небольшое дерево работы react.js, начинающееся с функции render.
Находим из интересующего поддерева самую нижнюю функцию, например для commitRoot, в данном случае, это функция insertOrAppendPlacementNodeIntoContainer, нажимаем на нее:
Нижняя функция в работе процедуры commmit библиотеки react
В блоке Summary видим ссылку на функцию, кликаем, попадаем на вкладку Source
Функция из react-dom библиотеки
Ставим брейкпоинт где-нибудь внутри, обновляем страницу. Это дает стек вызовов, по которому мы дошли до этого места:
Стек вызовов при комите 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 компонение
updateState просто вызывает updateReducer, который, в свою очередь, уже делает всю работу по изменению state. updateReducer не самая хорошая функция, примерно 150 строк, но при этом содержит много комментариев, облегчающих понимание работы.
Количество пользователей и разработчиков библиотеки react
Во первых, конечно, для интереса. Если вам не достаточно просто работать с библиотекой react как пользователь (программист-пользователь, который использует её в своем коде, но не разрабатывает), и интересно как это всё устроено изнутри, то можно войти в небольшое комьюнити разработчиков react.
Можно попытаться использовать такой путь для карьерного роста, из топ 10 react контрибьюторов, по количеству комитов - 8 работают в facebook. Хотя, конечно, комиты бывают разные.
14 комитов за 120 строк и за 4000
Можно просто набратся опыта как разрабатываются и развиваются топовые библиотеки - законодатели мод в сфере front-end разработки, но для этого, конечно, надо участвовать в обсуждениях, глубоко вникать в ключевые изменения и активно контрибьютить.
Обычно после каких-либо публикаций, энтузиасты разбираются в этих возможностях и делают доклады, например по теме concurrent в react уже можно найти несколько видео на просторах youtube.
Плейлист с анализом исходников react 17 и рисованием диаграмм как это работает от JSer (на английском): https://www.youtube.com/playlist?list=PLvx8w9g4qv_p-OS-XdbB3Ux_6DMXhAJC3
Работа над первым изменением позволит ковыряться не просто так, а с гораздо большим интересом.
Источник статьи: https://habr.com/ru/post/569564/
В этой статье, я стараюсь дать начальное понимание как разобраться в исходниках, отлаживать react и как сделать первые шаги в становлении контрибьютором.
Надеюсь это введение станет для кого-то отправной точкой и рассеет страхи, связанные с тем что разобраться в исходниках популярной библиотеки слишком сложно и "это не для меня".
С чего начать?
Конечно для того чтобы исследовать реакт необходимо быть пользователем этой библиотеки или хотя бы прочитать документацию.Помимо документации следует посмотреть доклады, почитать статьи с более детальным описанием как это работает изнутри. Например по react fiber можно найти много докладов и обсуждений как это устроено.
Следующее грядущее крупное изменение в react - concurrent режим, что можно найти по нему?
Доклад Дэна Абрамова на JSConf Iceland 2018, где concurrent режим еще назывался asynchronous (за 3-4 года до реализации возможности):
Несколько скрытых статей в документации React, на которые нет ссылки из меню: https://reactjs.org/docs/concurrent-mode-intro.html (внутри ссылки на остальные статьи)
Когда изучили чего ожидать внутри репозитория, можно переходить к исследованию исходного кода.
Структура репозитория
Открывая папку с исходниками 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
Интересно что информация в русской и англоязычных версиях документации отличается:
Воспользуемся следующей командой для сборки:
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/
Если вы хотите принимать активное участие в разработке 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, простановки в ней брейкпоинта и идти вниз по функциям, разбираясь шаг за шагом что происходит, при этом не видя всей картины.
Отладка снизу вверх
Есть более интересный вариант разбирания цепочки вызовов, чем идти шаг за шагом вниз. Условно назвал этот метод отладкой внизу вверх.На вкладке Performance в Chrome нажимаем Start profiling and reload page, ждем пару секунд и останавливаем работу профайлера.
Среди кучи вызовов babel'я, видим небольшое дерево работы react.js, начинающееся с функции render.
Находим из интересующего поддерева самую нижнюю функцию, например для commitRoot, в данном случае, это функция insertOrAppendPlacementNodeIntoContainer, нажимаем на нее:
В блоке Summary видим ссылку на функцию, кликаем, попадаем на вкладку Source
Ставим брейкпоинт где-нибудь внутри, обновляем страницу. Это дает стек вызовов, по которому мы дошли до этого места:
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 сбором информации и смотрим что получилось:
updateState просто вызывает updateReducer, который, в свою очередь, уже делает всю работу по изменению state. updateReducer не самая хорошая функция, примерно 150 строк, но при этом содержит много комментариев, облегчающих понимание работы.
Зачем вообще это нужно?
Во первых, конечно, для интереса. Если вам не достаточно просто работать с библиотекой react как пользователь (программист-пользователь, который использует её в своем коде, но не разрабатывает), и интересно как это всё устроено изнутри, то можно войти в небольшое комьюнити разработчиков react.
Можно попытаться использовать такой путь для карьерного роста, из топ 10 react контрибьюторов, по количеству комитов - 8 работают в facebook. Хотя, конечно, комиты бывают разные.
Можно просто набратся опыта как разрабатываются и развиваются топовые библиотеки - законодатели мод в сфере 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/