Schnellstart mit WebComponents

Webkomponenten sind eine Reihe von Standards, die Softwareschnittstellen zum Organisieren der Komponentenarchitektur definieren. Alle von ihnen sind in modernen Versionen von Browsern implementiert, d.h. Sie erfordern keine Verbindung von Bibliotheken oder Code-Transpilern. Wenn Sie jedoch Kompatibilität benötigen, z. B. mit Internet Explorer 11, müssen Sie wahrscheinlich weiterhin Bibliotheken und Transpiler verwenden.

Dieser Artikel richtet sich an Anfänger und Entwickler, die Erfahrung mit dem einen oder anderen Front-End-Framework haben. Dank einiger Tricks ist er jedoch möglicherweise für erfahrene Experten interessant.

Alle unten genannten Experimente wurden in Chrome getestet und Firefox ist möglicherweise nicht einmal die neueste Version.

Also fangen wir an.

Erstellen Sie zunächst ein Verzeichnis für das Projekt und wechseln Sie dorthin.

mkdir mywebcomp cd mywebcomp 

Ausführen:

 npm init 

in diesem Verzeichnis, indem Sie standardmäßig alle Fragen beantworten.

Erstellen Sie eine index.html- Datei mit dem einfachsten Inhalt im Verzeichnis.

 <html lang="en"> <body> </body> </html> 

Fügen Sie ein Tag für ein Element hinzu. Der Name muss einen Bindestrich enthalten. Dies ist ein Signal für das CusomElements-Subsystem, dieses Element so zu definieren, dass es auf den Standardelementen aufbaut.

 <html lang="en"> <body> <my-webcomp></my-webcomp> </body> </html> 

Fügen Sie dem Skript- Tag eine Handler-Klasse hinzu.

 <script type="module"> class MyWebComp extends HTMLElement { connectedCallback() { this.insertAdjacentHTML('beforeEnd', `<div>Hello</div>`) } } customElements.define('my-webcomp', MyWebComp); </script> 

Im modularen Skript- Tag haben wir eine neue Klasse definiert, die sie mithilfe der customElements.define () -Methode hinter dem my-webcomp-Tag definiert hat . Durch Hinzufügen des Codes zur Methode " linkedCallback () " haben wir den Aufruf beim Hinzufügen der Komponentenimplementierung zum Baum bereitgestellt. Das Ergebnis kann bereits im Browser angezeigt werden:



Das Platzieren des HTML-Layouts direkt im Code ist jedoch im Allgemeinen nicht richtig, wenn dies für kleine Teile geeignet ist. Dies gilt insbesondere dann, wenn die Teile eine anständige Größe erreichen. Zum Beispiel in einem Formular mit 20 Elementen, das auch auf Unterkomponenten zu schlagen ist, ist möglicherweise nicht immer bequem. Aus diesem Grund legen wir zunächst das Layout in der Vorlage fest, die sich im selben HTML-Code befindet, obwohl uns nichts daran hindert, es bei Bedarf aus einer separaten Datei zu laden.

 <html lang="en"> <body> <template id="myWebCompTemplate"> <div>Hello</div> </template> <my-webcomp></my-webcomp> <script type="module"> class MyWebComp extends HTMLElement { connectedCallback() { let tplEl = document.querySelector('#myWebCompTemplate'); let html = document.importNode(tplEl.content, true); this.appendChild(html); } } customElements.define('my-webcomp', MyWebComp); </script> </body> </html> 

Im Komponentencode haben wir das Einfügen einer Zeichenfolge durch HTML ersetzt, indem wir ein Vorlagenelement mit der ID erhalten haben. Importieren, d.h. Erstellen einer Kopie dieses Elements und Verknüpfen mit dem Inhalt des aktuellen Elements.

id wird seitdem in camelCase notation benannt Alle Bezeichner von Elementen werden bei Verwendung von Bindestrichen oder anderen Sonderangeboten in den globalen Namespace geworfen. Der Zugriff von Zeichen auf sie ist möglicherweise weniger elegant. Das heißt, wir könnten stattdessen:

  let tplEl = document.querySelector('#myWebCompTemplate'); let html = document.importNode(tplEl.content, true); 

schreibe in eine Zeile:

 let html = document.importNode(myWebCompTemplate.content, true); 

und dieser Code würde genauso funktionieren, wird aber als nicht sehr sicher angesehen. Wenn wir unserem Element eine ID zuweisen, können wir von überall im Kontext als Instanz aus dem globalen Namespace darauf zugreifen, indem wir Methoden aufrufen und Eigenschaftswerte abrufen.
Zum Beispiel so:

 <my-webcomp id="myWebComp"></my-webcomp> <script type="module"> class MyWebComp extends HTMLElement { connectedCallback() { let html = document.importNode(myWebCompTemplate.content, true); this.appendChild(html); } } customElements.define('my-webcomp', MyWebComp); </script> <script type="module"> myWebComp.showMessage(); </script> 

