javascript

Использование вулканизации для polymer-модулей

  • вторник, 1 августа 2017 г. в 03:14:01
https://habrahabr.ru/post/333660/
  • Разработка веб-сайтов
  • JavaScript


polymerjs vulcanize

Проекте, над которым я сейчас работаю, имеет виджетоподобную клиентскую архитектуру. Причем виджеты системы могут использовать любую библиотеку для своей реализации, например, ReactJS, PolymerJS, VueJS, d3JS и другие. Несколько виджетов системы реализованы, как раз, как вэб-компоненты на базе PolymerJS.

Поэтому предлагаю вашему вниманию один из подходов для оптимизации polymer-виджетов.



Содержание:


1. Описание проблемы
2. Какие сложности возникают?
3. Как их можно решить?
4. Библиотека vulcanize-polymer-module
4.1. Структура
4.2. Описание bower.json
4.3. Описание package.json
4.3.1. Установка утилит
4.3.2. Настройка RollupJS
4.4. vulcanize-utils.js
5. Выводы

1. Описание проблемы


Одним из главных проблем polymer-приложений — это множественная подгрузка, используемых компонентов и всех зависимых компонентов, которые в свою очередь, могут состоять из вспомогательных стилей,
behavior-ов, подгружаемых так же дополнительно. В результате консоль в network-разделе будет «засыпана» данными файлами. В виду всего этого, первая загрузка такого виджета может быть достаточно долгой, в зависимости от количества используемых составных вэб-компонентов.
Для этих целей в polymer-приложениях применяется вулканизация. Подразумевается, что у данного приложения, есть точка входа в виде, например, index.html, в котором разворачивается главный компонент-контейнер, например <App/>. В данном файле подключается само ядро polymer и файл компонента-контейнера <App/> и далее иерархически подключаются все используемые компоненты, которые сами являются отдельными html-файлами. Сам процесс вулканизации заключается в «склеивании» всех используемых компонентов и ядра polymer в один файл, который и будет в итоге являться точкой входа для index.html.

2. Какие сложности возникают?


  1. Первой сложностью является то, что у меня не polymer-приложение, а несколько составных компонентов(назовем их умными компонентами — УК), которые обернуты в виджет системы, то есть нет единой точки входа.
  2. Второй сложностью — что в течении работы с приложением может быть и не вызвана вовсе страница с данными виджетами, и соответсвенно ни один из polymer-компонентов будет просто не нужен в текущей сессию работы, не говоря уж о самом ядре polymer.
  3. Третьей — один УК использует один набор атомарных (paper-, iron- и другие) компонентов(назовем их глупыми компонентами — ГК), а другой — другой набор. Причем могут быть пересечения, то есть два разных УК используют одни и те же ГК.


3. Как их можно решить?


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

4. Библиотека vulcanize-polymer-module


Все выше сказанные тезисы я оформил в виде библиотеки vulcanize-polymer-module.
Хочу рассказать о ней подробней.

4.1. Структура

vulcanize-polymer-module/
├── imports.html
├── vulcanize-utils.js
├── rollup.config.js
├── bower.json
└── package.json


4.2. Описание bower.json

В нем мы описываем все ГК, которые нам необходимы, как зависимости, включая так же само ядро polymer.
Например, раздел dependencies может выглядеть так:
dependencies
"dependencies": {
    "polymer": "Polymer/polymer#^2.0.0",
    "polymer-redux": "^1.0.0",
    "iron-flex-layout": "PolymerElements/iron-flex-layout#^2.0.0",
    "paper-button": "PolymerElements/paper-button#^2.0.0",
    "paper-badge": "PolymerElements/paper-badge#^2.0.0",
    "paper-icon-button": "PolymerElements/paper-icon-button#^2.0.0",
    "paper-input": "PolymerElements/paper-input#^2.0.0",
    "paper-item": "PolymerElements/paper-item#^2.0.0",
    "paper-checkbox": "PolymerElements/paper-checkbox#^2.0.0",
    "paper-tabs": "PolymerElements/paper-tabs#^2.0.0",
    "paper-listbox": "PolymerElements/paper-listbox#^2.0.0",
    "iron-a11y-keys": "PolymerElements/iron-a11y-keys#^2.0.0",
    "iron-list": "PolymerElements/iron-list#^2.0.0",
    "iron-icons": "PolymerElements/iron-icons#^2.0.0",
    "paper-progress": "PolymerElements/paper-progress#^2.0.0",
    "vaadin-split-layout": "vaadin/vaadin-split-layout#^2.0.0",
    "vaadin-grid": "^3.0.0",
    "iron-pages": "PolymerElements/iron-pages#^2.0.0",
    "iron-collapse": "PolymerElements/iron-collapse#^2.0.0",
    "iron-overlay-behavior": "PolymerElements/iron-overlay-behavior#^2.0.0",
    "vaadin-context-menu": "^3.0.0"
  }




