С 23 миллионами уникальных посетителей в месяц Cdiscount является французским лидером в области электронной коммерции, и в результате мы постоянно стремимся к достижению наилучших результатов. Причина очень проста: более высокая производительность обычно означает лучший пользовательский опыт и, следовательно, более высокий уровень удержания пользователей.
В мае 2020 года Google объявил, что будет отдавать приоритет сайтам на основе Core Web Vitals (LCP, FID и CLS). В результате производительность больше не зависит только от пользовательского опыта, но и от SEO-рейтинга.
В этой статье мы увидим, как внедрение стратегий прогрессивной гидратации помогло нам уменьшить задержку первого ввода на нашем мобильном сайте более чем на 50 %, что позволило нам достичь цели БЫСТРО по всем трем показателям.
Гидратация и ее влияние на FID
С сильной направленностью на SEO и высокой кешируемостью контента рендеринг на стороне сервера является очевидным выбором для веб-сайта электронной коммерции, такого как Cdiscount.
Несмотря на то, что рендеринг сервера позволяет пользователям быстро просматривать наш веб-сайт в своих браузерах, пакеты JavaScript все равно должны быть загружены, обработаны и выполнены, чтобы они могли взаимодействовать с ним. В этом процессе, называемом гидратация, React проверяет узлы в текущей модели DOM и увлажняет их с помощью соответствующего JavaScript, создавая так называемую виртуальную модель DOM.
Вся страница увлажняется одновременно, то есть пользователь должен дождаться, пока нижняя часть страницы увлажнится, прежде чем он сможет взаимодействовать с верхней частью страницы. Это может разочаровать пользователя, так как пользовательский интерфейс может казаться замороженным.
Итак, как именно гидратация влияет на FID? Долгие задержки первого ввода обычно возникают, когда пользователь пытается взаимодействовать со страницей, когда основной поток занят и не может сразу ответить. На странице, отображаемой на стороне сервера, процесс гидратации может занимать много места в основном потоке.
Чтобы улучшить FID, а также пользовательский опыт, наша цель — сократить и разделить работу в основном потоке на более мелкие задачи, чтобы пользователь мог начать взаимодействовать со страницей раньше, а браузер мог обрабатывать эти взаимодействия как можно быстрее. . Именно здесь в игру вступают стратегии увлажнения.
Активное увлажнение
Активная гидратация заключается в гидратации только динамических частей МОМ. Вместо одной точки входа, отвечающей за гидратацию всей страницы, существует несколько точек входа, по одной для каждого динамического элемента, которые могут быть доставлены и гидратированы независимо, оставляя остальную часть страницы статическим HTML. Это похоже на островную архитектуру.
Хотя активная гидратация в первую очередь предназначена для страниц с большим количеством статического контента, и ее непросто реализовать в существующем приложении, наша текущая архитектура позволяет нам использовать аналогичный подход.
Первым шагом было разбить каждую гидратацию на отдельную задачу с помощью удачного setTimeout
и динамического импорта с волшебным комментарием Webpack webpackMode: "eager"
. Вам может быть интересно, почему этого еще не было, ведь у каждой из трех наших частей есть своя точка входа и файл? Что ж, если соблюдены правильные условия, получается, что браузеры могут анализировать/выполнять разные скрипты в рамках одной задачи.
Затем использование IntersectionObserver
и динамического импорта позволило нам различать загрузку кода и гидратацию нижнего колонтитула прямо перед тем, как пользователь прокручивает его в поле зрения.
С помощью этих двух шагов мы смогли сократить Общее время блокировки более чем на 40 %.
Удалив нижний колонтитул из начальной загрузки нашей страницы, давайте посмотрим, как мы можем оптимизировать наш заголовок и тело.
Частичная и прогрессивная гидратация
Стратегия активной гидратации, которую вы видите выше, работает лучше всего, когда вы хотите, чтобы некоторые элементы были интерактивными. Рассмотрим противоположный сценарий: у нас в основном интерактивное приложение, но мы хотим исключить определенные элементы из этапа гидратации, поскольку они не являются интерактивными. Это называется частичной гидратацией.
В качестве альтернативы мы можем захотеть запустить гидратацию позже, например, чтобы дождаться, пока элемент не станет видимым. Или мы можем просто захотеть отложить гидратацию менее важных частей страницы. Это называется прогрессивным увлажнением.
Давайте посмотрим, как частичная и прогрессивная гидратация использовались для повышения производительности мобильного сайта Cdiscount:
Целевая страница
Благодаря простоте компонентов, в основном состоящих из изображений и ссылок, мы смогли пропустить гидратацию большинства из них. Однако нам не удалось удалить какой-либо клиентский код, поскольку только первые 10 элементов обрабатываются на стороне сервера, а остальные элементы обрабатываются на стороне клиента. Благодаря частичной гидратации мы смогли сократить время гидратации целевой страницы на целых 80 %.
Страница продукта
Страница продукта является наиболее посещаемым шаблоном и имеет самый высокий FID. Под окном просмотра расположено много контента, и оказывается, что скорость прокрутки очень низкая, что привело к тому, что мы загружали большое количество этих компонентов по запросу, динамически загружая их код. Эти оптимизации позволили намуменьшить maxFID на 40 % и размер пакета на 30 %, в то время как наши пользователи могут быстрее взаимодействовать с верхней частью страницы.
Выполнение
Реализация, которую мы используем в Cdiscount, доступна на Github:
На момент написания этой статьи эта библиотека имела более 300 тыс. установок npm в месяц и используется на нескольких веб-сайтах с высокой посещаемостью.
Увлажнять или не увлажнять
Вот в чем вопрос ;). Даже если разделение задачи гидратации на более мелкие задачи позволяет браузеру быстрее реагировать на вводимые пользователем данные, браузеру требуется несколько секунд, чтобы проверить свою очередь событий ввода и выбрать следующую задачу. Более того, используя прогрессивную гидратацию, мы пропускаем этап гидратации, а затем вызываем React.render
, что обходится дороже, чем просто гидратация компонента с остальной частью дерева. Все это может задержать время, когда элемент становится полностью интерактивным.
Чтобы устранить необходимость искать компромисс между производительностью загрузки и реагированием на ввод, Facebook (не случайно) предложил и внедрил isInputPending
API в Chromium.
Как следует из названия, isInputPending сообщает вам, есть ли ожидающие ввода. Затем разработчики могут использовать эту информацию при запуске JavaScript, чтобы решить, хотят ли они вернуться к браузеру. При правильном использовании isInputPending может полностью устранить компромисс между быстрой загрузкой и быстрым реагированием на события.
При использовании прогрессивной гидратации это позволяет нам пропустить гидратацию, чтобы позволить браузеру обрабатывать взаимодействие с пользователем, или гидратировать все за один раз, если в очереди нет взаимодействия с пользователем.
isInputPending
доступен с момента выпуска Chrome 87, и его уже можно использовать в последнем выпуске реакции-гидратации-по-запросу.
Заключение
В конце концов, реализация стратегий гидратации оказалась очень эффективной и сыграла решающую роль в повышении производительности нашего мобильного сайта. Переведя все три основных веб-сайта на FAST, Cdiscount положительно повлияла на своих пользователей и бизнес.
Мы с нетерпением ждем выпуска React 18, который позволит нам объединить потоковый рендеринг на стороне сервера с новым подходом к гидратации: выборочной гидратацией.