Часть 1. Мозг системы: реактивность и планировщик
Собрался написать серию небольших постов про CRP (Critical Rendering Path) и Vue. В первую очередь для себя, но думаю и многим может быть полезно)
CRP отвечает за первую отрисовку, но в современных SPA пользователь взаимодействует с приложением долго. Поэтому мы посмотрим на Runtime Update Path — путь, который проходит приложение от изменения данных до обновления пикселей на экране.
🧩 На дефолтном примере ресторана:
В ролях: официант (Vue), повар (браузер), гость (пользователь приложения).
Гость хочет сделать заказ, но после каждого названного блюда официант бежит на кухню передавать информацию повару. Странный официант… Хотя повар максимально быстро узнает, что ему готовить, но пока официант будет бегать - гость уже начнет получать урон от голода. Гораздо эффективнее выслушать заказ гостя целиком и после один раз сходить, оповестить об этом повара.
Для ресторана это кажется привычным, стандартным поведением, но для браузера не совсем.
⚙️ Под капотом: Vue 3 использует Proxy для перехвата чтения и записи свойств. При изменении данных срабатывает триггер. Но Vue не обновляет DOM мгновенно при первом изменении, вместо этого включается scheduler (планировщик). Он собирает все изменения в очередь и обрабатывает их в одной микрозадаче. Как официант здорового человека - собирает заказ целиком и после несет на кухню.
После этого: формируются новые VNode для динамических частей -> сравниваются с текущими VNode (diffing) -> на основе разницы формируется минимальный набор патчей -> эти патчи применяются к DOM. Но об этом подробнее в следующих постах.
В итоге, браузеру не нужно делать десятки дорогих reflow/repaint. Vue группирует (батчит) обновления. Если вы меняете 10 переменных в цикле - Vue сделает один рендер.
💡 Совет: Меняйте состояние пачками в одном тике event loop. Если, например, при поочередном изменении связанных данных встретится await, то вы разобьете батч и спровоцируете лишние перерисовки.