Перейти к содержимому

15. Mobile Performance

Более 60% веб-трафика — мобильный. Google использует Mobile-First индексацию. Медленный мобильный = потеря трафика и позиций.

CPU: Мобильный в 4-10x медленнее desktop
RAM: Меньше, сильнее ограничена
Сеть: 4G = 10-30 Mbps, но с высокой задержкой
3G = 1-3 Mbps (ещё используется в регионах!)
Батарея: Тяжёлый JS разряжает батарею
Экран: Разные DPR (1x, 2x, 3x)
<!-- Обязательно! Без этого Google понизит сайт -->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Запрет масштабирования (осторожно — плохо для a11y) -->
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<!-- Для PWA -->
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes">
<!-- DPR-aware изображения -->
<img
src="photo-800.jpg"
srcset="
photo-400.jpg 1x,
photo-800.jpg 2x,
photo-1200.jpg 3x
"
alt="Photo"
>
<!-- Адаптивные по ширине -->
<img
src="photo-800.jpg"
srcset="photo-400.jpg 400w, photo-800.jpg 800w, photo-1600.jpg 1600w"
sizes="(max-width: 600px) 100vw, (max-width: 1200px) 50vw, 800px"
alt="Photo"
>
/* ✅ Минимальный размер для касания — 44x44px (Apple HIG) */
.button {
min-width: 44px;
min-height: 44px;
padding: 12px 20px;
}
/* ✅ Расстояние между кнопками */
.button + .button {
margin-left: 8px;
}
/* ✅ Используйте padding вместо увеличения видимой области */
.nav-link {
padding: 12px 16px; /* касание работает на всю padding зону */
}
/* ✅ Убираем задержку 300ms на клик (устарело, но ещё встречается) */
* {
touch-action: manipulation;
}
/* ✅ Убираем highlight при касании */
* {
-webkit-tap-highlight-color: transparent;
}
/* ✅ Плавная прокрутка */
.scroll-container {
-webkit-overflow-scrolling: touch;
overflow-y: scroll;
}
/* Минимальный размер шрифта — 16px (iPhone зумит если меньше!) */
input, textarea, select {
font-size: 16px; /* предотвращает автозум на iOS */
}
body {
font-size: 16px;
line-height: 1.5;
}
/* ✅ Fluid typography */
h1 {
font-size: clamp(1.5rem, 5vw, 3rem);
}
p {
font-size: clamp(1rem, 2.5vw, 1.25rem);
}
// Определяем тип соединения
const connection = navigator.connection || navigator.mozConnection;
if (connection) {
const { effectiveType, saveData } = connection;
if (saveData || effectiveType === 'slow-2g' || effectiveType === '2g') {
// Режим экономии: текст вместо изображений
document.body.classList.add('save-data');
}
if (effectiveType === '4g') {
// Хорошее соединение — можно грузить полный качество
preloadHighQualityImages();
}
}
// Адаптивная загрузка видео
function loadVideo(src) {
const video = document.createElement('video');
if (connection?.effectiveType === '4g') {
video.src = src.replace('.mp4', '-hd.mp4');
} else {
video.src = src.replace('.mp4', '-sd.mp4');
video.preload = 'none'; // не предзагружаем на медленном
}
return video;
}
/* CSS для Save-Data режима */
@media (prefers-reduced-data: reduce) {
.hero-video { display: none; }
.hero-image { display: block; } /* показываем изображение вместо видео */
.fancy-animation { animation: none !important; }
}
.save-data .background-image {
background-image: none !important; /* убираем тяжёлые фоны */
}
JavaScript: < 150KB (gzipped)
CSS: < 30KB (gzipped)
Images (above fold): < 300KB
Total Page Weight: < 1MB
Time to Interactive: < 3.8s (3G Fast)
// Webpack — предупреждения при превышении бюджета
module.exports = {
performance: {
maxAssetSize: 250000, // 250KB
maxEntrypointSize: 400000, // 400KB
hints: 'error',
},
};
// Определяем низкопроизводительные устройства
function isLowEndDevice() {
const memory = navigator.deviceMemory; // GB RAM
const cores = navigator.hardwareConcurrency;
return (memory && memory < 2) || (cores && cores < 4);
}
// Адаптируем под возможности устройства
if (isLowEndDevice()) {
// Отключаем тяжёлые анимации
document.body.classList.add('reduce-motion');
// Уменьшаем количество элементов на странице
maxItemsPerPage = 10; // вместо 20
}
/* Уважаем настройки пользователя */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}