In diesem Szenario wird die Warnung sofort beim Laden der Seite angezeigt.

Jetzt hängen wir einen Mausklick-Handler für unsere Komponente auf, der eine Warnmeldung anzeigt.

 <my-webcomp id="myWebComp" onclick="this.showMessage(event)"></my-webcomp> <script type="module"> class MyWebComp extends HTMLElement { connectedCallback() { let html = document.importNode(myWebCompTemplate.content, true); this.appendChild(html); } showMessage(event) { alert("This is the message"); console.log(event); } } customElements.define('my-webcomp', MyWebComp); </script> 

Wenn Sie jetzt auf eine Nachricht klicken, reagieren wir auf Benutzeraktionen.



Das Argument für die showMessage () -Methode deklariert auch ein Ereignisobjekt , das Ereignisdaten speichert, z. B. die Koordinaten eines Klicks oder einen Link zum Element selbst.

Oft muss jedes einzelne Element auf einzigartige Weise konfiguriert werden. Dies kann mithilfe von Attributen erfolgen.

Fügen Sie eine zweite Instanz des Elements hinzu und definieren Sie für jede von ihnen verschiedene Eigenschaften für Begrüßungsnamen, deren Werte angezeigt werden, wenn auf das Element geklickt wird.

 <my-webcomp id="myWebComp" greet-name="John" onclick="this.showMessage(event)"></my-webcomp> <my-webcomp id="myWebComp2" greet-name="Josh" onclick="this.showMessage(event)"></my-webcomp> <script type="module"> class MyWebComp extends HTMLElement { connectedCallback() { let html = document.importNode(myWebCompTemplate.content, true); this.appendChild(html); } showMessage(event) { alert("This is the message " + this.getAttribute('greet-name')); console.log(event); } } customElements.define('my-webcomp', MyWebComp); </script> 

Wenn Sie nun auf die erste klicken, wird "Dies ist die Nachricht für John" und auf der zweiten "Dies ist die Nachricht für Josh" angezeigt.

Es kann vorkommen, dass das Attribut nicht in der Ereignisverarbeitung verwendet, sondern direkt in die Vorlage gerendert werden muss. Dazu fügen wir dem Zielelement eine ID hinzu und ersetzen den Wert von api unmittelbar nach dem Rendern einer Kopie des Vorlagenobjekts.

 <template id="myWebCompTemplate"> <div id="helloLabel">Hello</div> </template> <my-webcomp id="myWebComp" greet-name="John" onclick="this.showMessage(event)"></my-webcomp> <my-webcomp id="myWebComp2" greet-name="Josh" onclick="this.showMessage(event)"></my-webcomp> <script type="module"> class MyWebComp extends HTMLElement { connectedCallback() { let html = document.importNode(myWebCompTemplate.content, true); this.appendChild(html); this.querySelector('#helloLabel').textContent += ' ' + this.getAttribute('greet-name'); } showMessage(event) { alert("This is the message " + this.getAttribute('greet-name')); console.log(event); } } customElements.define('my-webcomp', MyWebComp); </script> 

Es stellt sich so heraus:



Anstelle von .textContent kann es sich um .innerHTML handeln, oder Sie können dieselbe .insertAdjacentHTML () -Methode für das Objekt aus dem Selektor aufrufen, den wir am Anfang verwendet haben.

Die Verwendung von IDs galt lange Zeit als schlechte Form, da sie bei erheblichen Codemengen dupliziert werden konnten, was zu Kollisionen führte. Mit dem Aufkommen der Schattenbaumtechnologie können Sie jedoch den internen Inhalt eines Elements mithilfe von Bezeichnern, Stilen und anderen Ressourcen ohne Angst isolieren. Für Webkomponenten wird der Schattenbaum wie folgt aktiviert:

  this.attachShadow({mode: 'open'}); 

Die Wahrheit ist nun, dass alle DOM-Aufrufe dazu durch this.shadowRoot ersetzt werden müssen, da es nicht so viele davon gibt.

 <script type="module"> class MyWebComp extends HTMLElement { connectedCallback() { let html = document.importNode(myWebCompTemplate.content, true); this.attachShadow({mode: 'open'}); this.shadowRoot.appendChild(html); this.shadowRoot.querySelector('#helloLabel').textContent += ' ' + this.getAttribute('greet-name'); } showMessage(event) { alert("This is the message " + this.getAttribute('greet-name')); console.log(event); } } customElements.define('my-webcomp', MyWebComp); </script> 

Visuell wird sich die Arbeit dieses Codes in keiner Weise erneut ändern, aber jetzt gibt es kein helloLabel im globalen Namespace, und die Seite enthält bereits 2 Elemente mit dieser Kennung. Und Sie können zum Beispiel so darauf zugreifen:

 myWebComp.shadowRoot.getElementById('helloLabel'); 

