Новий LCP hack, або Як «розвести» Google на дві секунди

💡 Усі статті, обговорення, новини про Front-end — в одному місці. Приєднуйтесь до Front-end спільноти!

Усім привіт, я Айвазян Армен, Front-end Engineer в компанії АМО. Я присвячую багато часу Core Web Vitals показникам, оскільки один з наших проєктів — це великий медіасайт, благополуччя якого залежить зокрема від «Google циферок». Я думав, що мене вже не можна здивувати нічим, що повʼязано з Core Web Vitals, але в певний момент ми помітили, що маємо серйозну негативну динаміку показника LCP. І це за умови, що ніяких із цим повʼязаних змін не проводили; більше того, у нас реалізований LCP hack. Але показник падав у «червону зону» місяць за місяцем.

У цій статті я хочу розказати, куди завело мене детективне розслідування і яким був фінал (обіцяю неочікуваний поворот сюжету 🙂). Також я описую практичний гайд зі створення нового LCP hack. Можливо, ця стаття стане в пригоді людям, які зіштовхнуться з такою ж «LCP-проблемою» (або просто тим, хто любить гостросюжетні історії, де все найцікавіше ховається в деталях).

Дисклеймер: я розумію, що обман Google для підвищення показнику «балів перформансу» (і користувацький досвід у такій ситуації) — це доволі холіварна тема, яка заслуговує, як мінімум, окремої статті, але тут я хочу лише поділитися способом покращення LCP для людей у яких, як і в мене, є необхідність це зробити. А чи використовувати це, чи ні — дуже залежить від контексту проєкту і багатьох інших факторів.

Підґрунтя. Що таке LCP?

Простими словами, Largest Contentful Paint (LCP) — це час, який минає від початку першого завантаження сторінки до завершення рендеру найбільшого елементу, що знаходиться в області перегляду (viewport) користувача.

LCP — це спосіб від Google дізнатися, як довго рендериться основний контент вашої сторінки. Логіка така: якщо найбільший видимий елемент на сторінці показаний юзеру, це означає, що основний контент завантажився (погоджуюсь, звучить доволі суперечливо, але кращого методу оцінки Google поки не придумав). До речі, недосконалість такого підходу доводить існування такого собі LCP hack (про який поговоримо далі).

