На айфоне вкладка 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 по кругу, остальным —
data-srcиdata-srcset(у<picture>— и у каждого<source>). Стартовый слайд фиксирован, поэтому разметка одна и в кэше одна; - когда строка подходит к экрану и на каждой смене слайда скрипт подставляет адрес активному и соседям — по номеру слайда, а не по позиции в DOM: в бесконечном режиме swiper переставляет элементы;
- для дальнего прыжка по миниатюре: подставить адрес, дождаться загрузки (не дольше 600 мс), и только потом переключить;
- трогать только обложку. Кадры галереи внутри слайда тоже лежат с
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 на айфоне?
Пришлите адрес — посчитаю память картинок и слоёв и найду, что её съедает.
// загружаю комментарии…