Polub bloga na fejsie!

Podstawy Polymera czyli wstęp do Web Components

5

Dobra, starczy już tych wpisów podsumowujących! Dziś proponuję Ci trochę więcej „mięcha”… Na chwilę odejdziemy jednak od mojej ulubionej tematyki ReactJS. Przedstawię dziś bowiem podstawy Polymera i będzie to jednocześnie wprowadzenie do zagadnienia Web Components. Oprócz dzisiejszego wpisu, temat ten poruszę w tym tygodniu jeszcze w dwóch kolejnych wpisach. Być może jeden z nich przyjmie formę video…

Skąd mi się w ogóle wziął pomysł na wpis przedstawiający podstawy Polymera? No cóż, miałem przez chwile okazję pracować w tej technologii dla klienta firmy, w której ostatnio pracowałem. Nie powiem, nie byłem szczególnie szczęśliwy porzucając React na rzecz Polymera i Web Components. Z drugiej strony była to jednak okazja do wypróbowania czegoś z czym wcześniej nie miałem do czynienia… A poza tym dzięki temu mam teraz temat na blog posta!

No dobra, dość „pitolenia” – przechodzimy do rzeczy!

Kilka słów na temat Web Components

No cóż… Web Components to nie jest żadna super nowość w świecie web developmentu. Ogólnie rzecz biorąc jest to koncepcja re-używalnych kawałków kodu (komponentów), które zawierają szablon widoku HTML i które mogą być następnie zaimportowane w innych komponentach. Po zaimportowaniu web komponentu można używać go w kodzie tak, jakby był on zwykłym tagiem HTML. Główną intencją twórców tej koncepcji było stworzenie z Web Components standardu obsługiwanego przez wszystkie przeglądarki internetowe. Dzięki temu nie byłoby potrzebne użycie zewnętrznych bibliotek.

Niestety dobrymi chęciami jest piekło wybrukowane (czy jakoś tak). Na tę chwilę Web Components nie są oczywiście w pełni wspierane przez wszystkie przeglądarki. Z tego powodu, jeśli chcemy wykorzystać w projekcie Web Components, zmuszeni jesteśmy skorzystać z polyfilli (biblioteka implementująca coś czego nie potrafi przeglądarka). Zwykle używa się zestawu polyfilli o nazwie webcomponentsjs. W sumie nie wiem czy jest jakaś alternatywa na rynku…

Oto jak polyfille webcomponentsjs opisane są na stronie projektu:

A suite of polyfills supporting the Web Components specs:

  • Custom Elements: allows authors to define their own custom tags.
  • HTML Imports: a way to include and reuse HTML documents via other HTML documents.
  • Shadow DOM: provides encapsulation by hiding DOM subtrees under shadow roots.

This also folds in polyfills for MutationObserver and WeakMap.

Powyższy opis pokazuje, że dzięki webcomponentsjs, jesteśmy w stanie korzystać ze wszystkich funkcji komponentów webowych, we wszystkich przeglądarkach internetowych.

Czym jest Polymer?

Ok, tyle na temat komponentów webowych. Czas przejść do głównego tematu tego wpisu jakim są podstawy Polymera… Ale czym właściwie jest Polymer?

No więc… jest to biblioteka JavaScript, która zbudowana jest w oparciu o Web Components API. Poza wszystkimi zaletami jakie dają nam komponenty webowe, Polymer dostarcza dodatkowy zestaw narzędzi, które czynią tworzenie własnych komponentów łatwiejszym. Dla przykładu: biblioteka ta dostarcza deklaratywną składnię, która pomaga w definiowaniu struktury elementów, nadawaniu im styli CSS oraz dodawaniu zachowań tworzonych w języku JavaScript. Innym przykładem usprawnień Polymera dla komponentów webowych jest dostarczanie mechanizmu data-bindingu. Ale o tym akurat napiszę więcej w kolejnym wpisie.

Plik index.html

No dobra, tyle na temat nudnej teorii. Czas wreszcie trochę „pokodzić”… Jak wiadomo, najlepiej zacząć od przykładu typu „Hello world” i taki właśnie komponent stworzymy za chwilę. Najpierw musimy jednka utworzyć plik index.html i załadować polyfille:

Jak widzisz, jedyne co trzeba zrobić, to załadować plik projektu webcomponentsjs.

Pierwszy własny komponent

Ok, stwórzmy więc teraz prosty komponent. Aby to zrobić, musimy stworzyć nowy plik, niech to będzie powiedzmy hello-world.html (tak, web komponenty to zwyczajne pliki HTML):

W pierwszej linii powyższego przykładu zobaczyć możesz, że dokonujemy „importu” komponentu polymer.html. Trzeba to zrobić w każdym komponencie, który tworzysz. Możesz też w ten sam sposób importować również inne, w tym własne, komponenty webowe. Dzięki takiemu właśnie importowi, możliwe jest ich użycie w innych komponentach. Zresztą pokażę to jeszcze za moment.