У часи, коли динозаври ходили Землею, дехто намагався використовувати FMP та Speed Index для заміру чогось подібного, але це було важко та неточно. (Не варто плутати з дуже схожим FCP (який показує час рендеру будь-якого першого видимого елементу, яким може бути і звичайний лоадер).

Шкала «Гарного — поганого LCP».

Цікавий факт: найбільший видимий елемент може змінюватися в міру завантаження сторінки. Як тільки завантажується черговий фрейм, браузер відправляє запис largest-contentful-paint, який відстежує найбільший елемент. І якщо він більший за поточний — заміняє його. Відстежування завершується після першої взаємодії (прокрутка, клік і подібне) користувача зі сторінкою.

Ґрунт. Що таке LCP hack?

LCP hack — це старенький спосіб «обдурити» Google, відрендивши на сторінці легкий, невидимий для юзера, але дуже великий елемент (назвемо його фейком). Таким способом у largest-contentful-paint потрапляє фейк, а коли починається рендер реально найбільшого елементу, він під час порівняння однаково не потрапляє в largest-contentful-paint, тому Google стежить лише за фейком і формує показник LCP на його основі.

Є пару варіантів реалізації. Розкажу про найпопулярніший. Беремо тег img, в style задаємо щось на кшталт:

pointer-events: none;
position: absolute;
top: 0;
left: 0;
width: 99vw;
height: 99vh;
max-width: 99vw;
max-height: 99vh;

А в src якийсь прозорий base64. Задаємо дуже велике значення для атрибутів width та height і отримуємо:

<img 
width="2000" 
height="2000" style="pointer-events:none;position:absolute;top:0;left:0;width:99vw;height:99vh;max-width:99vw;max-height:99vh" 
src="data:image/svg+xml;base64,..."
role="presentation"
alt="some-alt"
>

Щоб протестувати наш hack, скористаємося найпростішим і найшвидшим способом вимірювання показників Core Web Vitals, а саме Lighthouse вкладкою в Chrome DevTools (і перед тим, як багато хто з вас справедливо почне писати мінуси такого заміру і про локальну машину, зауважу: цей спосіб я використовую лише для простої демонстрації результату до / після, де всі ці деталі можна опустити). Проганяємо піддослідну сторінку без hack, спускаємося до вкладки LCP та дивимося на результат:

Очікувано бачимо найбільший елемент на першому viewport. Тепер додамо наш hack за ідентичних вхідних даних:

Супер, спрацювало! І якщо ми порівняємо вагу цих двох елементів, різниця буде просто колосальною (105KB проти 300B). Але Google однаково думає, що найбільший елемент — це наш hack. Ідеальний злочин 🙂. Як-то кажуть, все геніальне — просто!

Але як і будь-яка хороша історія, ця також мала свій фінал. Й одного дня ми побачили негативну динаміку з LCP на наших сайтах...

Основний квест

Завʼязка: мені надсилають тікет з проблемою того, що в нас починає падати LCP-показник без видимої причини.

Дисклеймер: хто не знає та кому цікаво, як всі ці графіки будувати, де брати дані та інше — ласкаво прошу в коментарі, надам усю інформацію.

Вивантажуємо звіт з LCP нашого сайту за допомогою lookerstudio:

Дані, які можуть викликати серйозну депресію в Front-end спеціаліста, який стежить за Core Web Vitals показниками сайту.

Ось із цього скрина й розпочалася моя пригода.

Але ми не здаємося. Подивімось на більш детальні дані та побудуємо графік на основі page speed

Ще більш разючий графік, на якому бачимо, що біда сталась на початку квітня. Це вже якась інформація, нумо розбиратися далі. Перше припущення, яке напрошується відразу: щось не так з hack. Це можна легко перевірити, подивившись вкладку Lighthouse і спустившись до LCP.

Найбільший елемент визначено як реальне зображення на сайті, отже, припущення правильне — hack більше не працює. Але що ж сталося? Адже рішення проблеми майже завжди можна знайти в причині цієї проблеми. Тому потрібно відшукати корінь.

Шукаємо релізи Chrome (бо тільки користувачі цього браузеру потрапляють у вибірку для формування Performance показнику від Google) у період, який нас цікавить. Знаходимо статтю про нову версію Chrome 112, яка випустилася якраз на початку квітня. Ні слова про LCP, але ж це не може бути просто збігом? Занурюємося глибше та знаходимо в chromium запис про зміну виміру LCP саме в 112 версії!

На основі цього заголовку та абзацу

ставимо перед собою дві цілі:

  • збільшити вагу нашої картинки-hack;
  • спробувати переконати Google, що в нашій картинці-hack є контент.

Притримуючись ключового принципу «Що простіше, то краще»:

  1. Беремо реальну svg-картинку (головне, аби вона була простенька, а конвертований варіант — не дуже важким).
  2. Робимо всі вектори прозорими.
  3. Конвертуємо в base64.
  4. Вітаю, ви прекрасні!

Тепер ми маємо повністю непомітну для користувача, але «контентну» (ніхто ж не казав, що вона не може бути прозора, хе-хе) картинку, яка важить приблизно 3КB (шляхом величезної кількості спроб і страждань вивів саме такий мінімально необхідний розмір для LCP hack).

Біжимо перевіряти:

Чудово! Працює очікувано — допомогло. Але уважний читач може зауважити: а де ж неочікуваний поворот і гострий сюжет?

Як і обіцяв, розповідаю. Поки я відкорковував пляшечку соку, щоб відсвяткувати свою чудову роботу, вирішив заглянути в лабораторний репорт pagespeed.web.dev. І побачив, що Google правильно бачить старий hack.

Так що ж це виходить? Невже проблема не в цьому? Невже локальний замір просто дав збій, і вся теорія з 112-ю версією Chrome — надумана?

Мене чекало чергове занурення в інтернет та вичитка всіх тематичних і не дуже форумів. Урешті-решт, витративши пару годин, купу своїх нервів, не знайшовши нічого толкового й майже покинувши цю справу, я випадково глянув на те, який саме User Agent використовує лабораторний скринер pagespeed.web.dev.

Дрібним текстом написано про завітну 109 версію! Це знову дає сили і мізерну надію на те, що лабораторний скринер ще просто не оновив логіку трекання LCP і бачить наш hack. Але реальні люди частково оновлюють або оновили свої Chrome і збирають польові дані вже за новою логікою (оминаючи наш hack).

Хоч це й на рівні фантастики, але все ж має сенс, і ми легко (ну майже, бо Chrome офіційно не дає скачати і не підтримує старі свої версії) можемо це перевірити за допомогою такого інструменту як Docker.

  1. Запускаємо Docker.
  2. Піднімаємо image Chrome 109.
  3. Перевіряємо User Agent на нашій «віртуальній машині».

(ідентичний з Google-скринером)

4. Заходимо на нашу статтю і проганяємо через lighthouse, і що ж ми бачимо? Еврика! Бачимо старий hack, який зчитується як найбільший елемент.

Це доводить те, що лабораторний скринер pagespeed використовує справді ту версію, яка ще не вміє оминати його, але найголовніше, що це підтверджує правильність нашої теорії, і ми можемо спасти LCP!

Результати та висновок

Випуск фіксу відбувся 10 серпня. І ось результат, який ми бачимо вже 12 серпня:

Спостерігається позитивна динаміку далі. Станом на 13 вересня показник опустився до 1,6 секунд порівняно з 3,5 секундами, що були!

Отже, новий LCP hack подіяв і має право на життя.

Висновок дуже простий і короткий: будьте відкритими до нового, підходьте креативно до вирішення задач. Не відкидайте навіть найшаленіші теорії, якщо вони мають раціональне підґрунтя. Також завжди придивляйтесь до деталей — вони важливі.

👍ПодобаєтьсяСподобалось18
До обраногоВ обраному11
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Випробував ваш варіант. З повністю прозорим зображенням на 29.07.2024 не працює. Зробив 9% прозорість елементів на svg — запрацював. Але коли запрацював, то швидкості LCP в моєму випадку не додало.

Як хак, виглядає чудово!
Та на слабкому 3G-інтернеті зображення буде завантажуватись так само кволо, що створить відповідний поведінковий паттерн.

Такі бізнес вимоги (прев’ю картинка зверху статті), нічого не поробиш)
Але ми для комфорту користувача в цьому кейсі робимо все можливо, преконекти, оптимальні типи розширень зображення, cloudfront, сервіс воркер кешування і тому подібне.
Я з вами погоджуюся, забивати на UX та фокусуватися виключно на Core Web Vitals ні в якому разі не можна, тому ми працюємо з двох сторін)

