Web komponente

Share

Poslednjih nekoliko godina je došlo do značajnih promena u principima funkcionisanja ali i u  tehnologijama korištenim za razvoj web aplikacija. Jedan od značajnih trendova jeste udaljavanje od standardnih template sistema na klijentskoj strani i korišćenje mehanizama za prikaz i manipulaciju elemenata koje nam nudi sam HTML. Dolazi od razvoja mnogobrojnih komponent modela kao samostalnih bibiloteka ali i kao deo javascript framework-a. Ovo su samo neki od najpoznatijih rešenja na sadašnjoj Front-End sceni:

  • AngularJS Directives – https://angularjs.org/
  • React – https://facebook.github.io/react/
  • Polymer – https://www.polymer-project.org/
  • Riot – http://riotjs.com/
  • Vue – https://vuejs.org/
  • Web Components – http://webcomponents.org/

Za komponent modele bi mogli da kažemo da se prvenstveno bave manipulacijom DOM-a, koja predstavlja jednu od najskupljih operacija, pa stoga oni predstavljaju jedno od najvažniji delova web aplikacija.

Šta su web komponente?

Web komponente predstavljaju skup standarda ili tehnologija koji omogućavaju kreiranje HTML elementa i njihovo korišćenje u okviru web stranica i aplikacija.  Web komponente jesu deo W3C specifikacije što ih čini standardom za implementaciju novih elemenata.

Ovde imamo jednostavan primer elementa koji prikazuje text ograničene dužine (text-length) i na kraju stavlja tri tačke ako je text duži od definisanog.

HTML

<text-truncation text-length="100">
    <p>Curabitur non nulla sit amet nisl tempus convallis quis ac lectus. Vestibulum ac diam sit amet quam vehicula elementum sed sit amet dui. Cras ultricies ligula sed magna dictum porta. Quisque velit nisi, pretium ut lacinia in, elementum id enim. Curabitur non nulla sit amet nisl tempus convallis quis ac lectus.</p>
</text-truncation>

Njihov cilj jeste da olakšaju pravljenje i korišćenje novih biblioteka i funkcionalnosti.

Većina nas se do sada susretala sa problemom ubacivanja nove biblioteke u projekat a koja sadrži  nekoliko resursa i svaki od tih resursa je trebalo povezati sa glavnim dokumentom na kome želimo da prikažemo novu funkcionalnost.

Ovo bi bili koraci za standardnu implementaciju biblioteke:

  • prvo treba ubaciti stilove
  • nakon toga treba ubaciti javascript, minimalno jedan file ako biblioteka ne zavisi od drugih biblioteka.
  • često biblioteke imaju i određene slike koje treba da su dostupne javascriptu i stilovima
  • nakon svega toga treba postaviti HTML strukturu komponente
  • i na kraju treba komponentu inicijalozovati pozivanjem definisanih metoda

Korišćenjem web komponenti implementacija se značajno uprošćava i svodi na samo dva koraka:

  • ubacivanje html file-a u zaglavlje dokumenta
  • i postavljanje HTML strukture gde želimo da prikažemo komponentu

Tehnologije koje web komponente koriste?

Kao što je u prethodnom delu spomenuto, web komponente su sačinjene od nekoliko tehnologija:

  • Custom elements
  • HTML Imports
  • Templates
  • Shadow DOM

U nastavku ćemo ukratko proći kroz svaku od njih.

Custom elements

Kao što imamo neke već definisane HTML elemente (<video>, <select>, <footer> itd) sada smo u mogućnosti da kreiramo svoje nove elemente al i da ih kasnije koristimo na drugim projektima. Novim elementima možemo da definišemo naziv, atribute i sadržaj, čime u stvari komuniciramo sa API-om elementa.

Primer:

HTML

<my-element attribute="attr-value">
    ..content..
</my-element>

Prilikom definisanja elementa poželjno je da se koristi crta u nazivu kako bi se smanjila mogućnost preklapanja imena sa drugim elementima. Takodje neophodno je da naziv elementa i njegovih atributa odražava njihovu namenu i smisao.

HTML Imports

Ovo je deo web komponenti koji omogućava da se učitaju svi neophodni resursi definisani u jednom HTML fajlu. Pa tako možemo učitati sve neophodne resurse jedne biblioteke (css, js, slike itd) uključivanjem samo jednog HTML fajla. Implementacija se vrši jednostavnim stavljanjem <link> taga u zaglavlje dokumenta koristeći import za vrednost rel atributa i definisanjem putanje do html file.

Primer:

HTML

component.html

<link rel="stylesheet" href="css/style.css">
<script src="js/script.js"></script>

page.html

<link rel="import" href="component.html" >
<script src="js/host-script.js"></script>

U primeru gore vidimo da u stranicu uključujemo resurse definisane u component.html fajlu i time ti resursi postaju raspoloživi na samoj stranici gde je izvršen import.

