TL; DR: см. Https://en.wikipedia.org/wiki/Betteridge%27s_law_of_headlines. Или просто перейдите к резюме в конце сообщения.

У меня есть мечта! И в моей мечте все инструменты, необходимые сегодня для использования JavaScript, просто исчезли. Мы все можем просто написать код в нашем любимом редакторе, нажать «Обновить» и покончить с этим. Нет package.json. Нет babel. № 3_. Нет config.foo.json или .foorc. Просто напишите код и нажмите «Обновить».

Очевидно, что большие приложения всегда будут нуждаться в инструментах - линтерах, статическом анализе, инструментах оптимизации для производства. Но даже при написании больших приложений, пока мы просто запускаем наш код в разработке, все, что нам нужно, - это код и обновление.

Однажды я напишу в блоге сообщение о том, как этого добиться, но сегодня я хочу опробовать один из шагов, которые позволят нам достичь этого: модули ES.

Модули ES

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

Чтобы создать модуль ES, мы пишем такой код, который export является функцией.

А чтобы импортировать его, мы используем import.

Как я уже сказал, это не совсем учебное пособие, и чтобы по-настоящему изучить синтаксис, я бы посоветовал вам перейти к этому учебному пособию. Для меня важно то, что если сегодня мы script src модуль выше, который импортирует другой модуль, то он не будет работать ни в одном браузере - браузер будет рассматривать синтаксис import как синтаксическую ошибку. Производители браузеров говорят, что их последние версии более или менее на 100% покрывают ES6, и чудесная таблица совместимости с кангаксом согласуется, но это потому, что по какой-то причине модули ES не включены в их определение ES6, хотя он является частью этой версии.

Но все крутые ребята сегодня используют модули ES. Как? Как большинство примеров React и Angular могут использовать модули ES? Потому что, как обычно в наши дни, сообщество JavaScript нашло способы обойти поставщиков браузеров и включить модули ES с помощью таких инструментов, как Webpack, browserify и rollup.

Эти инструменты называются модулями связками, поскольку они сканируют модули JavaScript в поисках import операторов и объединяют все модули в один большой файл.

Модули CommonJS и ES вместе с npm произвели революцию в том, как мы пишем JavaScript, и сегодня было бы немыслимо написать программный проект без их использования.

К сожалению, ни один из поддерживаемых браузеров не поддерживает модули CommonJS или ES. До настоящего времени.

Собственные модули ES

Начиная с прошлой недели, все 4 основных браузера - Safari, Firefox, Edge и Chrome - начали изначально поддерживать модули ES. Это означает, что если вы script src импортируете модуль, он ПРОСТО РАБОТАЕТ. Что ж, на данный момент он будет «просто работать» только в версиях «для разработчиков / канарейек / технологий / инсайдеров».

Моя мечта приближается! Но я был настроен скептически. Во всех примерах, которые я видел в различных сообщениях в блогах, 2–3 модуля пытаются импортировать друг друга. Но реальность сегодня такова, что приложение может включать десять или даже сотни модулей одновременно - каждый очень маленький, правда, но тем не менее сотни из них.

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

Тестирование собственных модулей ES

К счастью, мне не пришлось создавать модуль, который импортирует десятки других модулей: такой модуль уже есть. Более того - это ОЧЕНЬ известный модуль. Это лодаш. Большинство людей не знают, но по какой-то причине у команды lodash есть пакет в npm, в котором lodash импортирует другие модули sub-lodash (например, pick и map, compose и debounce и другие) с использованием синтаксиса модулей ES. И есть много модулей sub-lodash.

Итак, все, что мне нужно было сделать, это создать HTML-страницу с тегом script, который импортирует lodash, и опробовать его. Это работает? Сравнима ли производительность загрузки нескольких модулей с использованием нескольких HTTP-запросов с загрузкой одного большого пакета?

Я был даже готов опробовать это с помощью HTTP / 2, поскольку HTTP / 2 НАМНОГО лучше справляется с несколькими небольшими ресурсами из одного домена. А затем проверить, как поддержка HTTP / 2 push сделает его еще лучше. Как мы увидим, я не попал туда, потому что, ну… вот увидите.

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



Чтобы опробовать их, вам нужно будет запустить npm install, затем npm run build и, наконец, npm start, чтобы запустить сервер узлов, который обслуживает HTML и JS. Если вы перейдете на http://localhost:3000/es6-modules.html, вы получите страницу, которая пытается импортировать lodash, и console.log-s результат:

Код загружает es6-module-1.js. Это модуль, который загружает lodash, который, в свою очередь, загружает все маленькие подмодули lodash.