Розкажіть як збираєте статистику по метриках

Дисклеймер: інформація може здатися «занадто базовою», але розпишу щоб на всіх рівнях було зрозуміло, а там, якщо будуть якісь конкретніші запитання, залюбки розкрию тему в цьому напрямку)

Перше що потрібно зрозуміти, збирати метрики можна двома способами: польові дані, або лабораторні. Якщо коротко — польові, це статистика реальних показників юзерів, які збирає Google, а лабораторні — це показники на конкретній машині/девайсі/пристрої на якому їх перевіряють (тобто якщо ви захочете заміряти LCP на своєму сайті за допомогою свого лептопа — це буде лабораторний замір, бо він покаже результат з вашим інтернетом, з вашимми показниками ноуту, тощо, тобто локальне середовище).
Більш детально тут — web.dev/...​nd-field-data-differences

Зупинимося саме на першому (польових даних), так як це більш показові та реальні дані, а саме, як користувач бачить ваш сайт.
1. Якщо потрібні актуальні дані, прямо зараз (дані збираються за останні 28 днів) — pagespeed.web.dev (треба дивитися саме саму верхню табличку, вона про реальних юзерів, нижче йдуть лабораторні заміри Google)
2. Якщо цікавлять зміни за історичний період, з розбивкою на місяці, ось офіційний ресурс для цього developer.chrome.com/docs/crux/dashboard вставляєте в поле потрібну url і клікаєте «Go» (також, по посиланню, нижче можна почитати як зручно додати прямо в адресну стрічку цей інструмент)
3. Якщо цікавить поденна статистика, то тут треба буде трошки забруднити руки, але воно того варте, бо по цих даних можна вирахвати, практично, день, коли щось пішло не так (або так : ) ) - CrUX History API, детальніше тут developer.chrome.com/docs/crux/history-api.
Як обробляти, це вже справа кожного, можна через Looker Studio (колишні Google Data Studio), а можна реалізувати свою обробку даних, з красивими графічками та під ваші цілі (особисто ми в команді використовуємо своє рішення для обробки, що дає нам можливість фокусуватися на потрібних для нас даних)