und dann, wenn Sie den Baum nicht schließen, indem Sie das entsprechende Attribut in der .attachShadow () -Methode übergeben.

Wir haben ziemlich viel Code und das direkte Platzieren in der HTML-Datei ist auch nicht sehr korrekt. Daher erstellen wir die Datei my-webcomp.js und übertragen unsere Klasse mit der export-Anweisung dorthin. Fügen Sie den Import dieser Klasse in das Skript-Tag ein, um Folgendes zu erhalten:

 <script type="module"> import { MyWebComp } from "./my-webcomp.js"; customElements.define('my-webcomp', MyWebComp); myWebComp.shadowRoot.getElementById('helloLabel'); </script> 

Dies hat keine Auswirkungen auf die Leistung, aber jetzt ist unsere gesamte Geschäftslogik in .js getrennt, die Konfiguration erfolgt in HTML-Attributen und die modularen und Komponentenbrowsersysteme kümmern sich um die modulare asynchrone Initialisierung.

Von nun an schlägt das Öffnen von index.html als lokal für die Entwicklung jedoch fehl, da Der Browser blockiert den Download des Skripts aus dem Dateisystem. Wenn Sie NodeJS haben, können Sie den einfachsten Webserver verwenden:

 npm i http-server -g 

Führen Sie es mit dem Befehl http-server im Projektverzeichnis aus. Beim Start werden der Host und der Port angegeben, von dem aus Sie die Seite öffnen können

 http://127.0.0.1:8080 

Welches wird nun die Adresse der Debug-Seite mit unserem Element sein.

Das Schreiben von Tests ist auch für die Entwicklung wichtig, da sie dazu beitragen, dass der Code während der Entwicklung seiner Komponenten funktionsfähig bleibt. Zum Testen von Webkomponenten können Sie Mokka im Startmodus über den Browser verwenden.

Pakete hinzufügen.

 npm i mocha chai wct-mocha --save-dev 

Erstellen Sie dazu das Testverzeichnis und fügen Sie die folgende Datei all.html hinzu :

 <!doctype html> <html> <head> <meta charset="utf-8"> <script src="../node_modules/mocha/mocha.js"></script> <script src="../node_modules/chai/chai.js"></script> <script src="../node_modules/wct-mocha/wct-mocha.js"></script> </head> <body> <script> WCT.loadSuites([ 'my-webcomp.tests.html', ]); </script> </body> </html> 

Kopieren Sie unsere index.html , um sie zu testen / mit dem Namen my-webcomp.tests.html zu versehen, und fügen Sie den gleichen Kopfinhalt wie in all.html hinzu .

Jetzt müssen wir jedoch die üblichen Initialisierungen und Manipulationen durch die im Rahmen des Testlaufs durchgeführten ersetzen:

 <script type="module"> import { MyWebComp } from "../my-webcomp.js"; customElements.define('my-webcomp', MyWebComp); suite('MyWebComp', () => { test('is MyWebComp', () => { const element = document.getElementById('myWebComp'); chai.assert.instanceOf(element, MyWebComp); }); }); </script> 

Jetzt beim Betreten

 http://127.0.0.1:8080/test/all.html 

Ein Testbericht wird angezeigt.



Möglicherweise müssen Sie die Tests jedoch automatisch ausführen und in verschiedenen Browsern ein spezielles Dienstprogramm installieren:

 sudo npm install --global web-component-tester --unsafe-perm 

und lauf so:

 wct --npm 

Diese Zeile kann zum Testabschnitt der Datei package.json hinzugefügt und wie folgt ausgeführt werden:

 npm test 



Es wird empfohlen, jede signifikante Klassenmethode als Teil eines separaten Tests zu testen. Wenn Sie für jede eine gemeinsame Initialisierung vornehmen möchten, sollten Sie diese in der suiteSetup () -Methode definieren:

 suiteSetup(() => { }); 

in der Suite.

Dies ist wahrscheinlich zum ersten Mal genug. Wir haben ein minimales Projekt, bei dem er sich nicht schämen würde, wenn er eine bestimmte Aufgabe lösen würde.

 npm publish 

oder zumindest

 git push 

Es gibt noch nicht so viele Bücher zu diesem Thema, aber die gesamte umfangreiche Dokumentation ist nach Webkomponenten, benutzerdefinierten Elementen, ShadowDOM, nativem Vorlagen-Tag und benutzerdefinierten Ereignissen leicht zu finden.

Sie können Ihren Code im Repository überprüfen: https://bitbucket.org/techminded/mywebcomp

Eine Fortsetzung des Themas, das die Interaktion zwischen Komponenten mithilfe von Ereignissen demonstriert, finden Sie hier.

Source: https://habr.com/ru/post/de460397/


All Articles