Кто я

Я — Александр Кашеверов. По образованию — магистр радиофизики. По профессии — веб-разработчик, работаю в компании DataArt с 2011 года, с 2009 увлекаюсь IT и веб-технологиями.

О чем статья, коротко

Рассмотрим, что такое веб-компоненты и polymer. Немного поразмышляем на тему развития веба. Посмотрим на технические детали, примеры, поддержку браузерами, тестирование. Коротко, понятно, по делу. С картинками.

Вступление

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

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

Для уменьшения головной боли хорошо бы, если в контексте веб:

  • CSS не пересекался.
  • Области видимости JS не пересекались.
  • HTML был понятным и читаемым, никаких лишних элементов.


В настоящее время это не совсем так. Относительно JavaScript: приходится следить, чтобы он не работал с global scope и разные компоненты случайно не взаимодействовали друг с другом. В CSS нет и не было инкапсуляции, приходится придумывать методологии вроде MCSS и bem, чтобы ничего лишний раз не сломалось. И наконец, в HTML можно увидеть множество лишних элементов на странице, например, даже сейчас в почте gmail…



Запутанно… Не правда ли?
Фреймворки в той или иной степени внедряют компонентизацию и стараются решить эти задачи. Однако, они так же построены на всё тех же HTML, CSS, JavaScript.

Веб-компоненты — коротко

Веб медленно, но верно движется к компонентизации — это естественный процесс. Сложное можно упростить, разбив на части. Параллельно развитию фреймворков идет работа и на более низком уровне. Набирают популярность веб-компоненты. Это реализация идеи компонентизации на уровне браузера.

История веб-компонентов началась в 2011 году (первое упоминание на github). Кратко — что это:

  • Custom Elements — создание своих html-элементов, дополнение существующих.
  • Shadow DOM — инкапсуляция логики и стилей.
  • Templates — шаблоны на уровне браузера!
  • HTML imports — вместо подключения отдельно разных файлов [css, js, ...] можно подключать один HTML-документ, включающий все остальные файлы.


При использовании этих четырех технологий вместе, получаются автономные переиспользуемые блоки — веб-компоненты.

На самом деле, некое подобие веб-компонентов уже было создано давно. Простейший пример — элемент <select> . Для него есть отдельный тег. Взаимодействуя с элементом, появляется выпадающий список и эта логика скрыта от нас, к тому же у него есть свои собственные стили.

<select>
   <option value="Yandex">Yandex</option>
   <option value="Google" selected>Google</option>
   <option value="Yahoo">Yahoo</option>
</select>




Так же и с HTML5 элементами <video> или <audio> .

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

Наверняка многие подключали в проект Bootstrap — для этого нужно отдельно прописать подгрузку стилей и скриптов. С компонентами это можно было бы сделать проще.
<link rel="import" href="bootstrap.html">


Веб-компоненты — не фреймворк. Это набор технологий, реализованных на уровне браузера.

Веб-компоненты — поддержка браузерами & polyfills

На данный момент (ноябрь 2015) три из четырех технологий находятся в стадии «Working Draft» на W3C.
Так выглядит ситуация на данный момент (ноябрь 2015):



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

С их помощью, поддержка браузерами уже получше.



На веб-странице полифиллы подключается стандартно в head (до всех скриптов, которые используют веб-компоненты):
<script src="path/webcomponents.js"></script>


Веб-компоненты — ссылки



Polymer

Тем временем, Google всё упрощает и развивает. Работа началась осенью 2012 года, судя по истории коммитов на github. Они взяли веб-компоненты, полифилы и создали еще одну надстройку.

Цель Polymer — упростить создание качественных веб-приложений (цитата product manager с конференции Google IO).

Polymer — коротко

Библиотека представляет собой веб-компоненты в основе, полифилы для поддержки старых браузеров, это всё обернуто в целостную более удобную систему с добавлением сахара.

Схематически это выглядит так:


