Почему React работает быстро, как устроен виртуальный DOM и процесс обновления?
Что проверяют
Понимаете ли вы, что именно даёт виртуальный DOM, или пересказываете рекламную формулу «он быстрее реального». Одновременно смотрят, знаете ли этапы обновления и правила сравнения деревьев, потому что из них растут все практические оптимизации.
Как отвечать
Честная рамка звучит так: React не быстрее ручных точечных правок DOM, аккуратный императивный код всегда выиграет. React даёт предсказуемость: интерфейс описан как функция от состояния, а сведение описания к настоящим узлам берёт на себя библиотека и делает это пакетно.
Виртуальный DOM это дерево обычных объектов, описывающих узлы. Обновление идёт в две фазы:
- render: вызываются функции компонентов, строится новое дерево и сравнивается с прошлым;
- commit: накопленные изменения применяются к настоящему DOM за один проход.
Сравнение эвристическое, полноценное сопоставление деревьев стоило бы кубического времени. Правила простые: другой тип элемента означает снос поддерева и монтирование заново, тот же тип означает обновление пропсов, элементы списка сопоставляются по key. Поверх этого Fiber умеет прерывать фазу render и отдавать браузеру кадр, отсюда startTransition и Suspense.
Где спотыкаются
Утверждают, что виртуальный DOM быстрее настоящего. Это сравнение разных вещей: в конце правится тот же самый DOM, а сравнение деревьев само стоит времени. Выигрыш в пакетности и в отсутствии ручной синхронизации.
Второе: ставят key по индексу массива в списке, который сортируется или из которого удаляют элементы. React переиспользует не те узлы, и введённый текст вместе с состоянием переезжает в чужую строку.
Третье: путают повторный рендер и изменение DOM. Компонент может отрендериться, дать такое же дерево и не тронуть ни одного узла. Поэтому React.memo вешают по замерам в Profiler, а не везде подряд, тем более что он бесполезен, когда в пропсах каждый раз новый объект или функция.