Итак, давайте попробуем на Chrome Canary. Если вы тоже хотите попробовать, вам нужно перейти к chrome://flags и включить «Экспериментальные функции веб-платформы».

Собственные модули ES в Chrome Canary (v60)

Так что я попробовал. Каково же было мое изумление, когда я получил такой результат:

benchmark: 18484.576904296875ms

Ага - загрузка всех этих модулей заняла более 18 секунд.

Если мы попробуем использовать ссылку на этой странице - webpack modules - мы получим:

benchmark: 191.23876953125ms

И эта страница загружает тот же код, что и предыдущая, но в одном файле. Да, и файл не такой уж и большой - около 1 КБ. Но это занимает около 200 миллисекунд. Намного меньше 18 секунд.

Так что же происходит? Почему Chrome так медленно загружает модули ES? Это множество HTTP-запросов? Поможет ли здесь HTTP / 2, распараллеливая все HTTP-запросы? Нет. Я заставил все запросы к серверу возвращать бесконечную дату истечения срока действия (используя HTTP-директивуmaxage) и попытался снова - результат тот же.

Итак, если браузер не выполняет никаких HTTP-запросов при включенном кешировании, что же происходит? Что он делает? Согласно вкладке «Сеть» в Chrome, файлы JS были загружены (при кешировании) через 3 секунды (что по-прежнему много). Но что он делает в течение 15 секунд после этого?

Я попытался выяснить, что он делает, используя вкладку «Производительность»:

Я обнаружил, что примерно через 6 секунд он практически не делает ничего до 20-й секунды - эти маленькие желтые линии, разбросанные вокруг, представляют собой просто сборку мусора DOM.

Так что же заставляет его работать так медленно? Без понятия. Это не проблема сети, как мы убедились, включив кеширование. но я все равно пробовал HTTP / 2, используя https://localhost:3001/es6-modules.html. Тот же результат! теперь браузер загружает модули параллельно, но время все еще составляет около 20 секунд.

Это было очень разочаровывающе - я действительно хотел оптимизировать выборку HTTP / 2, чтобы загрузка модулей была почти такой же быстрой, как загрузка пакетов, но с такой производительностью нет смысла экономить время в сети, потому что браузер делает что-то еще после загрузки модулей JS.

Но теперь мне стало любопытно. А как насчет других браузеров?

Собственные модули ES в Firefox Developer Edition (v54)

В основном тот же результат:

benchmark: 11749.47ms

Ой, подожди, нет. На самом деле все еще хуже. Я запускал его еще несколько раз и постоянно получал такие числа:

benchmark: 31409.71ms

Более того, при загрузке страницы браузер как бы зависает! Производительность намного хуже, чем в Chrome.

А как насчет сафари?

Собственные модули ES в Safari Technology Preview (v29)

Намного лучше! Без кеширования я получаю следующий результат:

benchmark: 2826.892ms

И с кешированием:

benchmark: 1583.700ms

Конечно, это даже не сравнимо с производительностью объединенной версии, которая занимает 200 мс без кеширования и 40 мс кэширования. Но это огромное улучшение по сравнению с Chrome и Firefox. Это напоминает историю о бедной семье, раввине и козле. Сафари избавился от козы.

И все же возникает тот же вопрос - что делает Safari 1,5 секунды, если все файлы находятся в кеше?

Резюме

Chrome, Firefox и, в меньшей степени, Safari демонстрируют одно и то же поведение при загрузке модулей ES изначально - что-то заставляет браузеры долгое время бездействовать после завершения сетевой загрузки модулей. И это нечто делает время загрузки страницы, использующей множество модулей ES, очень большим: около 18 секунд для Chrome, более 30 секунд для Firefox и около 2 секунд для Safari.

Я не мог понять, что это за что-то. Может быть, кто-нибудь может направить этот пост в блог различным разработчикам браузеров? Хотелось бы понять, что это такое!

Одно можно сказать наверняка - я абсолютно уверен, что это временное явление, и разработчики браузеров оптимизируют это. И когда они это сделают, я продолжу и оптимизирую сетевую часть, используя HTTP / 2 и другие технологии.

Но это для другого сообщения в блоге.

Обновлять

Люди призывали меня создавать отчеты об ошибках, поэтому вот они:

Хакерский полдень - это то, с чего хакеры начинают свои дни. Мы часть семьи @AMI. Сейчас мы принимаем заявки и рады обсуждать рекламные и спонсорские возможности.

Если вам понравился этот рассказ, мы рекомендуем прочитать наши Последние технические истории и Современные технические истории. До следующего раза не воспринимайте реалии мира как должное!