Поскольку мы создаем сайты, в большей степени зависящие от JavaScript, мы иногда платим за то, что отправляем, способами, которые не всегда легко увидеть. В этой статье мы расскажем, почему небольшая дисциплина может помочь, если вы хотите, чтобы ваш сайт быстро загружался и был интерактивным на мобильных устройствах. Доставка меньшего количества JavaScript может означать меньше времени на передачу по сети, меньше затрат на распаковку кода и меньше времени на анализ и компиляцию этого JavaScript.
Сеть
Когда большинство разработчиков думают о стоимости JavaScript, они думают об этом с точки зрения стоимости загрузки и выполнения . Отправка большего количества байтов JavaScript по сети занимает больше времени, чем медленнее соединение пользователя.
Это может быть проблемой, поскольку эффективный тип сетевого подключения, который имеет пользователь, на самом деле может не быть 3G, 4G или Wi-Fi. Вы можете пользоваться Wi-Fi в кофейне, но при этом быть подключенными к сотовой точке доступа со скоростью 2G.
Вы можете снизить стоимость сетевой передачи JavaScript посредством:
- Отправка только того кода, который нужен пользователю .
- Используйте разделение кода, чтобы разделить ваш JavaScript на то, что важно, а что нет. Упаковщики модулей, такие как Webpack, поддерживают разделение кода .
- Ленивая загрузка некритического кода.
- Минимизация
- Используйте UglifyJS для минимизации кода ES5.
- Используйте Babel-minify или Uglify-es для минимизации ES2015+.
- Сжатие
- Удаление неиспользуемого кода .
- Определите возможности для кода, который можно удалить или отложенно загрузить с помощью DevTools Code Coverage .
- Используйте Babel-preset-env и Browserlist, чтобы избежать переноса функций, уже имеющихся в современных браузерах. Опытные разработчики могут обнаружить, что тщательный анализ пакетов веб-пакетов помогает выявить возможности для удаления ненужных зависимостей.
- Для удаления кода см. Tree-shaking , расширенные оптимизации Closure Compiler и плагины обрезки библиотек, такие как lodash-babel-plugin или ContextReplacementPlugin веб-пакета для таких библиотек, как Moment.js.
- Кэширование кода для минимизации сетевых отключений.
- Используйте HTTP-кэширование , чтобы браузеры эффективно кэшировали ответы. Определите оптимальные сроки жизни сценариев (max-age) и предоставьте токены проверки (ETag), чтобы избежать передачи неизмененных байтов.
- Кэширование Service Worker может повысить устойчивость сети вашего приложения и предоставить вам быстрый доступ к таким функциям, как кэш кода V8 .
- Используйте долгосрочное кэширование, чтобы избежать необходимости повторно получать ресурсы, которые не изменились. Если вы используете Webpack, см. хеширование имен файлов .
Разбор/компиляция
После загрузки одна из самых тяжелых затрат JavaScript — это время, необходимое движку JS для анализа/компиляции этого кода. В Chrome DevTools анализ и компиляция являются частью желтого времени «Сценарии» на панели «Производительность».
На вкладках «Снизу вверх» и «Дерево вызовов» показано точное время синтаксического анализа/компиляции:
![ALT_TEXT_ЗДЕСЬ](https://web.developers.google.cn/static/articles/optimizing-content-efficiency-javascript-startup-optimization/image/alttexthere-cfd35a90562dd.png?authuser=5&hl=ru)
Но почему это имеет значение?
Если потратить много времени на анализ/компиляцию кода, это может сильно задержать то, как скоро пользователь сможет взаимодействовать с вашим сайтом. Чем больше JavaScript вы отправляете, тем больше времени потребуется на его анализ и компиляцию, прежде чем ваш сайт станет интерактивным.
Побайтовая обработка JavaScript обходится браузеру дороже, чем изображение эквивалентного размера или веб-шрифт — Том Дейл
По сравнению с JavaScript, обработка изображений эквивалентного размера требует больших затрат (их все равно придется декодировать!), но на обычном мобильном оборудовании JS с большей вероятностью негативно повлияет на интерактивность страницы.
![ALT_TEXT_ЗДЕСЬ](https://web.developers.google.cn/static/articles/optimizing-content-efficiency-javascript-startup-optimization/image/alttexthere-e3dc86ddc7603.png?authuser=5&hl=ru)
Когда мы говорим о медленном анализе и компиляции; контекст важен — мы говорим здесь об обычных мобильных телефонах. Обычные пользователи могут иметь телефоны с медленными процессорами и графическими процессорами, без кэша L2/L3 и даже с ограничениями по памяти.
Возможности сети и возможности устройства не всегда совпадают. Пользователь с отличным оптоволоконным соединением не обязательно имеет лучший процессор для анализа и оценки JavaScript, отправляемого на его устройство. То же самое верно и наоборот: ужасное сетевое соединение, но невероятно быстрый процессор. — Кристофер Бакстер, LinkedIn
Ниже мы можем увидеть стоимость анализа ~1 МБ распакованного (простого) JavaScript на низком и высокопроизводительном оборудовании. Разница во времени анализа/компиляции кода между самыми быстрыми телефонами на рынке и средними телефонами составляет 2–5 раз .
![ALT_TEXT_ЗДЕСЬ](https://web.developers.google.cn/static/articles/optimizing-content-efficiency-javascript-startup-optimization/image/alttexthere-52af247f207f5.png?authuser=5&hl=ru)
А как насчет реального сайта, такого как CNN.com?
На iPhone 8 высокого класса анализ/компиляция JS CNN занимает всего около 4 секунд по сравнению с ~13 секундами для среднего телефона (Moto G4) . Это может существенно повлиять на то, насколько быстро пользователь сможет полноценно взаимодействовать с этим сайтом.
![ALT_TEXT_ЗДЕСЬ](https://web.developers.google.cn/static/articles/optimizing-content-efficiency-javascript-startup-optimization/image/alttexthere-7cafa788a7bdf.png?authuser=5&hl=ru)
Это подчеркивает важность тестирования на среднем оборудовании (например, Moto G4), а не только на телефоне, который может быть у вас в кармане. Однако контекст имеет значение: оптимизируйте его с учетом устройства и условий сети, которые есть у ваших пользователей.
![ALT_TEXT_ЗДЕСЬ](https://web.developers.google.cn/static/articles/optimizing-content-efficiency-javascript-startup-optimization/image/alttexthere-6de3a79335e9b.png?authuser=5&hl=ru)
Действительно ли мы отправляем слишком много JavaScript? Эээ, возможно :)
Используя HTTP-архив (около 500 тыс. лучших сайтов) для анализа состояния JavaScript на мобильных устройствах , мы видим, что 50% сайтов становятся интерактивными более 14 секунд. Эти сайты тратят до 4 секунд только на парсинг и компиляцию JS.
Примите во внимание время, необходимое для получения и обработки JS и других ресурсов, и, возможно, неудивительно, что пользователям приходится некоторое время ждать, прежде чем они почувствуют, что страницы готовы к использованию. Мы определенно можем добиться большего здесь.
Удаление некритического JavaScript с ваших страниц может сократить время передачи, ресурсоемкий анализ и компиляцию, а также потенциальную нагрузку на память. Это также помогает быстрее сделать ваши страницы интерактивными.
Время исполнения
Не только анализ и компиляция могут иметь затраты. Выполнение JavaScript (запуск кода после анализа/компиляции) — это одна из операций, которая должна происходить в основном потоке. Длительное время выполнения также может повлиять на то, как скоро пользователь сможет взаимодействовать с вашим сайтом.
Если скрипт выполняется более 50 мс, время взаимодействия задерживается на все время, необходимое для загрузки, компиляции и выполнения JS — Алекс Рассел
Чтобы решить эту проблему, JavaScript выгодно разделить на небольшие фрагменты , чтобы избежать блокировки основного потока. Выясните, можете ли вы сократить объем работы, выполняемой во время выполнения.
Другие расходы
JavaScript может влиять на производительность страницы и другими способами:
- Объем памяти. Страницы могут часто зависать или приостанавливаться из-за GC (сборки мусора). Когда браузер освобождает память, выполнение JS приостанавливается, поэтому браузер, часто собирающий мусор, может приостанавливать выполнение чаще, чем нам хотелось бы. Избегайте утечек памяти и частых пауз в сборке мусора, чтобы избежать зависаний страниц.
- Во время выполнения продолжительный JavaScript может блокировать основной поток, вызывая зависание страниц. Разбиение работы на более мелкие части (с использованием
requestAnimationFrame()
илиrequestIdleCallback()
для планирования) может свести к минимуму проблемы с реагированием, что может помочь улучшить взаимодействие со следующей отрисовкой (INP) .
Шаблоны для снижения стоимости доставки JavaScript
Когда вы пытаетесь сократить время синтаксического анализа/компиляции и передачи по сети для JavaScript, существуют шаблоны, которые могут помочь, например, фрагментирование на основе маршрутов или PRPL .
ПРПЛ
PRPL (Push, Render, Pre-cache, Lazy-load) — это шаблон, который оптимизирует интерактивность за счет агрессивного разделения кода и кэширования:
Давайте представим, какой эффект это может оказать.
Мы анализируем время загрузки популярных мобильных сайтов и прогрессивных веб-приложений с помощью статистики вызовов во время выполнения V8. Как мы видим, время синтаксического анализа (показано оранжевым цветом) составляет значительную часть того, на что многие из этих сайтов тратят свое время:
Wego , сайт, использующий PRPL, умудряется поддерживать малое время анализа своих маршрутов и очень быстро становится интерактивным. Многие другие сайты, упомянутые выше, приняли бюджеты на разделение кода и производительность, чтобы попытаться снизить затраты на JS.
Прогрессивная загрузка
Многие сайты оптимизируют видимость контента за счет интерактивности. Чтобы получить быструю первую отрисовку при наличии больших пакетов JavaScript, разработчики иногда используют рендеринг на стороне сервера; затем «обновите» его, чтобы присоединить обработчики событий, когда JavaScript наконец будет получен.
Будьте осторожны — это имеет свои издержки. Вы 1) обычно отправляете более крупный HTML-ответ, который может повысить нашу интерактивность, 2) можете оставить пользователя в жуткой долине, где половина опыта не может быть интерактивной, пока JavaScript не завершит обработку.
Прогрессивная загрузка может быть лучшим подходом. Отправьте минимально функциональную страницу (состоящую только из HTML/JS/CSS, необходимых для текущего маршрута). По мере поступления большего количества ресурсов приложение может лениво загружать и разблокировать больше функций.
![ALT_TEXT_ЗДЕСЬ](https://web.developers.google.cn/static/articles/optimizing-content-efficiency-javascript-startup-optimization/image/alttexthere-bc9d1b36573cd.png?authuser=5&hl=ru)
Загрузка кода, пропорционального тому, что мы видим, — это Святой Грааль. PRPL и Progressive Bootstrap — шаблоны, которые могут помочь в этом.
Выводы
Размер передачи имеет решающее значение для сетей низкого уровня. Время анализа важно для устройств, привязанных к процессору. Сохранение этих низких вещей имеет значение.
Команды добились успеха, приняв строгие бюджеты производительности, чтобы сократить время передачи JavaScript и анализа/компиляции. См. статью Алекса Рассела « Можете ли вы себе это позволить?: Реальные бюджеты веб-производительности » для получения рекомендаций по бюджетам для мобильных устройств.
![ALT_TEXT_ЗДЕСЬ](https://web.developers.google.cn/static/articles/optimizing-content-efficiency-javascript-startup-optimization/image/alttexthere-72d5f9f9722e.png?authuser=5&hl=ru)
Если вы создаете сайт, ориентированный на мобильные устройства, сделайте все возможное для разработки на типичном оборудовании, сократите время анализа/компиляции JavaScript и примите бюджет производительности, чтобы ваша команда могла следить за расходами на JavaScript.
Узнать больше
- Chrome Dev Summit 2017 – лучшие практики современной загрузки
- Производительность запуска JavaScript
- Решение кризиса веб-производительности — Нолан Лоусон
- Можете ли вы себе это позволить? Реальные бюджеты производительности — Алекс Рассел
- Оценка веб-фреймворков и библиотек — Кристофер Бакстер
- Результаты экспериментов Cloudflare с Brotli для сжатия (обратите внимание, что динамический Brotli с более высоким качеством может задержать рендеринг начальной страницы, поэтому оценивайте внимательно. Вместо этого вы, вероятно, захотите статически сжать.)
- Фьючерсы производительности — Сэм Сакконе