Ускоряем
Мобильный Веб
Сергей Перескоков, Яндекс.Еда
2007 - 2019
Рост Mobile Web
Скорость парсинга JS
Топики:
- Ускорение первого рендеринга
- Ускорение момента интерактивности
- Отзывчивые большие списки
Что значит быстро?
Время | Что видит юзер |
---|---|
0-16мс | Время на отрисовку кадра |
0-100мс | Мгновенный отклик |
100-300мс | Заметная задержка |
300-1000мс | Выполнение задачи |
> 1000мс | Теряется фокус внимания |
> 10c - Теряете пользователя
Как мерить скорость?
- Navigation Timing API
- PerformanceObserver
- PageSpeed Insights
- Lighthouse
Метрики
Метрики
- First Contentful Paint
- First Meaningful Paint - FMP
- Speed Index
- First CPU Idle
- Time to Interactive - TTI
- Estimated Input Latency
First Meaningful Paint
Первая отрисовка полезного контента для пользователя
Как ускорить FMP
- Server rendering
- Critical CSS
- Загружать только нужный JS
- Lazy loading картинок
- Сократить TTFB
TTFB
Уменьшаем TTFB
HTTP ответ в браузер может прилетать по частям
app.get('/', async (req, res) => {
res.write(`
<html>
<head>
<script src='...'>
<link rel='stylesheet'>
`)
// TTFB here
// Start loading scripts/fonts/stylesheets
const data = await heavyRequest(...)
res.write(`
<title>...</title>
</head>
<body>
${dataToHTML(data)}
</body>
</html>
`)
res.end()
})
Минусы
- Непонятно что с GZIP
- Статус всегда 200
- Нет HTTP-редиректов
- Не выставить HTTP-only cookie
Чиним GZIP
import zlib, {Gzip} from 'zlib'
app.get('/', async (req, res) => {
const stream = zlib.createGzip()
// Отключаем буферизацию для proxy_pass
res.setHeader('X-Accel-Buffering', 'no')
stream.pipe(res)
stream.write(`
<html>
<head>
...
`)
// Отправляем содержимое всего стрима в HTTP ответ
stream.flush()
const data = await heavyRequest(...)
stream.write(`
<title>...</title>
</head>
<body>
...
</body>
</html>
`)
stream.flush()
stream.end()
})
Чиним HTTP-куки
- Создаем уникальный токен на запрос
- Отправляем токен как HTTP-only куку
- Дожидаемся ответа от бэка (PHPSESSID)
- Шифруем PHPSESSID c токеном
- Зашифрованный PHPSESSID отправляем в браузер
- Из браузера посылаем запрос на расшифровку
- В ответе на запрос проставляется кука PHPSESSID
Лоадер
Добавляем лоадер
import zlib, {Gzip} from 'zlib'
app.get('/', async (req, res) => {
const stream = zlib.createGzip()
stream.pipe(res)
stream.write(`
<html>
<head>
<script src="..."></script>
<div id="splash-screen"></div>
`)
...
})
(Плохой вариант)
Добавляем лоадер
import zlib, {Gzip} from 'zlib'
app.get('/', async (req, res) => {
const stream = zlib.createGzip()
stream.pipe(res)
stream.write(`
<html>
<head>
<script src="..."></script>
<script>
document.write('<body' + '><' + 'div id="splash-screen"><' + '/div>');
</script>
`)
...
})
PROFIT
Time To Interactive
Момент, когда пользователь может что-то делать со страницей
Что влияет на TTI
- Скорость загрузки скриптов
- Количество скриптов
- Скорость парсинга скриптов
- Скорость выполнения скриптов
JS Tasks
Hydrate
Rendering
Reflow / Repaint
- elem.offsetLeft, elem.offsetTop, elem.offsetWidth, elem.offsetHeight, elem.offsetParent
- elem.clientLeft, elem.clientTop, elem.clientWidth, elem.clientHeight
- elem.getClientRects(), elem.getBoundingClientRect()
- getComputedStyle
- range.getClientRects(), range.getBoundingClientRect()
- mouseEvt.layerX, mouseEvt.layerY, mouseEvt.offsetX, mouseEvt.offsetY
- inputElem.focus()
- inputElem.select(), textareaElem.select()
- ....
Как улучшить TTI
- Избегайте Reflow/Repaint
- Уменьшите размер DOM
- Сократите кол-во выполняемого JS
Большие списки
- Товарные списки
- Фиды/Ленты
- Таблицы
Как ускорить?
Скрывайте то, что не видно
Как ускорить?
- Virtual Scroll
- Visibility Observer
render = () => (
<InView>
{
({inView}) => {
if (inView) {
return (
<Content />
)
}
return (
<Skeleton />
)
}}
</InView>
)
Q: А если сразу нужно отрисовать много контента?
A: Отрисовываем, замеряем, удаляем лишнее
Выводы
-
Загружайте только то, что нужно
-
Рендерите, что видно
-
Посматривайте периодически в Lighthouse (CI)
-
Изучите вашу аудиторию перед началом оптимизации
-
Не забывайте про прогресс
Как мерить Impact?
thinkwithgoogle.com/feature/testmysite
Вопросы?
Сергей Перескоков, Яндекс.Еда
Ускоряем мобильный веб
By Sergey Pereskokov
Ускоряем мобильный веб
- 1,895