Разбираемся с оптимизацией веб-приложений с использованием VueJS и ReactJS (всё, кроме примеров, актуально и для других фреймворков)
# Lighthouse
Lighthouse - это инструмент для оценки основных показателей сайта.
Про Lighthouse есть отличная статья (opens new window), мы же постараемся разобраться, как улучшить показатели сайта.
Также затронем некоторые нюансы оптимизации и неочевидные моменты
# Общие моменты
# View Treemap
По нажатию на данную кнопку мы попадем на страницу с детальной информацией о бандле нашего приложения:

Информация о бандле на примере https://doctorpeso.mx/
# View Original Trace
Откроет вкладку производительности в devtools, в которой содержится информация о поведении страницы во время теста:

В данном пункте мы можем оценить нагрузку на CPU, падения частоты кадров. Также мы можем увить "тяжелые" задачи, смещение элементов на странице и стадии загрузки страницы (подробнее в разделе Performance)
Ниже пройдемся по каждому пункту и по способам улучшения конретных параметров:
# Performance
Один из важнейших показателей сайта, который включает:
Про каждую метрику можно почитать отдельно, кликнув на соответсвующий пункт. Мы же будем разбираться в том, как улучшить общую производительность
# Ленивая загрузка
Одно из самых эффективных решений для увелечения скорости загрузки приложения, которое позволяет уменьшить размер бандла при первой загрузке и получать необходимые куски кода только тогда, когда они используются в приложении.
Самый частый вариант использования ленивой загрузки - это отложенная загрузка страниц приложения, т.к. такой вариант, при минимальных изменениях в коде, позволяет существенно сократить размер бандла, а также позволяет загружать чанки при навигации, что положительно сказывается на пользовательском опыте. При сборке приложения, компоненты, которые были импортированы лениво, попадают не в общий бандл, а в отдельные файлы - чанки, для каждого из которых можно задать название файла или настроить его предзагрузку
// Пример использования на React:
const LazyComponent = React.lazy(() => import("../components/LazyComponent"));
// ...
// Suspense нужен, чтобы отображать различные лоадеры, пока загружается компонент/страница
return (
<Suspense fallback={<SomeLoader />}>
<LazyComponent />
</Suspense>
);
// Пример использования на React (роутинг):
const LazyComponent = React.lazy(() => import("../components/LazyComponent"));
// Можно смело вынести в отдельный файл
const routes = [{ path: '/lazy', component: LazyComponent }];
// ...
// Suspense нужен, чтобы отображать различные лоадеры, пока загружается компонент/страница
return (
<BrowserRouter>
<Routes>
{routes.map(({ path, component }) => (
<Route key={path} path={path} element={
<Suspense fallback={<SomeLoader />}>
{component}
</Suspense>
} />
))}
</Routes>
</BrowserRouter>
);
// Пример использования на Vue3:
import { defineAsyncComponent } from 'vue';
const LazyComponent = defineAsyncComponent({
loader: () => import('./components/LazyComponent.vue'),
loadingComponent: SomeLoader,
});
<!-- Пример использования на Vue3: -->
<template>
<Suspense>
<template #default>
<LazyComponent />
</template>
<template #fallback>
<SomeLoader />
</template>
</Suspense>
</template>
<script>
import { defineAsyncComponent } from 'vue';
const LazyComponent = defineAsyncComponent({
loader: () => import('./components/LazyComponent.vue'),
loadingComponent: SomeLoader,
});
</script>
Использование ленивой загрузки не ограничивается роутингом, таким же образом можно загружать содержимое модальных окон/различных меню или для отображения определенных компонентов по условию
Однако ленивую загрузку не стоит использовать для отделения содержимого, которое пользователь должен увидеть сразу
# Контент приложения
Содержимое приложение напрямую влияет на показатель LСP.
# Текстовое содержимое:
Для того, чтобы браузер мог отобразить тексты до загрузки шрифтов, необходимо добавить следующее свойство:
@font-face {
font-display: swap;
/* ... */
}
# Изображения
Для оптимизации загрузки изображений можно использовать:
- Современные форматы изображений (в частности, .webp)
- Сжатие слишком больших изображений
- Ленивую загрузку
В первом случае можно использовать любой онлайн-конвертер изображенй. .webp - это формат, специально созданный для отображения содержимого в браузерах, поэтому при простой конвертации изображение потеряет значительную часть веса.
.webp не поддерживается старыми браузерами, поэтому мы можем добавить копии изображений в классических форматах:
<picture>
<source srcset="image.avif" type="image/avif">
<source srcset="image.webp" type="image/webp">
<img src="image.jpg" alt="A description of the image."
width="300" height="200" loading="lazy" decoding="async">
</picture>
Изображения, которые всё еще слишком много весят, можно сжать (тоже онлайн), однако нужно соблюдать баланс веса и качества изображения, для некоторых изображений (например, для фона) критично хорошее качество
Ленивая загрузка подходит для сайтов, на которых содержится большое количество изображений (например, какя-то лента с изображениями). Браузер будет откладывать загрузку этих изображений, пока эти изображения находятся вне экрана пользователя.
Такой вид загрузки мало подходит для статичных сайтов с небольшим количеством изображений, т.к. может вызвать замедление загрузки картинок при загрузке страницы
Чтобы дать понять браузеру, что изображение должно загружаться лениво, нужно использовать параметр loading="lazy" (также можно использовать параметр decoding="async", для асинхронного декодирования изображения)
<img src="image.jpg" alt="A description of the image."
width="300" height="200" loading="lazy" decoding="async">
# Смещение макета
Смещение макета (CLS) - также важная метрика, т.к. при высоком показателе смещения, мы теряем довольно много очков производительности и ухудшаем пользовательский опыт
Чаще всего смещение происходит из-за того, что у изображений нет точной ширины и высоты. Так, если поместить такое изображение в начало страницы, при полной загрузке изображение, оно вызовет смещение контента вниз на высоту этого изображения.
Также смещение может вызывать несоответсвие блока с лоадером и контента при использовании ленивой загрузки. Например, если блок с лоадером имел высоту 500px, а загруженный контент - 700px, то это вызовет смещение окружаюжих блоков на 200px.