Google создал набор готовых компонент и разделил их на логические части:
 

Основные строительные блоки
<iron-icon>, <iron-input>, <iron-list>, …

Элементы на основе iron-elements и material design
<paper-input>, <paper-button>, <paper-tooltip>, ...

Коллекция веб-компонентов, использующих различные API Google
<google-analytics>, <google-chart>, <google-map>, …

Элементы, специфичные для интернет-коммерции
<gold-cc-input>, <gold-phone-input>, <gold-email-input>, ...

Элементы для создания анимаций
<neon-animation>

Элементы, помогающие создать из веб-страницы настоящее приложение
<platinum-bluetooth-device>, <platinum-sw-cache>, <platinum-push-messaging>, ...

Элементы-обертки других библиотек/приложений
<marked-element>

 

Можно заметить, что постоянно фигурирует слово «элемент». Google считает, что для всего можно сделать элемент. «There is an element for that» («для этого есть элемент») — звучит, как слоган в докладах Google IO 2015.

Да, даже для ajax запроса есть элемент. Выглядит так:
<iron-ajax url="some url" handle-as="json" on-response="onResponse"></iron-ajax>


При создании приложений мы сталкиваемся с примерно одинаковыми задачами, и Google предлагает набор готовых строительных блоков для этого. С какой бы проблемой мы не столкнулись — для ее решения есть элемент. «There is an element for that».
Создан каталог готовых Polymer-компонентов.

Polymer — не фреймворк, как и веб-компоненты — не фреймворк. Polymer — это обертка и сахар. Я бы идейно ее сравнил с jQuery. jQuery создана для работы с DOM, а Polymer — для работы с веб-компонентами.

Polymer — тестируемость

Да, он тестируемый, и для этого создана отдельная утилита web-component-tester. Понятное описание.

Пробовал. Работает (при установке могут возникнуть проблемы с Java и Selenium).

