В Rollup недавно была добавлена ​​экспериментальная функция разделения кода, начиная с версии 0.55.0 (https://github.com/rollup/rollup/pull/1841). Я обновил правило Bazel rules_nodejs для производственного бандлинга rollup_bundle, чтобы использовать эту функцию, и в этом сообщении в блоге описывается, как это правило работает с разделением кода.

Разделение кода позволяет разделить код приложения на различные пакеты. Их можно загружать по запросу (отложенная загрузка) или параллельно. Такой подход может значительно сократить время загрузки вашего приложения. При использовании с Angular, как в этой демонстрации, части вашего приложения, которые не нужны во время начальной загрузки, могут быть загружены лениво, когда пользователь переходит к определенным маршрутам, или предварительно загружены в фоновом режиме после запуска исходного приложения.

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

Заявление об ограничении ответственности

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

Код

Демонстрация построена на ответвлении канонического angular-bazel-примера Алекса Игла из этой ветки: https://github.com/gregmagolan/angular-bazel-example/tree/rollup-code-splitting.

Обновленное правило rollup_bundle взято из моей вилки rules_nodejs: https://github.com/gregmagolan/rules_nodejs/tree/rollup-code-splitting.

Конфигурация

Конфигурация правила rollup_bundle не сильно меняется при разделении кода.

Раньше это было:

rollup_bundle(
    name = "bundle",
    entry_point = "src/main",
    deps = ["//src"],
)
nodejs_binary(
    name = "prodserver",
    args = ["./src"],
    data = [
        "index.html",
        ":bundle",
        ":zone.js",
    ],
    entry_point = "http-server/bin/http-server",
)

Rollup создаст минифицированный файл пакета, и цель prodserver будет обслуживать его и все другие файлы, необходимые с использованием http-сервера. Вы можете опробовать текущий rollup_bundle без разделения кода на каноническом примере angular-bazel-example Алекса Игла.

При разделении кода конфигурация обновляется следующим образом:

rollup_bundle(
    name = "bundle",
    index_html_template = "index.template.html",
    entry_points = [
        "src/main",
        "src/foo/foo.module.ngfactory",
    ],
    deps = ["//src"],
)
nodejs_binary(
    name = "prodserver",
    args = ["src"],
    data = [
        ":bundle",
        ":zonejs",
    ],
    entry_point = "history-server/modules/cli.js",
)

Вы заметите, что entry_point был переименован в entry_points и теперь принимает массив точек входа, первая из которых является начальной точкой входа для приложения. Кроме того, index.html больше не является статическим файлом, предоставляемым prodserver, а скорее шаблонным файлом. Правило rollup_bundle расширит этот шаблон необходимой конфигурацией systemjs, необходимой для разделения кода, и выведет файл index.html, который обслуживается целью prodserver.

Точки входа с отложенной загрузкой (src/main, src/foo/foo.module.ngfactory) необходимо пока настроить вручную в правиле rollup_bundle. В будущем они будут автоматически извлекаться из метаданных, созданных во время сборки.

Цель prodserver также была обновлена ​​для использования сервера истории вместо http-сервера, поскольку он поддерживает ленивую загрузку с помощью маршрутизатора Angular.

Использование

В моей ветке с разделением сводного кода в angular-bazel-example рабочий сервер запускается с bazel run src:prodserver.

Bazel соберет пакеты разделения кода и запустит prodserver, который прослушивает порт 8080.

INFO: Running command line: dist/bin/src/prodserver src
history-server listening on port 8080; Ctrl+C to stop

В Chrome devtools, когда вы перейдете по адресу http: // localhost: 8080, вы увидите, что браузер загружает zone.min.js и system.js. Эти сценарии необходимы для запуска приложения. Затем он загружает main.js скрипт, который является основной точкой входа в приложение, которое было объединено путем объединения. main.js запросит требуемый общий фрагмент chunk1.js, который также загружается.

На данный момент у приложения есть все необходимое для запуска главной страницы, но оно еще не загрузило скрипт для маршрута `/ foo`. Только когда вы нажимаете ссылку Foo, Angular запрашивает foo.module.ngfactory.js скрипт, необходимый для этого маршрута. Затем браузер загружает foo.module.ngfactory.js, и Angular переходит к маршруту /foo. Для пакета foo.module.ngfactory.js также требуется общий фрагмент chunk1.js, но, поскольку он уже был загружен браузером, он не запрашивается.

Обратите внимание, что поведение будет другим, если вы вручную измените URL-адрес на http: // localhost: 8080 / foo вместо того, чтобы щелкнуть ссылку Foo, которая сообщает Angular изменить маршрут на /foo. В этом ручном случае браузер выполняет полную перезагрузку, и все сценарии перезагружаются в том же порядке, и приложение обновляется.

Глубокое погружение

Ленивая загрузка конфигурации в index.html

Шаблонный index.template.html, переданный в rollup_bundle, содержит тег TEMPLATED_rollup_scripts. Правило rollup_bundle расширяет этот тег до:

<script src="/system.js"></script>
<script>
  (function (global) {
    System.config({
      packages: {
        '' : {
          map: {"./main": "bundles.es5_min/main", "./foo/foo.module.ngfactory": "bundles.es5_min/foo.module.ngfactory"},
          defaultExtension: 'js'
        },
      }
    });
  })(this);
</script>
<script>
  System.import('main').catch(function(err){ console.error(err); });
</script>

Он добавляет эти теги сценария, которые настраивают systemjs в браузере для загрузки пакетов с отложенной загрузкой, когда они запрашиваются приложением, в сгенерированный index.html. Правило rollup_bundle также выводит файл system.js, поэтому он доступен для обслуживания prodserver.

Конфигурация накопительного пакета

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

Первый накопительный пакет запускается для исходных кодов скомпилированных ES6 приложений и выводит пакеты разделенного кода ES6 в папку bundles.es6 в вашей папке bazel-out. Следующий машинописный текст запускается для их понижения до ES5 в папке bundles.es5. Наконец, uglify запускается дважды, один раз без--beautify и один раз с --beautify, и он выводится в папки bundles.es5_min и bundles.es5_min_debug соответственно. Сгенерированный index.html настраивается правилом для использования вывода bundles.es5_min, который не является минифицированным продуктом без отладки.

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

Накопительный пакет в настоящее время не обеспечивает контроль над именами выходных файлов. Они будут соответствовать именам файлов точки входа: main.js и foo.module.ngfactory.js в демонстрации angular-bazel-example. Rollup выводит общий код между пакетами точек входа в один или несколько chunkX.js файлов. В примере angular-bazel есть только один общий блок: chunk1.js. В более крупных приложениях было бы больше общих фрагментов, при этом каждый пакет точек входа загружал бы только те общие фрагменты, которые ему требуются.