Kolejna interesująca sprawą, którą możesz zauważyć jest to, że cała implementacja komponentu znajduje się wewnątrz tagu dom-module. Jest on dla nas dostępny właśnie dzięki zaimportowaniu wcześniej komponentu polymer.html. Nazwa tworzonego przez nas komponentu przypisana jest do atrybutu id elementu dom-module. Zwróć uwagę na jej zapis: używamy tutaj myślników, ponieważ nazwa ta będzie później używana jako nazwa tagu HTML reprezentującego nasz komponent.

POLUB BLOGA NA FACEBOOKU!

Chcesz być na bieżąco informowany o nowościach na blogu oraz innych ciekawych treściach? Polub fanpage bloga na Facebooku!

Idźmy dalej… Wewnątrz tagu dom-module znajduje się element template. Zawiera on szablon widoku naszego komponentu. Jak przystało na wpis przedstawiający podstawy Polymera, nasz komponent ma jedynie wyświetlić tekst „Hello world!”. Dlatego też szablon ten zawiera jedynie akapit z tym właśnie tekstem.

W dalszej części komponentu znajduje się element script. W naszym bardzo prostym przykładzie zawiera on tylko to co każdy web komponent zawierać musi. Jak więc widzisz, mamy tutaj wywołanie konstruktora Polymer, do którego przekazujemy obiekt. Obiekt ten zawiera właściwość is, do której przypisana jest nazwa naszego komponentu. Dzięki przekazaniu takiego obiektu do konstruktora Polymer następuje rejestracja naszego komponentu w Polymerze. Obiekt ten może zawierać więcej właściwości o czym piszę więcej w dalszej części tego wpisu.

Użycie komponentu w praktyce

Skoro komponent jest już przygotowany, czas użyć go w praktyce. Można to zrobić chociażby w pliku index.html, co też uczyniłem – poniżej jego nowa wersja:

Jak widzisz, zaimportowałem komponent hello-world.html w nagłówku strony w ten sam sposób w jaki wcześniej importowałem komponent polymer.html. Jeśli spojrzysz teraz na body pliku index.html zauważysz, że użycie naszego komponentu sprowadza się do użycie go jak by był zwykłym tagiem HTML.

W tym momencie mamy już wszystko! Jeśli teraz otworzyłbyś plik index.html w przeglądarce, na ekranie powinien pojawić się napis „Hello world!”. Jeśli jesteś ciekawski to na pewno zajrzałeś do kodu wyrenderowanego przez przeglądarkę… Ja zrobiłem to już za ciebie – poniżej go prezentuję:

Oczywiście Polymer pododawał różnego rodzaju komentarze, style itp. Najważniejsze jest to co się stało w body. Jak widzisz, z pewnymi modyfikacjami, zawartość szablonu naszego komponentu została tutaj wstrzyknięta. Generalnie więc działa to tak, że wszystko finalnie trafia do głównego pliku aplikacji.

Przekazywanie zmiennych do komponentu

Z założenia miałem Ci jedynie przedstawić podstawy Polymera, mógłbym więc zakończyć ten wpis już w tym momencie. Pokażę Ci jednak jeszcze kilka istotnych zagadnień związanych z Polymerem. Taki dziś jestem dobry…

Na pierwszy ogień weźmy przekazywanie zmiennych do komponentu. Nie jest to nic trudnego – zmodyfikujmy co nieco nasz przykładowy komponent hello-world.html:

Po pierwsze, zajrzyj do skryptu. Jak widzisz, dodałem dodatkową właściwość do obiektu przekazywanego do konstruktora Polymer. Nazywa się ona properties i zawiera obiekt, który z kolei zawiera właściwość andWho. Jest to deklaracja zmiennej, jaką można przekazać do tego komponentu. Do właściwości andWho przypisujemy obiekt, który określa parametry tej zmiennej. W naszym przypadku definiujemy, że jest to zmienna o typie String. Więcej na temat możliwych parametrów w kolejnym wpisie (na temat data-bindingu).

Po drugie, spójrz teraz na definicję szablonu. Jak możesz łatwo zauważyć, użycie takiej zmiennej odbywa się podobnie jak w większości innych frameworków, za pomocą podwójnych nawiasów klamrowych. W przedstawiony sposób będziemy mogli przekazać do tego komponentu jakąś zmienną tekstową, która wyświetli się w zamiast „placeholdera” {{andWho}}.

Świetnie, to jak teraz przekazać tę wartość do komponentu? Bardzo prosto:

Proste prawda? Zwróć tylko uwagę, że w komponencie zmienną deklarowaliśmy w stylu „camelCase”. W atrybutach HTML stosujemy oczywiście inną konwencję, więc poszczególne słowa w nazwie oddzielamy myślnikiem. Polymer będzie wiedział o co chodzi.