Несколько шагов, чтобы заработало:
  • Установить
    npm install -g web-component-tester
    

    Написать тест, просто html файл (по-умолчанию, в папке `./test`):
    <!doctype html>
    <html>
    <head>
      <meta charset="utf-8">
      <script src="path/webcomponentsjs/webcomponents.min.js"></script>
      <script src="path/web-component-tester/browser.js"></script>
      <link rel="import" href="path/awesome-element.html">
    </head>
    <body>
      <awesome-element id="fixture"></awesome-element>
      <script>
       suite('<awesome-element>', function() {
          test('is awesomest', function() {
            assert.isTrue(document.getElementById('fixture').awesomest);
          });
        });
      </script>
    </body>
    </html>
    


    Запустить тесты:
    wct
    


    Polymer — поддержка браузерами

    Polymer — сахар веб-компонента, поэтому и поддержка браузерами точно такая же. Таблицы были представлены выше.

    К слову, у меня так и не получилось запустить на старом Android-телефоне.

    Polymer — оптимизация vulcanize

    HTML imports позволяет быстро и удобно подключить документ в другой документ, но в этом удобстве скрывается и проблема производительности: создается множество http-запросов. Решение есть —- соединить все подключаемые файлы в один. Для этого служит утилита vulcanize.

    Установка:
    npm install -g vulcanize
    

    Использование:
    vulcanize index.html > build.html
    


    Polymer — starter kit

    Для удобства быстрого создания проектов есть утилита Polymer Starter Kit. После установки сразу доступно множество полезной функциональности.
    Собрано в коробку:
    • Polymer-, Paper-, Iron- и Neon-элементы.
    • Material Design-верстка.
    • Роутинг с Page.js.
    • Юнит тесты с помощью Web Component Tester.
    • Поддержка оффлайн-работы с помощью Platinum Service Worker.
    • Создание билда (включая Vulcanize).
    • ES2015-поддержка.


    Polymer — альтернативы

    Polymer — лишь надстройка над веб-компонентами. Есть и другие подобные продукты:


    Polymer — ссылки



    Polymer — про взаимодействие с библиотеками

    • Angular 2
    • Веб-компоненты будут внедрены в Angular 2: angularjs.blogspot.ru (eng).
    • React
      Официальный представитель от React Sebastian Markbage говорит, что они не будут использовать веб-компоненты совместно с React, т. к. они идеологически разные (декларативный React против императивных веб-компонентов): docs.google.com (eng). Однако, React и веб-компоненты совместимы, в некоторых случаях есть польза: youtube.com (eng).
    • Backbone
      Удобно скрестить с уже готовыми компонентами (Polymer, X-Tags, Bosonic). Используются точно так же, как обычные HTML-элементы. webcomponents.org (eng).


    Веб-компоненты и Polymer — примеры

    Рассмотрим простой пример веб-компонента и его Polymer-реализацию:

    Пример чистого веб-компонента:

    <template>
        <span id="multiplier"></span> * <span id="multiplicand"></span> = <span id="result"></span>
    </template>
    
    <script>
    
        (function(window, document) {
            var ownerDocument =  (document._currentScript || document.currentScript).ownerDocument;
            var template = ownerDocument.querySelector('template').content;
            var elementPrototype = Object.create(HTMLElement.prototype);
            var multiplier = 0, multiplicand = 0, result = 0;
            elementPrototype.createdCallback = function() {
                var shadowRoot = this.createShadowRoot();
                var clone = document.importNode(template, true);
                shadowRoot.appendChild(clone);
                this.multiplier = shadowRoot.querySelector('#multiplier');
                this.multiplicand = shadowRoot.querySelector('#multiplicand');
                this.result = shadowRoot.querySelector('#result');
                this.calculate();
            };
    
            elementPrototype.attributeChangedCallback = function(attr, oldVal, newVal) {
                this.calculate();
            };
    
            elementPrototype.calculate = function(){
                multiplier = parseFloat( this.getAttribute('multiplier') );
                multiplicand = parseFloat( this.getAttribute('multiplicand') );
                this.multiplier.textContent = multiplier;
                this.multiplicand.textContent = multiplicand;
                this.result.textContent = multiplicand*multiplier;
            };
            window.MyElement = document.registerElement('element-multiplier', {prototype: elementPrototype });
        })(window, document);
    
    </script>
    


    Подключаем в <head>
    <link rel="import" href="element-multiplier.html">
    


    Используем на странице (в <body> )
    <element-multiplier multiplier="3" multiplicand="2"></element-multiplier>
    


    С Polymer будет выглядеть заметно понятней и проще:

    <link rel="import" href="polymer.html">
    
    <dom-module id="element-multiplier">
        <template>
            <span>{{multiplier}}</span> * <span>{{multiplicand}}</span> = <span>{{result}}</span>
        </template>
    </dom-module>
    
    <script>
        Polymer({
            is: "element-multiplier",
            properties: {
                "multiplier"    : { type: Number, value: 0, observer: 'calculate' },
                "multiplicand"  : { type: Number, value: 0, observer: 'calculate' },
                "result"        : { type: Number, value: 0 }
            },
            ready: this.calculate,
            calculate: function(){
                this.result = this.multiplier*this.multiplicand;
            }
        });
    </script>
    


    Подключаем в <head>
    <link rel="import" href="path/element-multiplier.html"><script src="path/webcomponents.min.js"></script>
    


    Используем на странице (в <body> )
    <element-multiplier multiplier="3" multiplicand="2"></element-multiplier>
    


    Примеры:
    • pubnub.com — (eng) создание приложения на Polymer.
    • translate.google.com — создан на Polymer.
    • todomvc.com — (eng) пример TODO MVC на Polymer.
    • pubnub.github.io — (eng) анонимный чат на Polymer.
    • github.com — (eng) мое маленькое приложение. Идея проста: когда группа людей делает совместную покупку с одинаковыми вкладами, часто возникает путаница в расчетах долгов, когда скидываются не все сразу. Писал специально, чтобы попробовать Polymer. Там настроены тесты и vulcanize, реализована минификация JS в один файл.


    Спасибо!

    Успехов! Если есть ошибки/замечания/дополнения — пишите.