Так как я совместно с polymer использую redux, я включил библиотеку
polymer-redux.

4.3. Описание package.json

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

scripts
"scripts": {
    "build": "rollup -c",
    "vulcanize": "vulcanize imports.html  --inline-scripts --inline-css --strip-comments",
    "run-vulcanize": "npm run vulcanize > imports.vulcanize.html",
    "vulcanized": "vulcanize imports.html  --inline-scripts --inline-css --strip-comments | crisper  --html imports.vulcanized.html --js imports.vulcanized.js > imports.vulcanized.html",
    "html-minifier": "html-minifier imports.vulcanized.html --remove-optional-tags --collapse-whitespace --preserve-line-breaks -o imports.vulcanized.min.html",
    "build-all": "npm run vulcanized && npm run build && npm run html-minifier"
  }


Команды, в порядке и приоритетности их использования:
  • build-all — основная команда, которая и запускает весь процесс вулканизации.
  • vulcanized — выполняет саму вулканизация, то есть объединение всех компонентов и ядра в один файл, затем разбивает всю сборку отдельно на .js и .html файлы.(Используется утилита vulcanize и crisper)
  • build — очистка кода js-файла от комментариев.(используется RollupJS)
  • html-minifier — минификация html-файла.(используется html-minifier)


4.3.1. Установка утилит

Как видим используется множество дополнительных утилит, которые нам необходимо предварительно установить в систему.
Установка утилит
npm install -g vulcanize
npm install -g crisper
npm install -g html-minifier


4.3.2. Настройка RollupJS

Так как rollup используется только для очистки js-кода я использую только один плагин к нему-
rollup-plugin-cleanup. Плагин rollup-plugin-progress используется для визуализации процесса сборки.
rollup.config.js
import progress from 'rollup-plugin-progress';
import cleanup from 'rollup-plugin-cleanup';

export default {
	entry: 'imports.vulcanized.js',
	dest: 'imports.vulcanized.js',

	plugins: [
		cleanup(),
		progress({
		}),
	]
};


4.4. vulcanize-utils.js

Для решения второго требования был написан утилитарный метод loadVulcanized, который загружает УК, но перед этим загружает вулканизированный файл, причем делает это один раз и в случаи повторного вызова загружает только сам УК.
Рассмотрим подробнее его параметры.
loadVulcanized = function(url, urlVulcanized, controller, html, store)
  • url — путь к умному компоненту. Является обязательным.
  • urlVulcanized -путь к вулканизированной сборке. По умолчанию — путь к данной сборке — ../vulcanize-polymer-module/imports.vulcanized.min.html
  • controller — в моем случаи это контроллер системного виджета. Опционально.
  • html — html-объект умного компонента. Имеет смысл, если задан контроллер.
  • store — redux store. Опционально.



5. Выводы


Конечно можно использовать
polymer-cli с параметром build, но при сборке с его помощью подразумевается, что билдится polymer-проект, а так как мы используем компоненты не в одном контейнере <App/>, то собирать каждый УК придется по отдельности и файлы сборки будут иметь дублирование ядра polymer и составных ГК. Поэтому подход, описанный в статье имеет достаточную эффективность в системах использующих несколько UI-библиотек совместно, за счет единой точки входа для всех УК на базе polymer.
Один из возможных минусов можно рассмотреть, как избыточность ГК в вулканизированном файле, так как в нем собраны все ГК всех УК, используемых
в системе, но при этом не все УК могут быть использованы за сессию работы, в виду чего не все ГК будут использованы в загруженом вулканизированном файле.
Так же, как небольшое неудобство, можно рассмотреть тот факт, что после добавления нового компонента необходимо запустить сборку заново, после чего сделать обновление репозитория(push), а другие пользователи должны обновить данную библиотеку через bower update.
Несмотря на все это данная библиотека решает свою задачу в данном проекте, а значит может пригодится и кому-то еще.
Так что форкайте, милости просим.