Spec-Zone.ru › Lit 1

Сборка для production

При создании приложения с компонентами LitElement можно использовать распространённые инструменты сборки JavaScript, например Rollup или webpack.

Мы рекомендуем Rollup, поскольку он разработан для работы со стандартным форматом модулей ES.

Примеры конфигураций сборки с использованием Rollup см. в разделе Сборка с помощью Rollup.

Если вас интересует сборка с помощью другого инструмента или интеграция LitElement в существующую систему сборки, см. раздел Требования к сборке.

Сборка с помощью Rollup

Существует множество способов настроить Rollup для объединения проекта. В этом разделе описаны две базовые сборки:

  • Современная сборка для актуальных браузеров.
  • Универсальная сборка для браузеров вплоть до Internet Explorer 11.

Если вам нужно поддерживать только актуальные браузеры, можно использовать только современную сборку. Если вы хотите поддерживать максимально широкий спектр браузеров с помощью одной сборки, можно использовать обратно совместимую сборку.

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

В приведённых здесь примерах конфигурации используется демонстрационное приложение Shop. Все описанные конфигурации можно найти в ветке репозитория Shop:

  • https://github.com/Polymer/shop/tree/rollup-examples-v2

Сборка для современных браузеров

Для сборки современных браузеров используются следующие npm-пакеты:

  • rollup. Сборщик Rollup.
  • @rollup/plugin-node-resolve. Для разрешения спецификаторов модулей без пути. (Дополнительные сведения см. в разделе Спецификаторы модулей без пути.)
  • rollup-plugin-terser. Для минификации JavaScript. Это не является строго обязательным, но при сборке для production, вероятно, потребуется минифицировать JavaScript.
  • rollup-plugin-copy. Для копирования статических ресурсов в папку сборки.
  • rollup-plugin-minify-html-literals. Необязательная оптимизация.

Файл конфигурации Rollup для этой сборки выглядит следующим образом: \

import resolve from '@rollup/plugin-node-resolve';
import { terser } from 'rollup-plugin-terser';
import minifyHTML from 'rollup-plugin-minify-html-literals';
import copy from 'rollup-plugin-copy';

// Static assets will vary depending on the application
const copyConfig = {
  targets: [
    { src: 'node_modules/@webcomponents', dest: 'build-modern/node_modules' },
    { src: 'images', dest: 'build-modern' },
    { src: 'data', dest: 'build-modern' },
    { src: 'index.html', dest: 'build-modern' },
  ],
};

// The main JavaScript bundle for modern browsers that support
// JavaScript modules and other ES2015+ features.
const config = {
  input: 'src/components/shop-app.js',
  output: {
    dir: 'build-modern/src/components',
    format: 'es',
  },
  plugins: [
    minifyHTML(),
    copy(copyConfig),
    resolve(),
  ],
  preserveEntrySignatures: false,
};

if (process.env.NODE_ENV !== 'development') {
  config.plugins.push(terser());
}

export default config;

Ещё более простой стартовый проект

Репозиторий rollup-starter-app содержит минимальный стартовый проект для создания приложения с помощью Rollup. Хотя в репозитории нет LitElement, в нём есть всё необходимое для объединения приложения LitElement. Если вы хотите использовать этот проект как образец для своего проекта, в первую очередь обратите внимание на файлы package.json и rollup.config.js.

Обратите внимание: помимо обязательных плагинов, rollup-starter-app включает плагин CommonJS — @rollup/plugin-commonjs. Этот плагин для LitElement не требуется, но может быть полезен, если вы хотите импортировать пакеты, распространяемые только в виде модулей CommonJS.

Универсальная сборка

Универсальная сборка компилируется в ES5 для старых браузеров (в первую очередь IE11) и использует System.js в качестве системы модулей (поскольку старые браузеры не поддерживают нативные модули JavaScript).

Для универсальной сборки требуются все пакеты, используемые современной сборкой, а также следующие:

Babel:

  • @babel/core
  • @babel/cli
  • @babel/preset-env