Prilikom korišcenja importa potrebno je imati u vidu sledeće:

  • Ukoliko je resurs linkovan iz više importa on će biti uključen samo jednom, pa tako ako više komponenti koristi jQuery on će samo jednom biti uključen.
  • Kao i kod Ajax poziva ukoliko import hoćemo da dohvatimo sa drugog domena on mora da bude CORS-enabled.
  • Import može da pristupi svom DOM-u ali i DOM-u stranice na koju je uključen.
  • Isto tako sa stranice možemo pristupiti DOM-u importovanog dokumenta.
  • Što se tiče izvršavanja skripti u importovanom fajlu se vrši kao da skripte korite defer atribut pa tako njihovo izvršavanje nastupa na kraju stranice kada je dokument spreman i to po definisanom redosledu.

Templates

Uključivanjem templejta na client strani oduvek je praćena određenim nedostatkom standarda pa su se u te svrhe koristili semantički neispravni tagovi kao što su <script> ili <textarea> tag kako bi se sprečilo izvršavanje code unutar tih tagova. Sada sa novim <template> elementom možemo da čuvano sadržaj koji neće biti renderovan ali će biti dostupan za korišćenje.

Primer:

HTML

<table id="producttable">
  <thead>
    <tr>
      <td>UPC_Code</td>
      <td>Product_Name</td>
    </tr>
  </thead>
  <tbody>
    <!-- existing data could optionally be included here -->
  </tbody>
</table>
Stari način
<script type="text/template" id="productrow">
  <tr>
    <td class="record"></td>
    <td></td>
  </tr>
</script>

Nov pristup
<template id="productrow">
  <tr>
    <td class="record"></td>
    <td></td>
  </tr>
</template>

Shadow DOM

Shadow DOM omogućava enkapsulaciju javascripta, stilova i templejta u web komponente tako da oni ostaju odvojeni od glavnog DOM-a stanice. Time mi  dobijamo mogućnost da odlučujemo kojim delovima komponente će krajnji korisnik moći da pristupi.

Potrebno je da razlikujemo tri glavna termina:

  • Light DOM
  • Shadow DOM
  • Composed DOM

Light DOM predstavlja početnu tačku, njega definiše korisnik koji želi da koristi komponentu. To je dakle inicijalna struktura komponente.

Primer:

HTML

Light DOM

<div class="custom-element">
  <span>Light DOM</span>
</div>

Shadow DOM je nova struktura ubačena nakon inicijalizacije a nevidljiva za krajnjeg korisnika. U primeru dole možete videti kako se dodaje struktura u Shadow DOM elementa. Najvažnija je createShadowRoot metoda kojom pristupamo i dodajemo nove elemente u Shadow DOM.

Shadow DOM

#shadow-root
 <span>Shadow DOM - red</span><br>
  <span class="green-span">Shadow DOM - green</span><br>

Enkapsulacija

var el = document.querySelector('.custom-element');
var shadow = el.createShadowRoot();
var inner = el.querySelector('span');
shadow.innerHTML += '<span>Shadow DOM - red</span><br>';
shadow.innerHTML += '<span class="green-span">Shadow DOM - green</span><br>';
shadow.innerHTML += '<content></content>';
shadow.innerHTML += '<style>span { color: red; }</style>';

Finalnu strukturu možemo da nazovemo Composed DOM, ona kao takva nije vidljiva krajnjem korisniku ali predstavlja rezultat koji je prikazan u pretraživaču.

Composed DOM

<div class="custom-element">
  <span>Shadow DOM - red</span><br>
  <span class="green-span">Shadow DOM - green</span><br>
  <span>Light DOM</span>
</div>

Moguće je stilizovati sve elemente definisane komponente. U ovom slučaju vidimo da je predefinisana boja Shadow elemenata crvena koja u jednom slučaju “pregažena” zelenom bojom korišćenjem “::shadow” selektor kako bi se pristupilo shadow elementima komponente. I na kraju vidimo da se Light DOM elementima može direktno pristupiti pošto su oni bili inicijalno dostupni u dokumentu.

Style:

.custom-element span{
  color:gray;
}
.custom-element::shadow .green-span{
  color:green;
}

Output:

Shadow DOM – red

Shadow DOM – green

Light DOM

Na ovom linku možete videti kompletan kod: https://jsfiddle.net/bpmh34uo/1/

Kreirajte svoju web komponentu

Sada pošto imamo sve potrebne delove možemo da krenemo sa pravljenjem svoje web komponente. Za primer ćemo koristiti <text-truncation> element koji smo gore već spomenuli.

Prvo kreirajte file text-truncation.html.

U okviru njega definišete templejt koje će te koristit u kompomenti.

U našem slučaju to će biti jednostavan template koji će da sadrži tri tačke ako je text predugačak.

HTML

<template>
    <span>...</span>
</template>

Dalje selektujemo lokalni dokument i dokument stranice na kojoj će se komoponenta koristiti. Bitno je napomenuti da se _currentScript uzima ako koristimo web compoments polyfil u suprotnom currentScript se koristi ako browser podržava HTML Imports.

JS

var hostDoc = document;
var thisDoc =  (hostDoc.currentScript || hostDoc._currentScript).ownerDocument;

Nakon toga preuzimamo sadržaj templejta koji ćemo koristiti da prikažemo tri tačke ako je tekst predugačak.

JS