Nadawanie stylów komponentom

Na koniec pokażę jeszcze jak można nadawać style własnym komponentom Polymera. Każdy komponent może mieć wewnętrznie własne style CSS (czy jest to lepsze rozwiązanie niż style definiowane w arkuszach, to temat na osobny wpis).

Dodanie definicji stylów do komponentu ogranicza się tak na prawdę do dodania elementu style do szablonu komponentu:

Nie ma tu za wiele do wyjaśniania, poza jednym. Zauważyłeś pewnie w stylach selektor :host p. Dzięki dodaniu :host wymuszamy aby dany styl dotyczył tylko tego konkretnego komponentu. Jeśli by go zabrakło, styl ten mógłby mieć wpływ również na inne komponenty.

Podstawy Polymera – podsumowanie

To tyle w temacie „podstawy Polymera”. To jednak nie koniec wpisów na temat tej biblioteki. W środę napiszę co nieco na temat data bindingu w Polymerze – zapraszam do czytania!

P.S. Prezentowany dziś przykład dostępny jest w moim repozytorium GitHub: https://github.com/burczu/polymer-introduction! Przeczytajcie też koniecznie komentarze pod tekstem – jest tam kilka wartościowych uwag dotyczących Polymera!

REACT, REDUX, REACT-ROUTER - KURSY ON-LINE

Chcesz od podstaw poznać tajniki React, Redux oraz react-router? Zapraszam do moich szkoleń on-line:

Przejdź do szkoleń

