Pull to refresh

SystemJS 0.20 — Совмещая с браузерными модулями

Reading time 6 min
Views 13K

Это перевод поста в блоге Гая Бедфорда — основного разработчика таких замечательных инструментов, как JSPM — менеджера пакетов для браузеров и NodeJS, который работает на основе его же детища SystemJS — асинхронного загрузчика JS модулей любых известных форматов, способного расправляться в том числе с циклическими зависимостями, и который, в свою очередь, основан на его же детище под названием es-module-loader, полифиле для загрузки ES модулей. Как я понимаю, автор довольно сильно переписал SystemJS в данном релизе, и об этом будет интересно почитать хабраюзерам.


SystemJS 0.20 только что зарелизился — это полная его переработка, а также коррекция спецификации, в то время как ES модули уже находятся прямо здесь, в браузерах.


SystemJS изначально был разработан ещё в 2013-м году для проекта jspm, в то время когда RequireJS был лидирующим загрузчиком модулей. Параллельно развивался ES6, а модули ES6 всё ещё казались нематериальным сном. Идея SystemJS была простой и убедительной: модули приходят в браузеры, так что нам надо иметь возможность загружать любой модуль в любое время из браузера, что дало бы очень простой процесс разработки.


С приходом ES модулей стало понятно, что RequireJS не собирается выдерживать испытание временем. Создавая свой загрузчик на основе ранней спецификации загрузчиков ES модулей, появилась возможность помочь получить ощутимую обратную связь о работе спецификации загрузчиков модулей, а также получить в конечном итоге стабильный загрузчик для того чтобы впоследствии работать с ним.


Почти 4 года спустя проект стал получать чуть меньше половины миллиона загрузок в каждый месяц.


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


В основном, SystemJS 0.20 представляет очередной релиз регулировки спецификации.


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


Выравнивание спецификации


Дорога практически чиста для <script type="module"> и синтаксиста динамического import(), спасибо авторам спецификации за эту тяжёлую работу.


SystemJS всегда представлял из себя следование за спецификацией загрузчика WhatWG, который, фактически, некоторое время находился в застое. То, что мы видели с <script type="module"> и динамическим import() — это плоды работы WhatWG загрузчика, но идущего из небольших фаз спецификации. Эти возможности всегда присутствовали на дорожной карте WhatWG и представляли собой постепенное уточнение функций загрузчика.


Отдельные функции, которые мы видели до сих пор, включают в себя:


  • Модульные скрипты: Описаны в спецификации WhatWG HTML, реализовано в Safari Preview и Edge Insider

  • Модульные воркеры: Описаны в спецификации WhatWG, реализуются в настоящее время

  • Синтаксис динамических импортов: Третий этап спецификации TC39, реализуются в настоящее время

Другие возможности от спецификации загрузчика WhatWG, или то, что до сих пор обсуждается, но может появиться в будущем, включают в себя:


  • Метаданные импорта: Возможность получить путь к текущему модулю, что-то вроде __filename в NodeJS

  • Хуки резолвера: Возможность перехватить браузерный механизм определения скрипта. В настоящее время, конструкция import 'jquery' завершится с ошибкой для скрипта модуля, соответствующего спецификации загрузки модулей WhatWG. Хук может добавить возможность сказать браузеру resolve('lodash') -> 'https://cdn.com/lodash/4.17.4/lodash.js'. Одно из огромных преимуществ такого подхода — это кеширование: локальный app.js, который импортирует lodash, может быть закеширован, вне зависимости от обновлений lodash.

  • API реестра: Предоставляет возможность проанализировать и изменить карту URL'ов для неймспейсов модулей. Позволяет указывать в реестре динамически создаваемые модули. API реестра также может включить горячую перезагрузку рабочих окружений (hot reload), если метаданные зависимостей доступны.

  • Хук инстанцирования: Это хук, который делает возможным перехватить процесс загрузки и указать, какой модуль должен быть возвращён по указанному URL. Это также включает поддержку устаревших модулей в загрузчике — возможность загрузить AMD или CommonJS модули.

Чего не хватает в вышеперечисленных функциях — это хуки "нормализовать, найти, получить, перевести и инстанциировать". Стало ясно, что этот оригинальный канон более не будет переработан или изменён.