Комментарии (13)


  1. ilinsky
    04.11.2015 17:48
    +3

    Хорошая вводная статья. Пара замечаний — первое: если в браузере нет Shadow DOM, то его ничем не заменить и не заполнить: в дереве документа будет присутствовать именно то, что видно в листинге примера с GMail, пересечения CSS не побороть, ну и, понятное дело, скоупы исполнения не изолировать. Второе: технологии компонентизации существовали в браузерах задолго до Web Components, так например в IE5.5 (1999 год) впервые появилась технология HTML Behaviors, а уже в IE6.0 — HTC (HTML Components) (RIP 2013), параллельно с чем в движке Gecko развивалась другая технология компонентизации — XBL (RIP 2012).


    1. k12th
      04.11.2015 18:44
      +1

      если в браузере нет Shadow DOM, то его ничем не заменить и не заполнить

      Плюс весят все эти неполноценные полифиллы совершенно безбожно.


      1. imashaman
        05.11.2015 01:07

        они так же безбожно лажают под OSX и iOS (визуально), но то что вселяет уверенность — уже есть первые коммиты в WebKit, правда я не удивлюсь если авторы будут с домена chromium.org.
        Основная сложность — это сделать компонент без поддержки shadow CSS.


    1. auine
      05.11.2015 09:55

      Если в браузере нет Shadow DOM, то у нас есть Shady DOM про который к сожалению не слово в публикации. Да инкапсуляция теряется, но если ваше дев окружение Shadow, то все это интерпретируется на Shady идентично. Хоть и фактически нет инкапсуляции и прочих плюшек.
      Публикация — habrahabr.ru/post/259187


    1. some_x
      05.11.2015 10:10

      Почему же тогда авторы полифилов заявляют что они реализуют shadowDOM. Что именно они тогда полифилят?


      1. auine
        05.11.2015 11:06

        Shadow который либо имплементирован медленно, особенно на моб. девайсах, либо не имплементирован вовсе. Shady полифилит Shadow.

        авторы полифилов заявляют что они реализуют shadowDOM

        Что именно они тогда полифилят?

        Вроде же логично, что :)


  1. i360u
    04.11.2015 18:18
    +1

    www.code-labs.io — очень рекомендую для первого знакомства. Уроки по работе с Полимером — в секции «web». По моему скромному мнению, веб-компоненты — это лучшее, что появилось для работы с интерфейсами веб-приложений за последнее время.


    1. easterism
      05.11.2015 00:50

      Вот он, современный web. Ссылку сразу в нужное место уже и не сделать.


      1. i360u
        05.11.2015 09:04

        www.code-labs.io/polymer-summit — можно и так, но по первой ссылке материалов не очень много и они, помимо работы с Полимером, очень интересны, поэтому изначально дал ссылку на все.


        1. easterism
          05.11.2015 09:55
          +1

          За все ссылки спасибо.
          Я имел ввиду другое, но ирония не передалась :) На code-labs в принципе нельзя получить ссылку на какую-либо секцию. В некоторых сайтах это просто раздражает.


  1. kresh
    05.11.2015 00:16
    +2

    HTML template в Edge обещают уже внедрить. https://wpdev.uservoice.com/forums/257854-microsoft-edge-developer/suggestions/6261273-template-element


  1. k0t
    05.11.2015 16:41

    В копилку ссылок — customelements.io. Агрегатор кастомных элементов.


  1. Punk_UnDeaD
    09.11.2015 13:41

    В полимер главное, как в танке, очень аккуратно выбирать имена для properties, войдёте в конфликт с родными свойствами DOM элемента — всё сломается.