Uwaga! Obecnie trwa przedsprzedaż kursów - premiera 1 sierpnia 2017!

  • marioosh

    Przekonałem się do Polymera podczas długiej wędrówki w obecnym projekcie 😀 Dwie warte odnotowania rzeczy:
    1. Tak na prawdę nie musisz importować polymer.js w każdym komponencie, wystarczy w jednym z „rodziców”.
    2. Jeżeli definiujesz property bez żadnych dodatkowych opcji, domyślnych wartości czy obserwerów to możesz pominąć tworzenie obiektu i uprościć to do lakonicznego andWho: String, poza tym, jeżeli tego property nie będziesz używał z poziomu javascriptu, to nie musisz go deklarować w ogóle, a on i tak będzie widoczny w innym komponencie przez binding {{andWho}} 😀

    • hej! dzięki za komentarz i zwrócenie uwagi na niuanse – na pewno komuś może się to przydać! 😉

  • Polymer jest w miarę fajny do tworzenia komponentów, z tym, że mało ma wspólnego tak po prawdzie z Web Components – a zwłaszcza jego wersja 1.0. Sam się na tym sparzyłem swego czasu: http://tutorials.comandeer.pl/polymer.html

    Używanie Polymera z wersją lite polyfillu eliminuje największą zaletę nowego standardu: enkapsulację przy pomocy Shadow DOM. Zamiast tego wskakuje nam tutaj Shady DOM Polymera, który jest bardzo ograniczonym dalekim kuzynem (nawet nie odpowiednikiem). Do prawdziwego Shadow DOM-u nie wpływają żadne style ze strony. Do Shady DOM-u – a i owszem. Nie jest to przecież odizolowane pod-drzewko, na co wskazuje zrzut wygenerowanego HTML-a.

    > Dzięki dodaniu :host wymuszamy aby dany styl dotyczył tylko tego konkretnego komponentu.

    Nie. Dzięki temu wymuszany, żeby styl dotyczył konkretnego Shadow DOM, co w kontekście tego, co napisałem powyżej, brzmi po prostu śmiesznie.

    Pomijam już fakt, że HTML Imports zostało zaimplementowane tylko w Chrome i tylko tam będzie, bo reszta przeglądarek powiedziała temu rozwiązaniu „nie” i widzi rozwiązanie w modułach ES. A cały problem wczytywania w ten sposób komponentów potęguje fakt, że importy są z założenia synchroniczne (chyba że do link doda się [async]), co rozwałkowuje całkowicie system wczytywania zależności w przeglądarkach, które nie implementują HTML Imports (tam z konieczności importy lecą asynchronicznie, bo są rozwiązywane Ajaksem). Zresztą importy stosuje się niemal wyłącznie do wczytywania custom elementów – a to można rozwiązać dzisiaj o wiele, wiele bardziej elegancko z powodu nowości wprowadzonej w Custom Elements v1: customElements.whenDefined → https://www.w3.org/TR/custom-elements/#dom-customelementregistry-whendefined

  • ale tak serio….
    O co chodzi z tym polymerem. dla kogo to jest i co w tym fajnego dla programisty ?
    Jak na to patrzę to zgroza….
    Html, js i CSS w jednym pliku. I teraz jak mam dla przykadu kilka osób zajmujących w projekcie – jedna od styli , jedna od UX, jedną od Frontend/js to na gicie beda mieli ładne konflikty, nie?
    Potem kwestia taka: Sass-a wymyślono nie bez celu. pętle, mixiny itd są przydatne. No to tu mam się cofnąć do css-a czystego z jakimiś selektorami? Brzydko.

    Kolejna rzecz. Jak mam angulara, to dostaję komplet narzędzi. „Opędzluje” w nim routing, http, localStorage, animacje i co tam jeszcze. Tu co mam? Szumnie nazwane web components, czyli koncepcke starą w sumie. W formie standardu.

    Dalej… teraz mamy ecma 6 w polimerze 2.0 , ale tworzenie „klas” nie wyglada dobrze – to nie jest POJ(s)O.
    Kolejna słaba cecha to to „bindowanie” po stringach. Listenery przypisuje sie po stringach, jakieś eventy itd też po stringach. No lipa.

    Da sie tam korzystać z modułów w ogóle jakichś? Comon js czy coś? Jakiś webpack?

    Jak dla mnie to to jest słabe.
    Moze mi ktoś wytłumaczyć co jest fajengo w tym ?

    • Nie nazywaj Polymera „standardem”, bo to niestety nawet nie leżało obok standardu WC…

      > Html, js i CSS w jednym pliku.

      Można rozdzielić bez żadnego problemu. Ostatnio dodano obsługę zewnętrznych arkuszy stylów do Shadow DOM. Nie ma zatem żadnego problemu, by z tego skorzystać.

      > I teraz jak mam dla przykadu kilka osób zajmujących w projekcie – jedna od styli , jedna od UX, jedną od Frontend/js to na gicie beda mieli ładne konflikty, nie?

      Och, ale jakże to może sprawiać komukolwiek problemy – wszak teraz, w dobie Reacta, myśli się komponentami… 😉

      > Potem kwestia taka: Sass-a wymyślono nie bez celu. pętle, mixiny itd są przydatne. No to tu mam się cofnąć do css-a czystego z jakimiś selektorami? Brzydko.

      „Cofanie się” do CSS-a to po prostu bycie tool-independent, co raczej jest plusem niż minusem. Niemniej, jak już mówiłem, w WC można wydzielić CSS do osobnego pliku, więc używanie Sassa nie sprawia jakichkolwiek trudności.

      > Kolejna rzecz. Jak mam angulara, to dostaję komplet narzędzi. „Opędzluje” w nim routing, http, localStorage, animacje i co tam jeszcze. Tu co mam? Szumnie nazwane web components, czyli koncepcke starą w sumie. W formie standardu.

      Zacznijmy od tego, że pchanie WC do czegokolwiek innego niż UI, to jakieś grube nieporozumienie… Poza tym Polymer jest biblioteką, nie frameworkiem, więc tym bardziej nie musi (nie powinien?) mieć tego wszystkiego. Co nie znaczy, że nie stworzono w Polymerze/WC różnych komponentów typu router itd (coś w podobieństwie do Reactowego). Co do animacji: masz dostęp do CSS-a i JS-a + możliwość wydzielania wszystkiego do deklaratywnych komponentów… Przecież to idealne warunki do tworzenia animacji.

      > Dalej… teraz mamy ecma 6 w polimerze 2.0 , ale tworzenie „klas” nie wyglada dobrze – to nie jest POJ(s)O.

      A w czym POJ(s)O byłyby tutaj lepsze…? Każdy WC musi dziedziczyć po HTMLElement, a w Polymerze dziedziczy po Polymer.Element, które jest nakładką na ten natywny „interfejs”. I znów: wygląda to bardzo podobnie do Reactowych komponentów. Prościej się tego zrobić nie da.

      > Kolejna słaba cecha to to „bindowanie” po stringach. Listenery przypisuje sie po stringach, jakieś eventy itd też po stringach. No lipa.

      A tego nie bardzo rozumiem. Eventy można bindować jak w normalnym DOM (bo to jest normalny DOM, huh). Natomiast składnia Polymera do tego faktycznie ciut kuleje. Niemniej system szablonów jest już znośny.

      < Da sie tam korzystać z modułów w ogóle jakichś? Comon js czy coś? Jakiś webpack?

      Oczywiście, że się da. Czemu miałoby się nie dać? Zamiast webpacka, Polymer ma dedykowanego Vulcanizera.

      Niemniej osobiście wybrałbym mimo wszystko czyste WC (albo własny framework), bo Polymer ma kilka irytujących mnie decyzji projektowych. I jak mówiłem, WC są idealne do tworzenia UI, ale nie widzę sensu w pchaniu do nich logiki aplikacji (jak to robią wszystkie przykłady Polymera).

Google Analytics Alternative