Стоимость JavaScript

Поскольку мы создаем сайты, в большей степени использующие JavaScript, мы иногда платим за то, что отправляем, способами, которые не всегда легко увидеть. В этом посте я расскажу, почему небольшая дисциплина может помочь, если вы хотите, чтобы ваш сайт быстро загружался и был интерактивным на мобильных устройствах.

tl; dr: меньше кода = меньше синтаксического анализа / компиляции + меньше передачи + меньше распаковки

Сеть

Когда большинство разработчиков думают о стоимости JavaScript, они думают об этом с точки зрения стоимости загрузки и выполнения. Отправка большего количества байтов JavaScript по сети занимает больше времени, чем медленнее пользовательское соединение.

Это может быть проблемой даже в странах первого мира, поскольку эффективный тип сетевого подключения, который имеет пользователь, на самом деле может не быть 3G, 4G или WiFi. Вы можете подключиться к Wi-Fi в кафе, но подключиться к сотовой точке доступа со скоростью 2G.

Вы можете снизить стоимость передачи JavaScript по сети следующим образом:

  • Доставка только кода, который нужен пользователю. Здесь может помочь разделение кода.
  • Минимизация (Uglify для ES5, babel-minify или uglify-es для ES2015)
  • Сильное сжатие (с использованием Brotli ~ q11, Zopfli или gzip). Brotli превосходит gzip по степени сжатия. Это помогло CertSimple сэкономить 17% на размере сжатых байтов JS, а LinkedIn - 4% на времени их загрузки.
  • Удаление неиспользуемого кода. Отождествляйте себя с покрытием кода DevTools. Для удаления кода см. Разделы tree-shaking, Closure Compiler, расширенные оптимизации и плагины обрезки библиотек, такие как lodash-babel-plugin или ContextReplacementPlugin Webpack для таких библиотек, как Moment.js. Используйте babel-preset-env & browserlist, чтобы избежать переноса функций, уже имеющихся в современных браузерах. Опытные разработчики могут обнаружить, что тщательный анализ их пакетов Webpack помогает выявить возможности для сокращения ненужных зависимостей.
  • Кэширование для минимизации сетевых отключений. Определите оптимальное время жизни для скриптов (max-age) и предоставьте токены проверки (ETag), чтобы избежать передачи неизмененных байтов. Кэширование Service Worker может сделать сеть вашего приложения устойчивой и предоставить вам быстрый доступ к таким функциям, как кеш кода V8. Узнайте о долгосрочном кэшировании с помощью хеширования имени файла.

Разбор / компиляция

После загрузки одна из самых больших затрат на JavaScript - это время, когда JS-движок проанализирует / скомпилирует этот код. В Chrome DevTools синтаксический анализ и компиляция являются частью желтого времени «Сценарии» на панели «Производительность».

Дерево Bottom-Up / Call позволяет просматривать точное время синтаксического анализа / компиляции:

Но почему это важно?

Длительные затраты на синтаксический анализ / компиляцию кода могут сильно замедлить то, как скоро пользователь сможет взаимодействовать с вашим сайтом. Чем больше JavaScript вы отправите, тем больше времени потребуется на его анализ и компиляцию, прежде чем ваш сайт станет интерактивным.

Побайтно, JavaScript обходится браузеру дороже, чем изображение или веб-шрифт аналогичного размера - Том Дейл.

По сравнению с JavaScript обработка изображений одинакового размера сопряжена с многочисленными расходами (их все равно нужно декодировать!), Но на среднем мобильном оборудовании JS с большей вероятностью отрицательно повлияет на интерактивность страницы.

Когда мы говорим о медленном синтаксическом анализе и компиляции; контекст важен - здесь мы говорим о средних мобильных телефонах. Обычные пользователи могут иметь телефоны с медленными процессорами и графическими процессорами, без кэша L2 / L3 и даже с ограниченным объемом памяти.

Возможности сети и возможности устройства не всегда совпадают. Пользователь с отличным оптоволоконным соединением не обязательно имеет лучший процессор для анализа и оценки кода JavaScript, отправленного на его устройство. Это верно и в обратном направлении ... ужасное сетевое соединение, но невероятно быстрый процессор. - Кристофер Бакстер, LinkedIn

В статье Производительность при запуске JavaScript я отметил стоимость синтаксического анализа ~ 1 МБ распакованного (простого) JavaScript на аппаратном обеспечении низкого и высокого уровня. Разница во времени для синтаксического анализа / компиляции кода между самыми быстрыми телефонами на рынке и средними телефонами составляет 2–5 раз.

А как насчет реального сайта, такого как CNN.com?

На iPhone 8 высокого класса для синтаксического анализа / компиляции JS CNN требуется всего ~ 4 секунды по сравнению с ~ 13 секундами для обычного телефона (Moto G4). Это может значительно повлиять на то, как быстро пользователь сможет полноценно взаимодействовать с этим сайтом.

Это подчеркивает важность тестирования на среднем оборудовании (например, Moto G4), а не только на телефоне, который может быть у вас в кармане. Однако контекст имеет значение: оптимизируйте устройство и сетевые условия ваших пользователей.

Аналитика может дать представление о классах мобильных устройств, с помощью которых реальные пользователи заходят на ваш сайт. Это может дать возможность понять реальные ограничения CPU / GPU, с которыми они работают.

