Посмотрите, как можно использовать модули ES6 и Service Workers для запуска веб-приложения без Webpack или Rollup.

Вступление

🇮🇹 Также доступно на итальянском языке

Спецификация ECMAScript 2015 (ES6) вывела JavaScript и веб-разработку в новую эру, основанную на чистом синтаксисе, улучшенном шаблоне исходных файлов и наборе инструментов разработчика, доступных в других контекстах программирования, таких как статический анализ кода , системы зависимостей, автозаполнение и многое другое.

За все это пришлось заплатить: для запуска веб-приложения в браузере теперь могут потребоваться сотни узловых модулей, наблюдатель для обнаружения изменений файлов и утомительных перестроек исходного кода. Потерянные несколько секунд могут легко превратиться в часы в неделю, пустая трата дискового пространства, чрезмерное потребление ОЗУ и ЦП, а для медленно работающих компьютеров - вентиляторы, вращающиеся, как авиационные двигатели.

Тематическое исследование

Как разработчик, я хотел бы создать классическое приложение To-Do List, используя библиотеку компонентов с поддержкой JSX, и запускать его в браузере без необходимости запускать сборщик, транспиллер и наблюдатель.

😱😱😱

Допустим, мы все любим Preact (как и должно быть) - получение только шаблона (и его зависимостей) означает загрузку ~ 1.500 узловых модулей, ~ 26000 файлов. , и требует общего дискового пространства около 200 МБ. Большинство этих зависимостей используется для запуска веб-сервера, запекаемого Webpack.

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

Как загрузить приложение ES6 / 7 / X в браузер без сборщика

Начиная с Chrome 62, Edge 16, Safari 11 и Firefox 54, можно импортировать модуль ES6 в браузер:

Когда анализатор HTML встречает этот тег, он выбирает указанный исходный файл и рекурсивно разрешает все операторы import и export.

Хотя эта новая функция прекрасна, ее недостаточно для запуска современного и сложного веб-приложения. Браузеры могут разрешать только относительные зависимости, и у них нет информации о зависимостях NPM. Кроме того, использование JSX вызовет несколько синтаксических ошибок, потому что это не стандарт языка JavaScript.

Сервисные работники спешат на помощь

Когда все кажется потерянным, нам на помощь приходят сервисные работники. Эти специальные рабочие после регистрации могут перехватывать сетевые запросы и обрабатывать их ответы.

Итак, мы можем использовать SW для:

  • перехватывать запросы JavaScript;
  • извлекать файлы;
  • найдите не относительныеimport операторы и переназначьте их в папку node_modules;
  • обнаруживать синтаксис JSX и транспилировать все выражения JSX в JavaScript;
  • вернуть измененные файлы в основной поток.

На этом этапе браузер должен иметь возможность обрабатывать файлы JavaScript и разрешать их зависимости.

Подробнее о сервис-воркерах в MDN: Использование сервис-воркеров

Разработка Unchained

Unchained - это доказательство концепции, размещенное на GitHub, которое претворяет эту идею в жизнь. Подобно пакетам и транспиляторам, которые мы уже используем, он был разработан для разделения процесса на этапы и является подключаемым.

Он предоставляет несколько полезных помощников для регистрации Service Workers, polyfill для поддержки dynamic import, а его ядро ​​(аналогично Rollup API) может разрешите импорт файлов JavaScript, выполнив следующие действия:

  • Преобразование: для изменения исходного кода и преобразования нестандартного синтаксиса;
  • Решение: найдите и разрешитеimport утверждения;
  • Завершение: возвращает сгенерированный код.

Все обновления окончательного пакета выполняются с помощью Babel Standalone, дистрибутива Babel, который запускается в браузере и позволяет подключаемым модулям обрабатывать абстрактное синтаксическое дерево (AST) файла напрямую, отслеживая изменения и генерируя окончательную исходную карту.

Следующие плагины Unchained уже доступны (и на данный момент довольно простые):

  • common: заменяет выражения CommonJS, такие как require иmodule.exports, операторами ES6;
  • babel: взаимодействует с Babel Standalone и его плагинами для транспиляции нестандартных синтаксисов, таких как JSX, Flow или Typescript (!);
  • текст: преобразует текстовые файлы в модули ES6;
  • json: конвертирует файлы JSON в модули ES6;
  • env: выполняет внедрение переменных среды (передается с использованием объекта JavaScript, поскольку браузеры не имеют прямого доступа к фактическим переменным env);
  • resolve: частичная реализация алгоритма разрешения узла.

Более того, Unchained использует интерфейс кеширования браузера для пропуска уже разрешенных файлов. Используя заголовок ETag, он также может обнаруживать изменения, поэтому перезагрузка страницы и внедрение кода происходят чертовски быстро.

Сделайте разработчика счастливым

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

Настройка проекта

Для начала нам понадобится всего две зависимости:

npm init -y
npm install preact unchained-js
# 2 modules, 65 items, < 2MB

и следующие файлы:

  • index.html: где зарегистрирован Service Worker и файлы приложения импортируются.
  • sw.js: Service Worker, который необходимо зарегистрировать, он использует библиотеку Unchained для транспиляции исходного кода.
  • index.js: основной файл JavaScript

Теперь просто запустите локальный сервер (здесь установлен глобально):

npm install -g http-server
http-server .

и… готово!

В консоли Unchained регистрирует все файлы, импортированные при запуске: позже он будет использовать кеш для последующих перезагрузок.

При каждом изменении файла, например если мы отредактируем метку кнопки в todolist.component.js и сохраним, браузер будет перезагружать только этот файл.

Git-проект этого примера доступен здесь.

Выводы

Несмотря на то, что поддержка браузеров ограничена Chrome и Firefox (для этого он недоступен по умолчанию, но вам нужно включить флаг dom.moduleScripts.enabled), я думаю, что этот подход может упростить структуру проекта и его конфигурацию:

  • значительно сокращает количество устанавливаемых зависимостей;
  • транспиляция не блокирует основной поток пользовательского интерфейса, потому что все происходит в контексте Service Worker;
  • после первого запуска обновление исходного кода происходит очень быстро;
  • разделение кода с помощью динамического import() просто работает;
  • Поддержка исходных карт.

Но есть и минусы:

  • первый запуск неэффективен (из-за отсутствия прямого доступа к файловой системе и необходимости создавать AST для каждого отдельного файла);
  • отсутствует дрожание деревьев, функция, доступная в Rollup и Webpack 4.

Настоящий тест

В приложении To-Do List всегда легко, но как насчет реальных примеров? Я заменил Rollup на Unchained в сложном проекте, над которым я работаю, который включает множество зависимостей, таких как jQuery, Moment, CKEditor и библиотеку компонентов, со следующими результатами:

Будущее

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

  • ускорить первое выполнение, используя только преобразования AST (отключено, потому что на данный момент существует проблема с созданием исходных карт);
  • проверить, возможно ли интегрировать Турбо, когда он будет доступен, чтобы улучшить процесс разрешения не относительных путей;
  • расширить совместимость с браузерами, которые не поддерживают модули Service Workers или ES6;
  • расширить поддержку среды узла для получения пакетов приложений, которые на 100% совместимы с предварительным просмотром в браузере;
  • blob (для загрузки изображений и шрифтов) и плагины postCSS.

Особая благодарность
Огромное спасибо xho и SteRosanelli за их терпение при рецензировании этой статьи.