var template = thisDoc.querySelector('template').content;

Inicijalizujemo element prototype pa sačuvavamo početnu dužinu texta i orginalni text ali i keširamo paragraf kako bi smo ga kasnije mogli koristiti.

JS

var TruncateTextEl = Object.create(HTMLElement.prototype);

TruncateTextEl.textLength = 100;

TruncateTextEl.paragraph;

TruncateTextEl.originalText;

Metoda createdCallback je standardni web komponent event i se poziva prilikom inicijalizacije kada se element kreira. U njoj prvo sačuvavamo paragraf element i orginalni text. Zatim pristupamo Shadow DOM-u i u njega smeštamo paragraf. Nakon toga vršimo inicijalno setovanje dužine texta.

TruncateTextEl.createdCallback = function() {
        this.paragraph = this.querySelector('p');
        this.originalText = this.paragraph.textContent;
       
        var shadowRoot = this.createShadowRoot();
        shadowRoot.appendChild(this.paragraph);
       
        if (this.hasAttribute('text-length')) {
            var textLength = this.getAttribute('text-length');
            this.setTextLength(textLength);
        } else {
            this.setTextLength(this.textLength);
        }

};

Metoda attributeChangedCallback je takođe standardan event koji se poziva kada dođe do promene atributa elementa. U našem slučaju proveravamo koji je atribut u pitanju i ako je text-length ponovo vršimo setovanje dužine text i njegovo skraćivanje ako je potrebno.

TruncateTextEl.attributeChangedCallback = function(attr, oldVal, newVal) {
    if (attr === 'text-length') {
        this.setTextLength(newVal);
    }
};

Ovako kreiranoj web komponenti možete pristupiti kao i svakom drugom HTML elementu što je posebno značajno sa aspekta fleksibilnosti. To znači da tako napisana komponenta može da se koristi svuda nezavisno od biblioteke i frameworka.

Primer:

Možete dinamički menjati dužinu teksta

var el = document.querySelector('truncate-text');
el.setTextLength(120);

Možete editovati atribut direktno:

el.setAttribute('text-length', 150);

Možete dinamički kreirati element:

var newEl = document.createElement('truncate-text');
newEl.setTextLength(120);
document.body.appendChild(newEl);

Integracija je vrlo jednostavna i podrazumeva uključivanje trunicate-text.html u vašu stanicu i definisanje Light DOM tj osnovne strukture komponent. Pored ovoga za sada na neophodno uključiti i webcomponents.js polyfil kako bi primer radio u pretraživačima koji ne podržavaju web komponente.

Primer:

HTML

<html>

  <head>
    <script data-require="webcomponentsjs@0.7.5" data-semver="0.7.5" src="https://cdnjs.cloudflare.com/ajax/libs/webcomponentsjs/0.7.5/webcomponents.js"></script>
    <link rel="import" href="truncate-text.html" />
    <script src="script.js"></script>
  </head>

  <body>
    <truncate-text text-length="150">
      <p>
        Quisque velit nisi, pretium ut lacinia in, elementum id enim.
        Donec rutrum congue leo eget malesuada. Vestibulum ac diam
        sit amet quam vehicula elementum sed sit amet dui. Curabitur
        arcu erat, accumsan id imperdiet et, porttitor at sem. Curabitur
        non nulla sit amet nisl tempus convallis quis ac lectus.
        Quisque velit nisi, pretium ut lacinia in, elementum id enim.
        Vivamus suscipit tortor eget felis porttitor volutpat. Curabitur
        arcu erat, accumsan id imperdiet et, porttitor at sem. Sed
        porttitor lectus nibh. Curabitur non nulla sit amet nisl tempus
        convallis quis ac lectus.
      </p>
    </truncate-text>
  </body>

Rezultat:

Quisque velit nisi, pretium ut lacinia in, elementum id enim. Donec rutrum congue leo eget malesuada. Vestibulum ac diam sit amet qu …

Kompletan primer zajedno sa integracijom možete da vidite ovde:

http://plnkr.co/edit/UwD8u6ySiCnvZcjpUvTw?p=preview

Zaključak

Web komponente će definitivno napraviti neke značajne promene u načinu razvoja web aplikacija. Lako korišćenje, fleksibilna implementacija kao i visoka modularnost predstavljaju pozitivne aspekte ove tehnologije koja definitivno pokazuje neke nove pravce razvoja web-a.

Naravno da postoje i neki manje poželjni efekti korišćenja web komponenti kao što je obaveza korišćenja webcomponents.js polyfila ili problem učitavanja velikog broja komponenti na stranici što za sobom povlači ogroman broj http poziva. Neki od ovih problema već mogu da se reše na primer korišćenjem vulcanize alata (https://github.com/Polymer/vulcanize) koji spaja import fajlove u jedan i time ubrzava učitavanje stranice. Takođe je i činjenica je da će na kraju pretraživači u potpunosti podržati standarde što vodi ka smanjivanju veličine polyfila pa i njegovom potpunom izbacivanju, što će definitivno uticati na popularnost ove tehnologije.

Share

Prijavi se da prvi dobijaš nove blogove i vesti.

Оставите одговор