Я так розумію, у вашого продукту більше концентрація на mobile first, тому запитаю на чому робите лабораторні заміри? Є своя локальна Mobile Testing Lab? Чи cloud версію використовуєте?

Ми якось писали статтю на цю тему, деякі деталі змінилися з того часу, але загальна концепція залишилася, тому, якщо цікаво, велком — dou.ua/forums/topic/40578 : )

Якщо ще залишаться питання, з великим задоволенням обговорю)

Дякую, цікаво почитати.
Вибчате, але я не зовсім точно сформулював питання, я питав про тестування більше в контексті UI/UX. Хоча, можливо у вашому продукті це не дуже актуально, бо не так часто зявляються нові сторінки чи компоненти.
Мені колись довелось працювати на проекті, де умовно змінювалась структура сторінки і потрібно було продивитись на десятку популярних девайсів чи воно нормально виглядає + зібрати метрики швидкодії. У нас тоді був локальний mobile device lab, де можна було все протестити на реальних пристроях. На жаль, не повністю автоматизовано, тому користуватись було не дуже зручно.

Ну не зовсім, UI дуже важлива частина нашого продукту, але так, не кожен день ми вносимо зміни в дизайн)
Якщо ви питаєте про тестування під час розробки, то все занадто банально — звичайне локальне тестування за допомогою DevTools, в крайньому випадку — на реальних девайсах (декількох типів) перевірити)
А якщо про тестування перед виливкою в продакшин, ось тут цікавіше, у нас для цього є ціла система різноманітних автомейшин тестів, які в тому числі і по UI складовій пробігються (на жаль, детально тут не розповім що там і як працює під капотом, бо у нас цим QA займаються), але система робоча і дуже корисна)

Ух, можу собі тільки уявити (як людина якій тільки інколи доводиться перевіряти через браузерстек на парочці девайсах якусь фічу), як це боляче в масштабах десятка девайсів та ще й з перформанс складовою, що ж тут можу сказати — добре що цей проект позаду XD ))

А був варіант вкласти сили у покращення самого сайту для користувачів?

—_—
Так, весь інший час цим і займаємося ; )

Можете мою попередню статтю почитати (dou.ua/forums/topic/41197), там як раз про покращення досвіду користувача в дуже цікавий спосіб)

3KB це не магічна цифра, може бути і більше/менше. Якщо мені не зраджує пам’ять, то для картинки, щоб вона кваліфікувалась на LCP, потрібно 0,05 біт на піксель.

UPDATE: знайшов де про це читав: csswizardry.com/...​imate-lqip-lcp-technique, ось тут ще github.com/...​ddadc9e30c4d5d6a22e1614b5

Так, ви абсолютно праві, це гарне уточнення, щоб було зрозуміло звідки воно взяте, дякую!
Бо я його побачив занадто пізно, вже після того, як відмучався з методом проб та помилок))

Саме цікаве та іронічне, що воно було в мене прямо під носом, на тому скріні що я додав (про зміни в 112 хромі, абзац та параграф) , трошки нижче, як раз було про це написано)) ось оригінал: chromium.googlesource.com/...​_changelog/2023_04_lcp.md

Тому дякую що вказали на це, бо я забув уточнити)

Підписатись на коментарі