Полифилы, используемые Babel:

  • corejs
  • regenerator-runtime

Плагины Rollup:

  • rollup-plugin-babel
  • rollup-plugin-commonjs

Загрузчик модулей SystemJS:

  • systemjs

Если вы хотите просто посмотреть код, вот наиболее важные части универсальной сборки:

  • Конфигурация Rollup: https://github.com/Polymer/shop/blob/rollup-examples-v2/rollup-universal.js
  • index.html: https://github.com/Polymer/shop/blob/rollup-examples-v2/index-universal.html

В следующих разделах описаны особенности сборки.

Сборка Babel и пакет полифилов

В отличие от современной сборки, эта сборка создаёт два отдельных пакета: один для кода приложения, а другой — для полифилов, необходимых Babel. Многие конфигурации Babel создают один пакет, включающий как полифилы, так и код приложения. Однако это может вызывать проблемы с другими полифилами, включая полифилы Web Components. Отдельная загрузка полифилов Babel перед загрузкой полифилов Web Components позволяет обоим наборам полифилов работать правильно. Полифилы Babel предоставляются в виде отдельных модулей CommonJS.

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

Конфигурация Babel для этой сборки довольно небольшая. Она указывает Babel использовать плагин @babel/preset-env для компиляции кода, совместимого с Internet Explorer 11.

import babel from 'rollup-plugin-babel';
...

const babelConfig = {
  babelrc: false,
  ...{
    presets: [
      [
        '@babel/preset-env',
        {
          targets: {
            ie: '11',
          },
        },
      ],
    ],
  },
};

Пакет приложения

Конфигурация Rollup для пакета приложения похожа на конфигурацию сборки модели:

