Нет более быстрого способа замедлить сайт (такой вот каламбур), чем использовать на нём кучу JavaScript-кода. При использовании JavaScript приходится расплачиваться за это производительностью проектов не менее чем четыре раза. Вот чем JavaScript-код сайта нагружает системы пользователей:
- Загрузка файла по сети.
- Парсинг и компиляция распакованного исходного кода после загрузки.
- Выполнение JavaScript-кода.
- Потребление памяти.
Эта комбинация оказывается очень дорогой.
А мы включаем в состав своих проектов всё больше и больше JS-кода. По мере того, как организации движутся в сторону сайтов, работающих на базе фреймворков и библиотек вроде React, Vue и прочих, мы делаем основной функционал сайтов очень сильно зависящим от JavaScript.
Я видел множество весьма тяжёлых сайтов, использующих JavaScript-фреймворки. Но моё видение вопроса отличается сильной предвзятостью. Дело в том, что компании, с которыми я работаю, обращаются ко мне именно из-за того, что они встречаются со сложными проблемами в области производительности сайтов. В результате мне стало любопытно узнать о том, насколько распространена эта проблема, и о том какие «штрафы» мы платим тогда, когда выбираем тот или иной фреймворк в качестве основы для некоего сайта.
Выяснить это мне помог проект HTTP Archive.
Данные
Проект HTTP Archive, в общей сложности, отслеживает 4308655 ссылок на обычные сайты, предназначенные для настольных устройств, и 5484239 ссылок на мобильные сайты. Среди многих показателей, связанных с этими ссылками, есть список технологий обнаруженных на соответствующих сайтах. Это означает, что мы можем построить выборку из тысяч сайтов, которые используют различные фреймворки и библиотеки, и узнать о том, какой объём кода они отправляют клиентам, и о том, какую нагрузку на системы пользователей создаёт этот код.
Я собирал данные за март 2020 года, это были самые свежие данные, к которым у меня был доступ.
Я решил сравнить агрегированные HTTP Archive данные по всем сайтам с данными по сайтам, на которых обнаружено использование React, Vue и Angular, хотя я рассматривал возможность использования и другого исходного материала.
Чтобы было интереснее — я добавил в набор исходных данных ещё и сайты, на которых применяется jQuery. Эта библиотека всё ещё пользуется огромной популярностью. Она, кроме того, представляет подход к разработке веб-сайтов, отличающийся от модели одностраничного приложения (SPA, Single Page Application), предлагаемый React, Vue и Angular.
Ссылки в HTTP Archive, представляющие сайты, на которых обнаружено использование интересующих нас технологий
Фреймворк или библиотека | Ссылки на мобильные сайты | Ссылки на обычные сайты |
jQuery | 4615474 | 3714643 |
React | 489827 | 241023 |
Vue | 85649 | 43691 |
Angular | 19423 | 18088 |
Надежды и мечты
Прежде чем мы перейдём к анализу данных, хочу рассказать о том, на что мне хотелось бы надеяться.
Я полагаю, что в идеальном мире фреймворки должны идти дальше удовлетворения нужд разработчиков и давать некие конкретные преимущества обычным пользователям, работающим с нашими сайтами. Производительность — это лишь одно из таких преимуществ. Тут ещё приходят в голову доступность и безопасность. Но это — лишь самое важное.
Итак, в идеальном мире некий фреймворк должен облегчать создание высокопроизводительного сайта. Делаться это должно либо за счёт того, что фреймворк даёт разработчику достойную базу, на которой можно построить проект, либо за счёт того, что накладывает на разработку ограничения, выдвигает к ней требования, которые усложняют разработку чего-то такого, что окажется медленным.
Самые лучшие фреймворки должны делать и то и другое: и давать хорошую базу, и накладывать на работу ограничения, позволяющие добиваться достойного результата.
Анализ медианных значений данных не даст нам нужной информации. И, на самом деле, такой подход оставляет за пределами нашего внимания очень много важного. Вместо этого я вывел из имеющихся у меня данных показатели, выраженные в перцентилях. Это — 10, 25, 50 (медиана), 75, 90 перцентили.
Меня особенно интересуют 10 и 90 перцентили. 10 перцентиль представляет самые лучшие показатели (или, по крайней мере, более или менее близкие к самым лучшим) для конкретного фреймворка. Другими словами, это значит, что лишь 10% сайтов, использующих конкретный фреймворк доходят до этого, или до более высокого уровня. 90 перцентиль, с другой стороны, это — обратная сторона медали — он показывает нам то, насколько всё может быть плохо. 90 перцентиль — это сайты, плетущиеся в хвосте — те последние 10% сайтов, которые характеризуются самыми большими объёмами JS-кода или самым большим временем, требуемым на обработку их кода в главном потоке.
Объёмы JavaScript-кода
Для начала имеет смысл проанализировать размер JavaScript-кода, передаваемого разными сайтами по сети.
Объём JavaScript-кода (Кб), переданного на мобильные устройства
Перцентили | 10 | 25 | 50 | 75 | 90 |
Все сайты | 93.4 | 196.6 | 413.5 | 746.8 | 1201.6 |
jQuery-сайты | 110.3 | 219.8 | 430.4 | 748.6 | 1162.3 |
Vue-сайты | 244.7 | 409.3 | 692.1 | 1065.5 | 1570.7 |
Angular-сайты | 445.1 | 675.6 | 1066.4 | 1761.5 | 2893.2 |
React-сайты | 345.8 | 441.6 | 690.3 | 1238.5 | 1893.6 |
Объём JavaScript-кода, передаваемый на мобильные устройства
Объём JavaScript-кода (Кб), переданного на настольные устройства
Перцентили | 10 | 25 | 50 | 75 | 90 |
Все сайты | 105.5 | 226.6 | 450.4 | 808.8 | 1267.3 |
jQuery-сайты | 121.7 | 242.2 | 458.3 | 803.4 | 1235.3 |
Vue-сайты | 248.0 | 420.1 | 718.0 | 1122.5 | 1643.1 |
Angular-сайты | 468.8 | 716.9 | 1144.2 | 1930.0 | 3283.1 |
React-сайты | 308.6 | 469.0 | 841.9 | 1472.2 | 2197.8 |
Объём JavaScript-кода, передаваемый на настольные устройства
Если говорить только о размерах JS-кода, отправляемого сайтами на устройства, то всё выглядит примерно так, как можно ожидать. А именно, если используется один из фреймворков — это значит, что даже в идеальной ситуации объём JavaScript-кода сайта вырастет. Это и неудивительно — нельзя сделать JavaScript-фреймворк основой сайта и ожидать того, что объём JS-кода проекта окажется очень низким.
Примечательно в этих данных то, что некоторые фреймворки и библиотеки можно счесть более удачной стартовой точкой проекта, чем другие. Сайты с jQuery выглядят лучше всего. В настольных версиях сайтов они содержат на 15% больше JavaScript, чем все сайты, а в мобильных — на 18% больше. (Надо признать, что тут можно наблюдать некоторое искажение данных. Дело в том, что jQuery присутствует на множестве сайтов, поэтому вполне естественно то, что такие сайты теснее, чем другие, связаны с общим количеством сайтов. Однако это не влияет на то, как исходные данные выводятся для каждого фреймворка.)
Хотя рост объёма кода в 15-18% — это заметная цифра, сравнив это с другими фреймворками и библиотеками, можно прийти к выводу о том, что «налог», взимаемый jQuery, очень низок. Сайты на Angular из 10 перцентиля отправляют на настольные устройства на 344% больше данных, чем все сайты, а на мобильные — на 377% больше. React-сайты — следующие по «тяжести», отправляют на настольные устройства на 193% больше кода, чем все сайты, а на мобильные — на 270% больше.
Раньше я уже упоминал о том, что хотя применение фреймворка и означает, что в проект, в самом начале работы над ним, будет включён определённый объём кода, я надеюсь на то, что фреймворк способен как-то ограничивать разработчика. В частности, речь идёт об ограничении максимального объёма кода.
Интересно то, что jQuery-сайты следуют этой идее. Хотя они, на уровне 10 перцентиля, немного тяжелее всех сайтов (на 15-18%), они, на уровне 90 перцентиля, немного легче всех — примерно на 3% и в настольном, и в мобильном вариантах. Нельзя сказать, что это — очень уж значительная выгода, но можно сказать, что сайты на jQuery, по крайней мере, не отличаются огромными размерами JavaScript-кода даже в своих самых больших вариантах.
А вот о других фреймворках того же самого сказать нельзя.
Так же, как и в случае с 10 перцентилем, на 90 перцентиле сайты на Angular и React отличаются от других сайтов, но отличаются они, к сожалению, в худшую сторону.
На 90 перцентиле Angular-сайты отправляют на мобильные устройства на 141% больше данных, чем все сайты, а на настольные — на 159% больше. Сайты на React отправляют на настольные устройства на 73% больше, чем все сайты, а на мобильные — на 58% больше. Размер кода React-сайтов на 90 перцентиле составляет 2197.8 Кб. Это значит, что такие сайты отправляют на мобильные устройства на 322.9 Кб больше данных, чем их ближайшие конкуренты, основанные на Vue. Разрыв между настольными сайтами, основанными на Angular и React, и между другими сайтами, ещё больше. Например, настольные React-сайты отправляют на устройства на 554.7 Кб больше JS-кода, чем аналогичные Vue-сайты.
Время, уходящее на обработку JavaScript-кода в главном потоке
Вышеприведённые данные чётко указывают на то, что сайты, использующие исследуемые фреймворки и библиотеки, содержат большие объёмы JavaScript-кода. Но, конечно, это лишь одна из частей нашего уравнения.
После того, как JavaScript-код прибыл в браузер, его нужно привести в работоспособное состояние. Особенно много проблем вызывают те действия, которые приходится проводить с кодом в главном потоке браузера. Главный поток ответственен за обработку действий пользователя, за вычисление стилей, за построение и вывод макета страницы. Если завалить главный поток JavaScript-делами, у него не будет возможности вовремя решать остальные задачи. Это приводит к задержкам и «тормозам» в работе страниц.
В базе данных HTTP Archive есть сведения о том, сколько времени уходит на обработку JavaScript-кода в главном потоке движка V8. Это значит, что мы можем собрать эти данные и узнать о том, сколько времени главного потока требуется на обработку JavaScript различных сайтов.
Процессорное время (в миллисекундах), относящееся к обработке скриптов на мобильных устройствах
Перцентили | 10 | 25 | 50 | 75 | 90 |
Все сайты | 356.4 | 959.7 | 2372.1 | 5367.3 | 10485.8 |
jQuery-сайты | 575.3 | 1147.4 | 2555.9 | 5511.0 | 10349.4 |
Vue-сайты | 1130.0 | 2087.9 | 4100.4 | 7676.1 | 12849.4 |
Angular-сайты | 1471.3 | 2380.1 | 4118.6 | 7450.8 | 13296.4 |
React-сайты | 2700.1 | 5090.3 | 9287.6 | 14509.6 | 20813.3 |
Процессорное время, относящееся к обработке скриптов на мобильных устройствах
Процессорное время (в миллисекундах), относящееся к обработке скриптов на настольных устройствах
Перцентили | 10 | 25 | 50 | 75 | 90 |
Все сайты | 146.0 | 351.8 | 831.0 | 1739.8 | 3236.8 |
jQuery-сайты | 199.6 | 399.2 | 877.5 | 1779.9 | 3215.5 |
Vue-сайты | 350.4 | 650.8 | 1280.7 | 2388.5 | 4010.8 |
Angular-сайты | 482.2 | 777.9 | 1365.5 | 2400.6 | 4171.8 |
React-сайты | 508.0 | 1045.6 | 2121.1 | 4235.1 | 7444.3 |
Процессорное время, относящееся к обработке скриптов на настольных устройствах
Тут можно разглядеть кое-что очень знакомое.
Для начала — сайты с jQuery тратят на обработку JavaScript в главном потоке значительно меньше, чем другие. На 10 перцентиле, в сравнении со всеми сайтами, jQuery-сайты на мобильных устройствах тратят на обработку JS-кода в главном потоке на 61% больше времени. В случае с настольными jQuery-сайтами время обработки растёт на 37%. На уровне 90 перцентиля показатели jQuery-сайтов очень близки к агрегированным показателям. А именно, jQuery-сайты на мобильных устройствах тратят на 1.3% меньше времени в главном потоке, чем все сайты, а на настольных устройствах — на 0.7% меньше времени.
С другой стороны нашего рейтинга находятся фреймворки, которые характеризуются наибольшей нагрузкой на главный поток. Это, опять, Angular и React. Разница между ними заключается только в том, что, хотя Angular-сайты шлют в браузеры большие объёмы кода, чем React-сайты, на обработку кода Angular-сайтов уходит меньше процессорного времени. Гораздо меньше.
На 10 перцентиле настольные Angular-сайты тратят на обработку JS-кода на 230% больше времени главного потока, чем все сайты. Для мобильных сайтов этот показатель составляет 313%. React-сайты характеризуются худшими показателями. На настольных устройствах они тратят на обработку кода на 248% больше времени, чем все сайты, а на мобильных — на 658% больше. 658% — это не опечатка. На 10 перцентиле React-сайты тратят 2.7 секунды времени главного потока на обработку имеющегося в них кода.
Показатели 90 перцентиля, если сравнить их с этими огромными цифрами, выглядят, по меньшей мере, немного лучше. Angular-проекты, сравнённые со всеми сайтами, тратят на настольных устройствах в главном потоке на 29% больше времени, а на мобильных устройствах — на 27% больше. В случае с React-сайтами аналогичные показатели выглядят, соответственно, как 130% и 98%.
Процентные значения отклонений для 90 перцентиля выглядят лучше, чем похожие значения для 10 перцентиля. Но тут стоит помнить о том, что цифры, указывающие на время, кажутся довольно-таки страшными. Скажем — 20.8 секунд в главном потоке мобильного устройства для сайта, построенного на React. (Полагаю, рассказ о том, что, на самом деле, происходит за это время, достоин отдельной статьи).
Тут имеется одна потенциальная сложность (благодарю Джереми за то, что он привлёк моё внимание к этой особенности, и за то, что я внимательно рассмотрел данные с этой точки зрения). Дело в том, что на многих сайтах используется несколько фронтенд-инструментов. В частности, я видел множество сайтов, которые применяют jQuery вместе с React или Vue, так как эти сайты мигрируют с jQuery на другие фреймворки или библиотеки. В результате я снова обратился к базе данных, выбирая на этот раз лишь те ссылки, которые соответствуют сайтам, на которых используется только React, jQuery, Angular или Vue, но не какая-то их комбинация. Вот что у меня получилось.
Процессорное время (в миллисекундах), относящееся к обработке скриптов на мобильных устройствах в ситуации, когда на сайтах используется только один фреймворк или только одна библиотека
Перцентили | 10 | 25 | 50 | 75 | 90 |
Сайты, на которых используется только jQuery | 542.9 | 1062.2 | 2297.4 | 4769.7 | 8718.2 |
Сайты, на которых используется только Vue | 944.0 | 1716.3 | 3194.7 | 5959.6 | 9843.8 |
Сайты, на которых используется только Angular | 1328.9 | 2151.9 | 3695.3 | 6629.3 | 11607.7 |
Сайты, на которых используется только React | 2443.2 | 4620.5 | 10061.4 | 17074.3 | 24956.3 |
Процессорное время, относящееся к обработке скриптов на мобильных устройствах в ситуации, когда на сайтах используется только один фреймворк, или только одна библиотека
Сначала — о том, что удивления не вызывает: когда на сайте используется лишь один фреймворк или одна библиотека, производительность такого сайта чаще улучшается, чем не улучшается. Показатели по каждому инструменту выглядят лучше на 10 и 25 перцентилях. Это имеет смысл. Сайт, который сделан с использованием одного фреймворка, должен быть производительнее, чем сайт, который сделан с использованием двух или большего количества фреймворков или библиотек.
На самом деле, показатели по каждому исследуемому фронтенд-инструменту выглядят лучше во всех случаях, за вычетом одного любопытного исключения. Меня удивило то, что на уровне 50 перцентиля и выше сайты, использующие React, отличаются худшей производительности в том случае, если React — это единственная используемая на них библиотека. Это, кстати, стало причиной того, что я привожу тут эти данные.
Это немного странно, но я, всё же попытаюсь поискать объяснение этой странности.
Если в проекте используется и React, и jQuery, то этот проект, скорее всего, находится где-то на полпути в процессе перехода с jQuery на React. Возможно, он имеет кодовую базу, в которых эти библиотеки смешаны. Так как мы уже видели, что jQuery-сайты тратят в главном потоке меньше времени, чем React-сайты, это может говорить нам о том, что реализация некоего функционала на jQuery помогает немного улучшить показатели сайта.
Но по мере того, как проект, переходя с jQuery на React, всё больше полагается на React, ситуация меняется. Если сайт сделан по-настоящему качественно, и разработчики сайта используют React осмотрительно, то с таким сайтом всё будет хорошо. Но для среднестатистического React-сайта широкое использование React означает, что главный поток подвергается повышенной нагрузке.
Разрыв между мобильными и настольными устройствами
Ещё одна точка зрения, с которой я взглянул на исследуемые данные, заключалась в исследовании того, насколько велик разрыв между работой с сайтами на мобильных и настольных устройствах. Если говорить о сопоставлении объёмов JavaScript-кода, то ничего страшного такое сопоставление не выявляет. Конечно, приятно было бы видеть меньшие объёмы загружаемого кода, но особой разницы в объёмах мобильного и настольного кода нет.
А вот если проанализировать время, необходимое на обработку кода, становится заметным очень большой разрыв между мобильными и настольными устройствами.
Рост времени (в процентах) относящегося к обработке скриптов на мобильных устройствах в сравнении с настольными
Перцентили | 10 | 25 | 50 | 75 | 90 |
Все сайты | 144.1 | 172.8 | 185.5 | 208.5 | 224.0 |
jQuery-сайты | 188.2 | 187.4 | 191.3 | 209.6 | 221.9 |
Vue-сайты | 222.5 | 220.8 | 220.2 | 221.4 | 220.4 |
Angular-сайты | 205.1 | 206.0 | 201.6 | 210.4 | 218.7 |
React-сайты | 431.5 | 386.8 | 337.9 | 242.6 | 179.6 |
Хотя некоторая разница в скорости обработки кода между телефоном и ноутбуком — это вполне ожидаемо, такие большие числа говорят мне о том, что современные фреймворки недостаточно нацелены на маломощные устройства, и на стремление к закрытию обнаруженного разрыва. Даже на 10 перцентиле React-сайты тратят в главном потоке мобильных устройств на 431.5% больше времени, чем в главном потоке настольных. Наименьший разрыв наблюдается у jQuery, но даже здесь соответствующий показатель равен 188.2%. Когда разработчики сайтов делают свои проекты так, что на их обработку требуется больше процессорного времени (а так и происходит, и со временем всё только хуже), платить за это приходится владельцам маломощных устройств.
Итоги
Хорошие фреймворки должны давать разработчикам качественную базу для создания веб-проектов (в плане безопасности, доступности, производительности), или должны обладать встроенными ограничениями, которые усложняют создание чего-то такого, что эти ограничения нарушают.
Это, как кажется, не относится к производительности веб-проектов (и, по видимому, к их доступности).
Стоит отметить, что то, что React- или Angular-сайты тратят больше процессорного времени на подготовку кода, чем другие, не обязательно значит то, что React-сайты в ходе работы сильнее нагружают процессор, чем Vue-сайты. На самом деле, рассмотренные нами данные очень мало говорят о рабочей производительности фреймворков и библиотек. Они больше говорят о подходах к разработке, к которым, осознанно или нет, эти фреймворки могут подталкивать программистов. Речь идёт о документации к фреймворкам, об их экосистеме, о распространённых приёмах разработки.
Ещё стоит сказать о том, что мы тут не анализировали, а именно, о том, сколько времени устройство тратит на выполнение JavaScript-кода при переходах между страницами сайта. Довод в пользу SPA заключается в том, что, как только одностраничное приложение загружено в браузер, пользователь, теоретически, сможет быстрее открывать страницы сайта. Мой собственный опыт подсказывает мне, что это — далеко не факт. Но у нас нет данных, позволяющих прояснить этот вопрос.
Совершенно ясно лишь то, что если вы пользуетесь фреймворком или библиотекой для создания сайта, вы идёте на компромисс в плане первоначальной загрузки проекта и подготовки его к работе. Это относится даже к самым позитивным сценариям.
В соответствующих ситуациях вполне можно пойти на некоторые компромиссы, но важно то, чтобы разработчики шли бы на такие компромиссы осознанно.
Но у нас есть и повод для оптимизма. Меня воодушевляет то, как плотно разработчики Chrome взаимодействуют с теми, кто занимается некоторыми из рассмотренных нами фронтенд-инструментов в стремлении помочь улучшить производительность этих инструментов.
Однако я — прагматичный человек. Новые архитектуры так же часто создают проблемы с производительностью, как и решают их. И на то, чтобы устранить недочёты, нужно время. Точно так же, как нам не стоит ожидать того, что новые сетевые технологии решат все проблемы производительности, не стоит ждать этого и от новых версий наших любимых фреймворков.
Если вы хотите воспользоваться одним из рассмотренных в этом материале фронтенд-инструментов, это означает, что вам придётся приложить дополнительные усилия к тому, чтобы, между делом, не навредить производительности своего проекта. Вот некоторые соображения, которые стоит рассмотреть перед началом применения нового фреймворка:
- Проверьте себя с точки зрения здравого смысла. Вам правда нужно использовать выбранный фреймворк? Чистый JavaScript сегодня способен на очень многое.
- Есть ли более лёгкая альтернатива выбранному фреймворку (вроде Preact, Svelte или чего-то ещё), способная дать вам 90% возможностей этого фреймворка?
- Если вы уже пользуетесь неким фреймворком — подумайте о том, есть ли что-то такое, что предлагает лучшие, более консервативные, стандартные параметры (например — Nuxt.js вместо Vue, Next.js вместо React, и так далее).
- Каким будет ваш бюджет JavaScript-производительности?
- Как вы можете ограничить процесс разработки с целью усложнения внедрения в проект большего объёма JavaScript-кода, чем это абсолютно необходимо?
- Если вы используете фреймворк ради удобства разработки, подумайте о том, нужно ли вам отправлять код фреймворка клиентам. Может быть, вы сможете решить все вопросы на сервере?
Обычно к этим идеям стоит присмотреться вне зависимости от того, что именно вы выбрали для разработки фронтенда. Но они особенно важны тогда, когда вы работаете над проектом, которому, с самого начала, не хватает производительности.
Уважаемые читатели! Каким вы видите идеальный JavaScript-фреймворк?
Автор: ru_vds