SystemJS идёт очень близко с этой дорогой, так что удаление этих хуков представляет собой одно из ломающих совместимость изменений проекта — больше нет возможности поймать событие System.fetch или System.transate. Вместо System[System.constructor.resolve] и System.instantiate[System.constructor.instantiate] стали новыми только два хука.


Короче говоря, SystemJS 0.20 основан на упрощённых предположениях хука резолвера и API реестра в браузерах, основанных на спецификациях. Для получения более точных деталей об этих API, вы можете изучить проект загрузчика модулей на https://github.com/ModuleLoader/es-module-loader, который внимательно детализирует все эти решения спецификаций и компромиссы.


Совместимость с модулями NodeJS


Сторона NodeJS добилась значительного прогресса в работе над принятием пути модулей. Формат модулей SystemJS в значительной степени согласуется с данными направлениями, но здесь есть одна корректировка, которая представляет из себя одно из самых больших изменений в релизе, нарушающих совместимость.


Это изменение заключается в том, что именованные экспорты больше не будут разрешены при импорте модуля CommonJS из ES-модуля, и всё это обсуждается на https://github.com/nodejs/CTC/pull/60/files#diff-2b572743d67d8a47685ae4bcb9bec651R217


То есть, конструкция import { name } from 'cjs.js, где cjs.js — это модуль CommonJS — больше не будет поддерживаться, вместо этого потребуется написать import cjs from 'cjs.js'; cjs.name. Это будет жёсткий разрыв, влияющий на пользователей NodeJS и Babel во многих местах, поэтому SystemJS принимает этот удар прямо сейчас.


Мы будем продолжать поддерживать флаг __esModule в Interop, позволяя поднимать именованные экспорты для подобных ситуаций.


Так что, если модуль cjs.js написан следующим образом:


exports.__esModule = true;

exports.name = function () {  ... }

То это даст возможность получить import { name } from 'cjs.js', даже если cjs.js будет являться модулем CommonJS, хотя в долгосрочной перспективе это также в итоге будет упразднено.


Билды для продакшена


Так как проект сократился в размерах, новый продакшн-билд SystemJS был разработан для оптимальной загрузки и производительности на продакшене.


В окружении разработки, загрузчик имеет дело с проблемой загрузки неизвестных модулей, т.е. мы имеем проблему конфигурации — как вы получите конфигурацию пакета, который ещё не загружен? И по этой причине SystemJS получил полноценную систему управления конфигурацией, включающую в себя возможность динамической подгрузки конфигураций для файлов в браузере как часть разрешения, почти как package.json файлы, которые загружаются в NodeJS.


Удаление всех этих удобств разработки сокращает продакшн билд до всего 5KB. Этот объём обеспечивает загрузку модулей System.register и System.registerDynamic, плюс содержит функци базового URL, путей к модулям, карт, контекстных карт, бандлов и поддержку кеша зависимостей.


Изоморфные браузерные модули — polyfil-like подход


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


К примеру, вы можете пожелать предоставить ES модули для браузеров с поддержкой ES модулей,
в то же время загрузчик SystemJS будет работать в старых браузерах на основе полифилов.


Я ссылаюсь на этот подход как на изоморфные браузерные модули, потому что с использованием System.register как с форматом модуля, версия полифила может поддерживать точную семантику выполнения ES модуля в поддерживаемых браузерах.


Был составлен демонстрационный пример на https://github.com/guybedford/isomorphic-browser-modules, основанный на продакшн билде SystemJS 0.20, который демонстрирует работу этого механизма загрузки ES модулей в Safari Technology Preview и возврат обратно на SystemJS, когда нативные модули не поддерживаются.


Улучшения модульных подходов разработки


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


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


Смотрите мои последние беседы на конференции с DotJS, где я веду дискуссию о техниках оптимизации сборки для браузеров, поддерживающих ES модули.


Спасибо всем, кто поддерживал и вкладывался в развитие SystemJS. Если у вас есть желание поддержать проект любым способом — у нас всегда есть пространство для работы, в том числе денежные пожертвования также приветствуются.

Tags:
Hubs:
+8
Comments 4
Comments Comments 4

Articles