Смещение макета во вкладке Experience пункта View Original Trace. При наведении на пункт Related Node будет показан элемент, который был затронут смещением
# Что еще?
Для того, чтобы ускорить загрузку и улучшить показатели производительности, можно использовать сжатие ресурсов (использовать brotli или gzip на сервере nginx или apache)
Не совсем очевидно, но расширения браузера также могут вносить существенный вклад в понижение метрик при тестировании локально, чтобы упростить тестирование, можно добавить в package.json команду "serve": "npx serve -s build". После билда приложения, с помощью этой команды можно развернуть приложение в продакшен-окружении, чтобы не деплоить сайт после каждого изменеия. Также это позволит протестировать продакшен-сборку (на localhost'е показатели будут всегда ниже)
Также значительный прирост в метриках даст добавление на сервер политики кэширования статики (html/css/js или изображенй). Однако прирост будет только при повторной загрузке страницы, когда статика уже хранится в кэше
# Accessibility
Данный пункт включает в себя многие пункты из семантической вёрстки, а также наличия атрибутов alt у изображений, лейблов у инпутов и т.д.
Чтобы улучшить этот показатель, достаточно следовать инструкциям самого Lighthouse по улучшению (у какого элемента чего не хватает)
# Best Practices
Улучшить данную метрику можно, если заменить устаревшие API в приложении (Lighthouse также укажет, что это за API и где оно было использовано)
# SEO
Показатель уровня поисковой оптимизации сайта
Без использования SSR (opens new window), можно улучшить путем добавления мета-тегов в index.html. В некоторых случаях будет указывать на слишком маленький размер элементов для пользователей мобильной версии
# PWA
Слабо актуален для лэндингов, однако очень полезен для функциональных приложений (CRM и т.п.). Позволяет использовать сервис воркер (opens new window) для кэширование всей статики приложения, что позволяет загружать сервис почти моментально (после этого всё будет упираться в скорость ответа API). Также дает возможность обновлять приложение не после обновления страницы (можно настроить и это), а после вызова определенного события (например, по кнопке "Обновить приложение")
PWA доступен для добавления в проект через CLI (Vue cli и CRA)