На айфоне вкладка Safari перезагружалась сама — у средней товарной строки главной, при открытии меню. Ошибок в консоли нет, в Chrome на компьютере всё работает. Так Safari ведёт себя, когда странице не хватает памяти. Первая находка: слайдер в режиме смены через прозрачность загружал обложки всех товаров строки, а не только видимой.

Это третья часть цикла «Вёрстка без сюрпризов».

Почему fade грузит всё

На узких экранах слайдер товарной строки работал с эффектом fade: слайды не едут, а сменяют друг друга прозрачностью. Для этого swiper складывает все слайды стопкой в одном месте и прячет неактивные через opacity: 0.

Стопка целиком лежит в видимой области. А loading="lazy" решает, грузить картинку или нет, по положению на странице — прозрачность он не учитывает. В режиме обычной прокрутки соседние слайды стоят за краем экрана и не грузятся; в режиме fade грузятся обложки всех товаров строки.

Как мерить память

Объём декодированных картинок считается прямо на странице — ширина × высота × 4 байта на каждую загруженную картинку:

const mb = [...document.images]
  .filter((img) => img.naturalWidth > 0)
  .reduce((sum, img) => sum + img.naturalWidth * img.naturalHeight * 4, 0) / 1048576;
console.log(mb.toFixed(1) + ' МБ декодированных картинок');

Замер в эмуляции iPhone (390×844, плотность 3) у средней строки: 63,7 МБ, из них 46 МБ — обложки неактивных слайдов, по 1,24 МБ каждая. С учётом всех строк страницы это порядка 190 обложек.

Число подгруженных строк между прогонами плавает, поэтому сравнивать надо не итог по странице, а обложки на одну строку.

Лечение без пустых слайдов

Требование было жёсткое: при листании не должно быть пустых слайдов. Поэтому не «грузить по клику», а заранее держать готовыми активный и соседние:

  1. сервер печатает адрес обложки только стартовому слайду и соседям ±1 по кругу, остальным — data-src и data-srcset (у <picture> — и у каждого <source>). Стартовый слайд фиксирован, поэтому разметка одна и в кэше одна;
  2. когда строка подходит к экрану и на каждой смене слайда скрипт подставляет адрес активному и соседям — по номеру слайда, а не по позиции в DOM: в бесконечном режиме swiper переставляет элементы;
  3. для дальнего прыжка по миниатюре: подставить адрес, дождаться загрузки (не дольше 600 мс), и только потом переключить;
  4. трогать только обложку. Кадры галереи внутри слайда тоже лежат с data-src, и общая функция «покажи отложенные картинки слайда» подставила бы адрес и им — замер дал +47 МБ.
/* дальние слайды стопки: буфер слоя освобождается, на глаз ничего не меняется */
.swiper-fade .swiper-slide:not(.swiper-slide-active):not(.swiper-slide-prev):not(.swiper-slide-next) {
  visibility: hidden;
}

Последнее правило — для Safari: каждый слайд с трёхмерным сдвигом там отдельный слой со своим буфером (около 4 МБ на экране с плотностью 3). opacity: 0 буфер не освобождает, visibility: hidden — освобождает. Классы соседних слайдов swiper ставит до начала перехода, поэтому смена прозрачностью идёт между видимыми слайдами.

Результат

  • на той же строке: 63,7 → 36,4 МБ, три обложки на строку вместо всех;
  • все переходы — вперёд, назад, прыжок — начинаются с загруженной обложкой.

Падать перестало не сразу. После этой правки Safari всё ещё перезагружал вкладку при открытии меню. Картинки оказались половиной причины, а второй половиной — слои композитинга. Об этом следующая часть.

Сайт «вылетает» в Safari на айфоне?

Пришлите адрес — посчитаю память картинок и слоёв и найду, что её съедает.

· полезно?
АЧ
Алексей Чикун веб-разработчик, команда TRIOHEAD

Делаю сайты и сервисы целиком: от вёрстки до сервера. Пишу о том, что проверил на своих проектах.

Комментарии

// загружаю комментарии…

Комментарии публикуются после проверки. Ссылки и HTML не принимаются.