Мы действительно отправляем слишком много JavaScript? Возможно :)

Используя HTTP-архив (~ 500 тыс. Сайтов) для анализа состояния JavaScript на мобильных устройствах, мы видим, что 50% сайтов становятся интерактивными за 14 секунд. Эти сайты тратят до 4 секунд на анализ и компиляцию JS.

Учитывайте время, необходимое для получения и обработки JS и других ресурсов, и, возможно, неудивительно, что пользователям приходится ждать некоторое время, прежде чем они почувствуют, что страницы готовы к использованию. Мы определенно можем добиться большего здесь.

Удаление некритического кода JavaScript с ваших страниц может сократить время передачи, интенсивный синтаксический анализ и компиляцию процессора, а также потенциальные накладные расходы на память. Это также помогает сделать ваши страницы более интерактивными.

Время исполнения

Это не просто синтаксический анализ и компиляция, которые могут иметь свою цену. Выполнение JavaScript (запуск кода после синтаксического анализа / компиляции) - одна из операций, которые должны выполняться в основном потоке. Длительное время выполнения также может повлиять на то, как скоро пользователь сможет взаимодействовать с вашим сайтом.

Если скрипт выполняется более 50 мс, время до интерактивности задерживается на все время, необходимое для загрузки, компиляции и выполнения JS - Алекс Рассел

Чтобы решить эту проблему, JavaScript может быть разделен на небольшие фрагменты, чтобы не блокировать основной поток. Узнайте, можно ли уменьшить объем работы, выполняемой во время выполнения.

Шаблоны для снижения стоимости доставки JavaScript

Когда вы пытаетесь сохранить медленное время синтаксического анализа / компиляции и передачи по сети для JavaScript, есть шаблоны, которые могут помочь, например, фрагменты на основе маршрутов или PRPL.

PRPL - это шаблон, который оптимизируется для интерактивности за счет агрессивного разделения кода и кеширования:

Давайте представим себе, какое влияние это может оказать.

Мы анализируем время загрузки популярных мобильных сайтов и прогрессивных веб-приложений с помощью статистики вызовов времени выполнения V8. Как мы видим, время синтаксического анализа (показано оранжевым цветом) составляет значительную часть того, на что многие из этих сайтов проводят свое время:

Wego, сайту, использующему PRPL, удается поддерживать низкое время синтаксического анализа своих маршрутов, благодаря чему он очень быстро становится интерактивным. Многие другие вышеперечисленные сайты приняли бюджет на разделение кода и производительность, чтобы попытаться снизить затраты на JS.

Другие расходы

JavaScript может влиять на производительность страницы и другими способами:

  • Объем памяти. Страницы могут часто зависать или останавливаться из-за GC (сборки мусора). Когда браузер освобождает память, выполнение JS приостанавливается, поэтому браузер, часто собирающий мусор, может приостанавливать выполнение чаще, чем нам хотелось бы. Избегайте утечек памяти и частых пауз gc, чтобы страницы не зависали.
  • Во время выполнения длительно выполняющийся JavaScript может блокировать основной поток, вызывая невосприимчивость страниц. Разделение работы на более мелкие части (с использованием requestAnimationFrame () или requestIdleCallback () для планирования) может минимизировать проблемы с быстродействием.

Прогрессивная загрузка

Многие сайты оптимизируют видимость контента, поскольку интерактивность требует больших затрат. Чтобы получить быструю первую отрисовку, когда у вас есть большие пакеты JavaScript, разработчики иногда используют рендеринг на стороне сервера; затем «обновите» его, добавив обработчики событий, когда, наконец, будет получен JavaScript.

Будьте осторожны - это имеет свои издержки. Вы 1) обычно отправляете больший HTML-ответ, который может повысить нашу интерактивность, 2) можете оставить пользователя в жуткой долине, где половина опыта фактически не может быть интерактивной, пока JavaScript не завершит обработку.

Прогрессивная загрузка может быть лучшим подходом. Отправьте минимально функциональную страницу (состоящую только из HTML / JS / CSS, необходимых для текущего маршрута). По мере поступления дополнительных ресурсов приложение может выполнять отложенную загрузку и разблокировать больше функций.

Загрузка кода пропорционально тому, что вы видите, - это Святой Грааль. PRPL и прогрессивная загрузка - это шаблоны, которые могут помочь в этом.

Выводы

Размер передачи имеет решающее значение для сетей низкого уровня. Время синтаксического анализа важно для устройств с привязкой к ЦП. Держать эти мелкие вопросы.

Команды добились успеха, приняв строгие бюджеты производительности, чтобы сократить время передачи и синтаксического анализа / компиляции JavaScript. См. Статью Алекса Рассела Можете ли вы себе это позволить? Реальные бюджеты веб-производительности »для получения рекомендаций по бюджетам для мобильных устройств.

Если вы создаете сайт, ориентированный на мобильные устройства, сделайте все возможное для разработки на типичном оборудовании, уменьшите время синтаксического анализа / компиляции JavaScript и установите бюджет производительности, чтобы ваша команда могла следить за своими расходами на JavaScript.

Учить больше

Благодарим Нолана Лоусона, Кристофера Бакстера и Джереми Вагнера за их отзывы.