const configs = [
  // The main JavaScript bundle for older browsers that don't support
  // JavaScript modules or ES2015+.
  {
    input: ['src/components/shop-app.js'],
    output: {
      dir: 'build-universal/nomodule/src/components',
      format: 'systemjs',
    },
    plugins: [
      minifyHTML(),
      babel(babelConfig),
      resolve(),
      copy(copyConfig),
    ],
    preserveEntrySignatures: false,
  },

От современной конфигурации её отличают три основных момента:

  • В ней задаётся массив configs вместо одного объекта конфигурации.
  • Для основного пакета приложения используется другой формат (SystemJS вместо модулей ES).
  • Основной пакет приложения компилируется с помощью плагина Babel.

Пакет полифилов

Точкой входа для пакета полифилов Babel служит файл JavaScript, который импортирует два модуля CommonJS, а те, в свою очередь, импортируют несколько более мелких модулей:

import 'core-js/stable';
import 'regenerator-runtime/runtime';

Конфигурация Rollup объединяет все эти модули в один файл:

const configs = [

   ...

  // Babel polyfills for older browsers that don't support ES2015+.
  {
    input: 'src/babel-polyfills-nomodule.js',
    output: {
      file: 'build-universal/nomodule/src/babel-polyfills-nomodule.js',
      format: 'iife',
    },
    plugins: [commonjs({ include: ['node_modules/**'] }), resolve()],
  },
];

Загрузка всех компонентов

Файл index.html загружает все пакеты в правильном порядке:

  • Пакет полифилов Babel.
  • Загрузчик полифилов Web Components, который проверяет наличие необходимых возможностей и загружает все необходимые полифилы Web Components.
  • Загрузчик SystemJS.
  • Пакет приложения.

(В примере index.html также есть небольшой полифил для API fetch, специфичный для приложения.)

Если убрать дополнительные материалы, часть index.html, отвечающая за загрузку скриптов, будет выглядеть так:

<!-- Babel polyfills--need to be loaded _before_ Web
     Components polyfills -->
<script src="nomodule/src/babel-polyfills-nomodule.js"></script>

<!-- Load Web Components polyfills, if needed. -->
<script src="node_modules/@webcomponents/webcomponentsjs/webcomponents-loader.js"></script>

<!-- SystemJS loader -->
<script src="node_modules/systemjs/dist/s.min.js"></script>

<!-- Use SystemJS to load the application bundle -->
<script>
  System.import('./nomodule/src/components/shop-app.js');
</script>

Использование нескольких сборок

В идеале современным браузерам следует предоставлять пакет ES6, а старым — более крупный пакет ES5. На практике это может быть непросто. Вот три возможных способа предоставлять разные сборки разным браузерам:

  • Теги скриптов module и nomodule.

  • Проверка наличия возможностей на стороне клиента.

  • Дифференцированная раздача.

Эти подходы рассматриваются в следующих разделах.

Приём с module/nomodule

Поскольку браузеры, не поддерживающие модули, не выполняют модульные скрипты (<script type="module">), а браузеры, поддерживающие модули, не загружают скрипты nomodule (<script nomodule>), для подключения устаревших пакетов можно использовать nomodule. На практике браузеры с поддержкой модулей также поддерживают другие используемые LitElement возможности языка ES6 и не нуждаются в транспиляции.

Этот подход показан на примере приложения Shop. См. файл index-modnomod.html. Преимущество этого подхода в том, что для него не требуется никакая логика на стороне сервера.

Главный недостаток этого подхода заключается в том, что Edge версий 16–18 поддерживает модули JavaScript, но не поддерживает динамический импорт. Это не проблема, если приложение можно упаковать в один пакет. Однако если у вас большое приложение, использующее динамический импорт, и вам нужно поддерживать старые версии Edge, этот подход вам не подойдёт.

Ещё один недостаток: IE 11 (и, возможно, некоторые другие старые браузеры) может загружать пакеты <script type="module">, но не выполнять их, а Edge 16–18 может загружать пакеты <script nomodule>, но не выполнять их. Это снижает производительность в этих старых браузерах.

Проверка наличия возможностей на стороне клиента

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

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

Дифференцированная раздача

При таком подходе сервер использует заголовок запроса User-Agent, чтобы определить, какой пакет отправить браузеру. У этого подхода (также называемого «определением браузера») есть недостатки. Он менее точен, чем проверка возможностей на стороне клиента, поскольку опирается на статический список возможностей каждого браузера. Кроме того, некоторые браузеры могут отправлять неверный заголовок User-Agent. Однако у этого подхода обычно есть преимущество в производительности по сравнению с другими: браузер получает только необходимые ему пакеты.

Проект prpl-server — это веб-сервер Node.js, поддерживающий дифференцированную раздачу.

Требования к сборке

В этом разделе описаны требования к сборке приложений с использованием LitElement. Используйте его, если создаёте собственную конфигурацию сборки.

LitElement распространяется в виде набора модулей ES, написанных на современном JavaScript (ES 2017). Они нативно поддерживаются современными браузерами, например Chrome, Safari, Firefox и Edge. LitElement также использует спецификаторы модулей без пути, которые пока не поддерживаются ни одним браузером.

При сборке приложения с использованием LitElement система сборки должна выполнять следующие задачи:

  • Разрешать идентификаторы модулей без пути (или в стиле Node). LitElement использует спецификаторы модулей без пути.
  • Преобразовывать модули ES в другую систему модулей, если это необходимо для поддержки старых браузеров.
  • Транспилировать современный синтаксис JavaScript в ES 5, если это необходимо для поддержки старых браузеров.

Для старых браузеров также потребуется загрузить некоторые полифилы:

  • Полифилы Web Components
  • Полифил динамического импорта. Нужен браузерам (в частности, Edge версий 16–18), которые поддерживают статический импорт модулей ES, но не динамический.

Спецификаторы модулей без пути

LitElement использует спецификаторы модулей без пути для импорта модулей из библиотеки lit-html, например:

import {html} from 'lit-html';

В настоящее время браузеры поддерживают загрузку модулей только по URL-адресам или относительным путям, но не по коротким именам, указывающим, например, на npm-пакет. Поэтому система сборки должна обработать такие спецификаторы: либо преобразовать их в подходящие для модулей ES в браузере, либо создать на выходе модуль другого типа.

Webpack автоматически обрабатывает спецификаторы модулей без пути; для Rollup потребуется плагин (@rollup/plugin-node-resolve).

Зачем нужны спецификаторы модулей без пути? Они позволяют импортировать модули, не зная точного расположения пакетов, установленного менеджером пакетов. Предложение стандарта под названием Import maps позволит браузерам поддерживать спецификаторы модулей без пути. А пока такие спецификаторы импорта можно легко преобразовать на этапе сборки. Кроме того, существуют полифилы и загрузчики модулей с поддержкой карт импорта.

Поддержка старых браузеров

Для поддержки старых браузеров (в частности, Internet Explorer 11) потребуется выполнить несколько дополнительных действий:

  • Транспилировать современный синтаксис JavaScript в ES 5.
  • Преобразовать модули ES в другую систему модулей.
  • Загрузить полифилы.
    • Полифилы Babel.
    • Полифилы Web Components.

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

Транспиляция в ES5

Для Rollup, webpack и других инструментов сборки существуют плагины, позволяющие транспилировать современный JavaScript для старых браузеров. Наиболее распространённый транспилятор — Babel.

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

Если система сборки уже настроена, она может игнорировать папку node_modules при транспиляции. В таком случае мы рекомендуем изменить настройку, чтобы транспилировать LitElement и lit-html. Например, если вы используете плагин Babel для Rollup, конфигурация может выглядеть так, чтобы исключить папку node_modules из транспиляции:

exclude: [ 'node_modules/**' ]

Вместо этого можно задать правило, явно включающее папки для транспиляции:

include: [ 'src/**', 'node_modules/lit-element/**', 'node_modules/lit-html/**']

Babel использует набор вспомогательных функций и полифилов для реализации различных современных возможностей JavaScript. Babel может включить эти полифилы в код приложения. Однако это вызывает проблемы с полифилами Web Components. Чтобы избежать проблем, объединяйте полифилы Babel в отдельный пакет. Пример создания такого пакета с помощью Rollup см. в разделе Пакет полифилов, а пример index.html с порядком загрузки — в разделе Загрузка всех компонентов.

Почему нет сборки ES5? Пакет LitElement в npm не включает сборку ES5, поскольку современный JavaScript компактнее и, как правило, быстрее. При сборке приложения можно скомпилировать современный JavaScript, создав сборку (или сборки), точно соответствующую браузерам, которые нужно поддерживать.

Если бы LitElement включал несколько сборок, отдельные элементы могли бы зависеть от разных сборок LitElement, в результате чего браузеру приходилось бы загружать несколько версий библиотеки.

Преобразование модулей

При создании сборки для IE11 обычно используют один из трёх форматов вывода:

  • Без модулей (IIFE). Код объединяется в один файл, обёрнутый в немедленно вызываемое функциональное выражение (IIFE).
  • Модули AMD. Используется формат Asynchronous Module Definition; требуется скрипт загрузчика модулей, например require.js.
  • Модули SystemJS. SystemJS — это загрузчик модулей, который определяет собственный формат модулей. Он также поддерживает AMD, CommonJS и стандартные модули JavaScript.

Формат IIFE подходит, если весь код можно объединить в один файл. Чтобы использовать разделение кода в старых браузерах, таких как IE11, потребуется создавать файлы в формате модулей AMD или SystemJS.

В разделе Сборка с помощью Rollup универсальная сборка использует формат SystemJS. Пример загрузки основного модуля SystemJS:

https://github.com/Polymer/shop/blob/rollup-examples-v2/index-universal.html#L109

Полифилы

Помимо полифилов, необходимых конкретному приложению, потребуется загрузить полифилы Babel и Web Components.

Обратите внимание: полифилы Babel следует объединять отдельно от пакета приложения и загружать до полифилов Web Components. Это обсуждается в разделе Сборка Babel и пакет полифилов. Пример создания пакета полифилов Babel с помощью Rollup см. в разделе Пакет полифилов. Пример загрузки пакетов см. в разделе Загрузка всех компонентов.

Оптимизация

Для проектов LitElement полезны те же оптимизации, что и для других веб-проектов:

  • Объединение файлов (например, с помощью Rollup или webpack).
  • Минификация и оптимизация кода (Terser хорошо подходит для LitElement, поскольку поддерживает современный JavaScript).
  • Сжатие (например, gzip).

Кроме того, есть несколько дополнительных оптимизаций, более специфичных для LitElement:

  • Минификация шаблонных строк. Шаблоны lit-html определяются с помощью шаблонных строк, которые не обрабатываются стандартными HTML-минификаторами. Добавление плагина для минификации шаблонных строк может немного уменьшить размер кода. (Если только ваши шаблоны не очень большие, выигрыш от такой оптимизации будет небольшим.)
  • Исключение shady-render из сборки. Если вы поддерживаете только современные браузеры, можно исключить модуль shady-render, используемый для поддержки старых браузеров.

Примеры сборок, приведённые в разделе Сборка с помощью Rollup, включают большинство этих оптимизаций.

Минификация кода

Существует множество способов минификации кода. Terser хорошо работает с современным JavaScript, используемым в LitElement. Дополнительные сведения можно найти в подробном описании пакета terser.

Минификация шаблонных строк

Шаблоны lit-html определяются с помощью шаблонных строк, которые не обрабатываются стандартными HTML-минификаторами. Добавление плагина для минификации шаблонных строк может немного уменьшить размер кода. (Если только ваши шаблоны не очень большие, выигрыш от такой оптимизации будет небольшим.)

Для этой оптимизации доступны несколько пакетов:

  • Rollup: rollup-plugin-minify-html-literals
  • Webpack: minify-template-literal-loader

Исключение модуля shady-render из сборки

Если вы создаёте сборку только для современных браузеров, можно удалить встроенную поддержку LitElement для shady DOM — полифила shadow DOM, — уменьшив размер пакета примерно на 12 КБ.

Для этого настройте систему сборки так, чтобы она заменяла модуль shady-render базовым модулем lit-html, предоставляющим универсальную версию render. Это уменьшит размер пакета примерно на 12 КБ.

Для сборки Rollup:

  1. Установите плагин alias.

    npm i -D @rollup/plugin-alias
  2. Настройте плагин alias так, чтобы ссылки на модуль shady-render заменялись ссылками на основной модуль lit-html.

    alias({
      entries: [{
        find: 'lit-html/lib/shady-render.js',
        replacement: 'node_modules/lit-html/lit-html.js'
      }]
    }),

Для сборки webpack:

  • Добавьте следующую настройку resolve.alias в конфигурацию webpack:

    resolve: {
      alias: {
        'lit-html/lib/shady-render.js': path.resolve(__dirname, './node_modules/lit-html/lit-html.js')
      }
    },

TypeScript

Язык TypeScript расширяет JavaScript, добавляя типы и проверку типов. Компилятор TypeScript, tsc, компилирует TypeScript в стандартный JavaScript.

Хотя компилятор TypeScript можно запускать в рамках процесса объединения файлов, мы рекомендуем запускать его отдельно, чтобы создавать промежуточную версию проекта на JavaScript. Это связано с проблемами, возникающими при генерации кода для старых браузеров компилятором TypeScript. Мы рекомендуем настроить TypeScript для вывода современного JavaScript (целевая версия ES2017 и модули ES).

Например, если у вас есть файл tsconfig.json, добавьте следующие параметры для вывода современного JavaScript:

{
  "compilerOptions": {
    "target": "es2017",
    "module": "es2015",
    ...

Используйте эту промежуточную версию в качестве входных данных для инструментов сборки. Для поддержки старых браузеров используйте Babel, чтобы повторно скомпилировать промежуточную версию современного JavaScript в обратно совместимый JavaScript, как описано в разделах Транспиляция в ES5 и Преобразование модулей.

Редактировать эту страницу

© Google LLC
Licensed under the Creative Commons Attribution 3.0 Unported License.
https://lit.dev/docs/v1/tools/build/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API