Webanwendungen mit Yeoman und Polymer erstellen

Webanwendungen mit modernen Tools erstellen

Einführung

Allo’ Allo’. Jeder, der eine Webanwendung schreibt, weiß, wie wichtig es ist, produktiv zu bleiben. Es ist eine Herausforderung, wenn Sie sich um mühsame Aufgaben wie das Finden des richtigen Boilerplates, das Einrichten eines Entwicklungs- und Testworkflows und das Minimieren und Komprimieren aller Quellen kümmern müssen.

Glücklicherweise können moderne Frontend-Tools einen Großteil davon automatisieren, sodass Sie sich auf das Schreiben einer erstklassigen App konzentrieren können. In diesem Artikel erfahren Sie, wie Sie Yeoman verwenden, einen Workflow von Tools für Web-Apps, um die Erstellung von Apps mit Polymer zu optimieren, einer Bibliothek von Polyfills und Sugar für die Entwicklung von Apps mit Webkomponenten.

Yeoman

Yo, Grunt und Bower

Yeoman ist ein Mann mit einem Hut und drei Tools, mit denen Sie Ihre Produktivität steigern können:

  • yo ist ein Scaffolding-Tool, das ein Ökosystem von frameworkspezifischen Scaffolds bietet, die als Generatoren bezeichnet werden. Mit ihnen lassen sich einige der mühsamen Aufgaben erledigen, die ich zuvor erwähnt habe.
  • grunt wird verwendet, um Ihr Projekt zu erstellen, in der Vorschau anzusehen und zu testen. Dabei helfen Aufgaben, die vom Yeoman-Team und von grunt-contrib zusammengestellt wurden.
  • bower wird für die Abhängigkeitsverwaltung verwendet, sodass Sie Ihre Skripts nicht mehr manuell herunterladen und verwalten müssen.

Mit nur ein oder zwei Befehlen kann Yeoman Boilerplate-Code für Ihre App (oder einzelne Teile wie Modelle) schreiben, Ihr Sass kompilieren, Ihre CSS-, JS-, HTML- und Bilddateien minimieren und verketten und einen einfachen Webserver in Ihrem aktuellen Verzeichnis starten. Sie kann auch Ihre Unit-Tests ausführen und mehr.

Sie können Generatoren aus Node Packaged Modules (npm) installieren. Derzeit sind über 220 Generatoren verfügbar, von denen viele von der Open-Source-Community geschrieben wurden. Beliebte Generatoren sind generator-angular, generator-backbone und generator-ember.

Yeoman-Startseite

Wenn Sie eine aktuelle Version von Node.js installiert haben, öffnen Sie ein Terminal und führen Sie Folgendes aus:

$ npm install -g yo

Geschafft! Sie haben jetzt Yo, Grunt und Bower und können sie direkt über die Befehlszeile ausführen. Hier ist die Ausgabe der Ausführung von yo:

Yeoman-Installation

Polymer-Generator

Wie bereits erwähnt, ist Polymer eine Bibliothek mit Polyfills und Sugar, die die Verwendung von Webkomponenten in modernen Browsern ermöglicht. Das Projekt ermöglicht es Entwicklern, Apps mit der Plattform von morgen zu erstellen und das W3C über Bereiche zu informieren, in denen Spezifikationen weiter verbessert werden können.

Polymer-Generator-Startseite

generator-polymer ist ein neuer Generator, mit dem Sie Polymer-Apps mithilfe von Yeoman erstellen können. So können Sie Polymer-Elemente (benutzerdefinierte Elemente) ganz einfach über die Befehlszeile erstellen und anpassen und sie mithilfe von HTML-Importen importieren. So sparen Sie Zeit, da der Boilerplate-Code für Sie geschrieben wird.

Installieren Sie als Nächstes den Polymer-Generator, indem Sie Folgendes ausführen:

$ npm install generator-polymer -g

Das war's. Jetzt hat Ihre App Webkomponenten-Superkräfte!

Unser neu installierter Generator bietet Ihnen Zugriff auf einige spezifische Funktionen:

  • polymer:element wird verwendet, um neue einzelne Polymer-Elemente zu erstellen. Beispiel: yo polymer:element carousel
  • polymer:app wird verwendet, um die erste index.html-Datei, eine Gruntfile.js-Datei mit der Build-Konfiguration für Ihr Projekt sowie Grunt-Aufgaben und eine für das Projekt empfohlene Ordnerstruktur zu erstellen. Außerdem haben Sie die Möglichkeit, Sass Bootstrap für die Stile Ihres Projekts zu verwenden.

Polymer-Anwendung erstellen

Wir erstellen einen einfachen Blog mit einigen benutzerdefinierten Polymer-Elementen und unserem neuen Generator.

Polymer-App

Öffnen Sie zuerst das Terminal, erstellen Sie ein neues Verzeichnis und wechseln Sie mit mkdir my-new-project && cd $_ dorthin. Sie können Ihre Polymer-App jetzt mit folgendem Befehl starten:

$ yo polymer
Polymer-App-Entwicklung

Dadurch wird die aktuelle Version von Polymer von Bower abgerufen und eine index.html, eine Verzeichnisstruktur und Grunt-Aufgaben für Ihren Workflow erstellt. Wie wäre es mit einem Kaffee, während wir darauf warten, dass die App fertig wird?

Als Nächstes können wir grunt server ausführen, um eine Vorschau der App zu sehen:

Grunt-Server

Der Server unterstützt LiveReload. Das bedeutet, dass Sie einen Texteditor öffnen und ein benutzerdefiniertes Element bearbeiten können. Der Browser wird dann beim Speichern neu geladen. So erhalten Sie eine gute Echtzeitansicht des aktuellen Status Ihrer App.

Als Nächstes erstellen wir ein neues Polymer-Element, das einen Blogpost darstellt.

$ yo polymer:element post
Beitragselement erstellen

Yeoman stellt uns ein paar Fragen, z. B. ob wir einen Konstruktor einfügen oder einen HTML-Import verwenden möchten, um das Post-Element in index.html einzufügen. Wir wählen für die ersten beiden Optionen „Nein“ aus und lassen die dritte Option leer.

$ yo polymer:element post

[?] Would you like to include constructor=''? No

[?] Import to your index.html using HTML imports? No

[?] Import other elements into this one? (e.g 'another_element.html' or leave blank)

    create app/elements/post.html

Dadurch wird im Verzeichnis /elements ein neues Polymer-Element mit dem Namen „post.html“ erstellt:

<polymer-element name="post-element"  attributes="">

    <template>

    <style>
        @host { :scope {display: block;} }
    </style>

    <span>I'm <b>post-element</b>. This is my Shadow DOM.</span>

    </template>

    <script>

    Polymer('post-element', {

        //applyAuthorStyles: true,

        //resetStyleInheritance: true,

        created: function() { },

        enteredView: function() { },

        leftView: function() { },

        attributeChanged: function(attrName, oldVal, newVal) { }

    });

    </script>

</polymer-element>

Es enthält:

Mit einer echten Datenquelle arbeiten

Unser Blog benötigt einen Ort, an dem neue Beiträge geschrieben und gelesen werden können. Um die Arbeit mit einem echten Datendienst zu demonstrieren, verwenden wir die Google Apps Spreadsheets API. So können wir den Inhalt jeder mit Google Docs erstellten Tabelle ganz einfach einlesen.

So richtest du die Funktion ein:

  1. Öffnen Sie in Ihrem Browser (für diese Schritte wird Chrome empfohlen) diese Google Docs-Tabelle. Sie enthält Beispielbeitragsdaten in den folgenden Feldern:

    • ID
    • Titel
    • Autor
    • Inhalt
    • Datum
    • Keywords
    • E-Mail-Adresse (des Autors)
    • Slug (für die Slug-URL Ihres Beitrags)
  2. Öffnen Sie das Menü Datei und wählen Sie Kopie erstellen aus, um eine eigene Kopie der Tabelle zu erstellen. Sie können die Inhalte nach Belieben bearbeiten und Beiträge hinzufügen oder entfernen.

  3. Gehen Sie noch einmal zum Menü Datei und wählen Sie Im Web veröffentlichen aus.

  4. Klicken Sie auf Veröffentlichung starten.

  5. Kopieren Sie unter Link zu den veröffentlichten Daten abrufen aus dem letzten Textfeld den Schlüsselteil der angegebenen URL. Sie sieht so aus: https://docs.google.com/spreadsheet/ccc?key=0AhcraNy3sgspdDhuQ2pvN21JVW9NeVA0M1h4eGo3RGc#gid=0

  6. Fügen Sie den Schlüssel in die folgende URL ein, wo your-key-goes-here steht: https://spreadsheets.google.com/feeds/list/your-key-goes-here/od6/public/values?alt=json-in-script&callback=. Ein Beispiel mit dem obigen Schlüssel könnte so aussehen: https://spreadsheets.google.com/feeds/list/0AhcraNy3sgspdDhuQ2pvN21JVW9NeVA0M1h4eGo3RGc/od6/public/values?alt=json-in-script.

  7. Sie können die URL in Ihren Browser einfügen und aufrufen, um die JSON-Version Ihres Blog-Inhalts anzusehen. Notieren Sie sich die URL und sehen Sie sich das Format dieser Daten an. Sie müssen sie später iterieren, um sie auf dem Bildschirm anzuzeigen.

Die JSON-Ausgabe in Ihrem Browser sieht vielleicht etwas beängstigend aus, aber keine Sorge! Wir sind nur an den Daten für Ihre Beiträge interessiert.

Die Google Spreadsheets API gibt jedes Feld in Ihrer Blog-Tabelle mit dem speziellen Präfix post.gsx$ aus. Beispiel: post.gsx$title.$t, post.gsx$author.$t, post.gsx$content.$t usw. Wenn wir jede „Zeile“ in unserer JSON-Ausgabe durchlaufen, verweisen wir auf diese Felder, um die relevanten Werte für jeden Beitrag zu erhalten.

Sie können jetzt das neu erstellte Post-Element bearbeiten, um Teile des Markups an die Daten in Ihrer Tabelle zu binden. Dazu führen wir ein Attribut post ein, das für den Beitragstitel, den Autor, den Inhalt und andere Felder, die wir zuvor erstellt haben, gelesen wird. Das Attribut selected (das wir später füllen werden) wird verwendet, um einen Beitrag nur dann anzuzeigen, wenn ein Nutzer zum richtigen Slug dafür navigiert.

<polymer-element name="post-element" attributes="post selected">

    <template>

    <style>
        @host { :scope {display: block;} }
    </style>

        <div class="col-lg-4">

            <template if="[[post.gsx$slug.$t === selected]]">

            <h2>
                <a href="#[[post.gsx$slug.$t]]">
                [[post.gsx$title.$t  ]]
                </a>
            </h2>

            <p>By [[post.gsx$author.$t]]</p>

            <p>[[post.gsx$content.$t]]</p>

            <p>Published on: [[post.gsx$date.$t]]</p>

            <small>Keywords: [[post.gsx$keywords.$t]]</small>

            </template>

        </div>

    </template>

    <script>

    Polymer('post-element', {

        created: function() { },

        enteredView: function() { },

        leftView: function() { },

        attributeChanged: function(attrName, oldVal, newVal) { }

    });

    </script>

</polymer-element>

Als Nächstes erstellen Sie mit yo polymer:element blog ein Blogelement, das sowohl eine Sammlung von Beiträgen als auch das Layout für Ihren Blog enthält.

$ yo polymer:element blog

[?] Would you like to include constructor=''? No

[?] Import to your index.html using HTML imports? Yes

[?] Import other elements into this one? (e.g 'another_element.html' or leave blank) post.html

    create app/elements/blog.html

Dieses Mal importieren wir den Blog mit HTML-Importen in „index.html“, damit er auf der Seite angezeigt wird. Für den dritten Prompt geben wir post.html als das Element an, das wir einbeziehen möchten.

Wie zuvor wird eine neue Elementdatei (blog.html) erstellt und zu /elements hinzugefügt. Dieses Mal wird post.html importiert und <post-element> wird in das Vorlagen-Tag eingefügt:

<link rel="import" href="post.html">

<polymer-element name="blog-element"  attributes="">

    <template>

    <style>
        @host { :scope {display: block;} }
    </style>

    <span>I'm <b>blog-element</b>. This is my Shadow DOM.</span>

        <post-element></post-element>

    </template>

    <script>

    Polymer('blog-element', {

        //applyAuthorStyles: true,

        //resetStyleInheritance: true,

        created: function() { },

        enteredView: function() { },

        leftView: function() { },

        attributeChanged: function(attrName, oldVal, newVal) { }

    });

    </script>

</polymer-element>

Da wir darum gebeten haben, das Blog-Element mithilfe von HTML-Importen (eine Möglichkeit, HTML-Dokumente in andere HTML-Dokumente einzufügen und wiederzuverwenden) in unseren Index zu importieren, können wir auch prüfen, ob es dem Dokument <head> korrekt hinzugefügt wurde:

<!doctype html>
    <head>

        <meta charset="utf-8">

        <meta http-equiv="X-UA-Compatible" content="IE=edge">

        <title></title>

        <meta name="description" content="">

        <meta name="viewport" content="width=device-width">

        <link rel="stylesheet" href="styles/main.css">

        <!-- build:js scripts/vendor/modernizr.js -->

        <script src="bower_components/modernizr/modernizr.js"></script>

        <!-- endbuild -->

        <!-- Place your HTML imports here -->

        <link rel="import" href="elements/blog.html">

    </head>

    <body>

        <div class="container">

            <div class="hero-unit" style="width:90%">

                <blog-element></blog-element>

            </div>

        </div>

        <script>
        document.addEventListener('WebComponentsReady', function() {
            // Perform some behaviour
        });
        </script>

        <!-- build:js scripts/vendor.js -->

        <script src="bower_components/polymer/polymer.min.js"></script>

        <!-- endbuild -->

</body>

</html>

Beeindruckend.

Abhängigkeiten mit Bower hinzufügen

Als Nächstes bearbeiten wir unser Element, um das Dienstprogrammelement Polymer JSONP zum Einlesen von posts.json zu verwenden. Sie können den Adapter entweder durch Klonen des Repositorys mit Git oder durch Installieren von polymer-elements über Bower mit bower install polymer-elements abrufen.

Bower-Abhängigkeiten

Sobald Sie das Dienstprogramm haben, müssen Sie es als Import in Ihr blog.html-Element einfügen:

<link rel="import" href="../bower_components/polymer-jsonp/polymer-jsonp.html">

Fügen Sie als Nächstes das Tag dafür ein und geben Sie die url für die Tabelle mit den Blogposts von vorhin an. Fügen Sie am Ende &callback= hinzu:

<polymer-jsonp auto url="https://spreadsheets.google.com/feeds/list/your-key-value/od6/public/values?alt=json-in-script&callback=" response="[[posts]]"></polymer-jsonp>

Jetzt können wir Vorlagen hinzufügen, um die Tabelle zu iterieren, sobald sie eingelesen wurde. Die erste gibt ein Inhaltsverzeichnis mit einem verlinkten Titel für einen Beitrag aus, der auf den Slug verweist.

<!-- Table of contents -->

<ul>

    <template repeat="[[post in posts.feed.entry]]">

    <li><a href="#[[post.gsx$slug.$t]]">[[post.gsx$title.$t]]</a></li>

    </template>

</ul>

Die zweite rendert eine Instanz von post-element für jeden gefundenen Eintrag und übergibt den Inhalt des Beitrags entsprechend. Wir übergeben ein post-Attribut, das den Beitragstext für eine einzelne Tabellenzeile darstellt, und ein selected-Attribut, das wir mit einer Route füllen.

<!-- Post content -->

<template repeat="[[post in posts.feed.entry]]">

    <post-element post="[[post]]" selected="[[route]]"></post-element>

</template>

Das Attribut repeat, das Sie in unserer Vorlage sehen, erstellt und verwaltet eine Instanz mit [[ Bindungen ]] für jedes Element in der Array-Sammlung unserer Beiträge, sofern vorhanden.

Polymer-App

Damit die aktuelle [[route]] ausgefüllt wird, schummeln wir ein wenig und verwenden eine Bibliothek namens Flatiron Director, die sich an [[route]] bindet, wenn sich der URL-Hash ändert.

Glücklicherweise gibt es ein Polymer-Element (Teil des more-elements-Pakets), das wir dafür verwenden können. Nachdem Sie die Datei in das Verzeichnis „/elements“ kopiert haben, können Sie sie mit <flatiron-director route="[[route]]" autoHash></flatiron-director> referenzieren. Geben Sie dabei route als Attribut an, an das Sie binden möchten, und weisen Sie es an, den Wert aller Hash-Änderungen automatisch zu lesen (autoHash).

Wenn wir alles zusammenfügen, erhalten wir Folgendes:

    <link rel="import" href="post.html">

    <link rel="import" href="polymer-jsonp/polymer-jsonp.html">

    <link rel="import" href="flatiron-director/flatiron-director.html">

    <polymer-element name="blog-element"  attributes="">

      <template>

        <style>
          @host { :scope {display: block;} }
        </style>

        <div class="row">

          <h1><a href="/#">My Polymer Blog</a></h1>

          <flatiron-director route="[[route]]" autoHash></flatiron-director>

          <h2>Posts</h2>

          <!-- Table of contents -->

          <ul>

            <template repeat="[[post in posts.feed.entry]]">

              <li><a href="#[[post.gsx$slug.$t]]">[[post.gsx$title.$t]]</a></li>

            </template>

          </ul>

          <!-- Post content -->

          <template repeat="[[post in posts.feed.entry]]">

            <post-element post="[[post]]" selected="[[route]]"></post-element>

          </template>

        </div>

        <polymer-jsonp auto url="https://spreadsheets.google.com/feeds/list/0AhcraNy3sgspdHVQUGd2M2Q0MEZnRms3c3dDQWQ3V1E/od6/public/values?alt=json-in-script&callback=" response="[[posts]]"></polymer-jsonp>

      </template>

      <script>

        Polymer('blog-element', {

          created: function() {},

          enteredView: function() { },

          leftView: function() { },

          attributeChanged: function(attrName, oldVal, newVal) { }

        });

      </script>

    </polymer-element>
Polymer-App

Sehr gut! Wir haben jetzt einen einfachen Blog, der Daten aus JSON liest und zwei Polymer-Elemente verwendet, die mit Yeoman erstellt wurden.

Mit Elementen von Drittanbietern arbeiten

Das Element-Ökosystem rund um Webkomponenten ist in letzter Zeit gewachsen und es sind Websites mit Komponentengalerien wie customelements.io entstanden. Beim Durchsehen der von der Community erstellten Elemente habe ich eines zum Abrufen von Gravatar-Profilen gefunden. Wir können es uns holen und unserer Blog-Website hinzufügen.

Startseite für benutzerdefinierte Elemente

Kopieren Sie die Gravatar-Elementquellen in Ihr /elements-Verzeichnis, binden Sie sie über HTML-Importe in post.html ein und fügen Sie dann zu Ihrer Vorlage hinzu. Übergeben Sie dabei das E-Mail-Feld aus Ihrer Tabelle als Quelle für den Nutzernamen. Zack!

<link rel="import" href="gravatar-element/src/gravatar.html">

<polymer-element name="post-element" attributes="post selected">

    <template>

    <style>
        @host { :scope {display: block;} }
    </style>

        <div class="col-lg-4">

            <template if="[[post.gsx$slug.$t === selected]]">

            <h2><a href="#[[post.gsx$slug.$t]]">[[post.gsx$title.$t]]</a></h2>

            <p>By [[post.gsx$author.$t]]</p>

            <gravatar-element username="[[post.gsx$email.$t]]" size="100"></gravatar-element>

            <p>[[post.gsx$content.$t]]</p>

            <p>[[post.gsx$date.$t]]</p>

            <small>Keywords: [[post.gsx$keywords.$t]]</small>

            </template>

        </div>

    </template>

    <script>

    Polymer('post-element', {

        created: function() { },

        enteredView: function() { },

        leftView: function() { },

        attributeChanged: function(attrName, oldVal, newVal) { }

    });

    </script>

</polymer-element>

Sehen wir uns an, was wir erhalten:

Polymer-Anwendung mit benutzerdefinierten Elementen

Sehr gut.

In relativ kurzer Zeit haben wir eine einfache Anwendung erstellt, die aus mehreren Webkomponenten besteht, ohne dass wir uns um das Schreiben von Boilerplate-Code, das manuelle Herunterladen von Abhängigkeiten oder das Einrichten eines lokalen Servers oder Build-Workflows kümmern mussten.

Anwendung optimieren

Der Yeoman-Workflow umfasst ein weiteres Open-Source-Projekt namens Grunt – ein Task Runner, der eine Reihe von Build-spezifischen Aufgaben (definiert in einem Gruntfile) ausführen kann, um eine optimierte Version Ihrer Anwendung zu erstellen. Wenn Sie grunt allein ausführen, wird eine default-Aufgabe ausgeführt, die der Generator für Linting, Tests und Builds eingerichtet hat:

grunt.registerTask('default', [

    'jshint',

    'test',

    'build'

]);

Die Aufgabe jshint oben prüft Ihre Einstellungen in der Datei .jshintrc und führt sie dann für alle JavaScript-Dateien in Ihrem Projekt aus. Eine vollständige Liste der Optionen für JSHint finden Sie in der Dokumentation.

Die Aufgabe test sieht in etwa so aus und kann Ihre Anwendung für das von uns empfohlene Test-Framework Mocha erstellen und bereitstellen. Außerdem werden Ihre Tests für Sie ausgeführt:

grunt.registerTask('test', [

    'clean:server',

    'createDefaultTemplate',

    'jst',

    'compass',

    'connect:test',

    'mocha'

]);

Da unsere App in diesem Fall recht einfach ist, überlassen wir das Schreiben von Tests Ihnen als separate Übung. Es gibt noch ein paar andere Dinge, die wir in unserem Build-Prozess berücksichtigen müssen. Sehen wir uns also an, was die in der Gruntfile.js definierte Aufgabe grunt build tut:

grunt.registerTask('build', [

    'clean:dist',    // Clears out your .tmp/ and dist/ folders

    'compass:dist',  // Compiles your Sassiness

    'useminPrepare', // Looks for <!-- special blocks --> in your HTML

    'imagemin',      // Optimizes your images!

    'htmlmin',       // Minifies your HTML files

    'concat',        // Task used to concatenate your JS and CSS

    'cssmin',        // Minifies your CSS files

    'uglify',        // Task used to minify your JS

    'copy',          // Copies files from .tmp/ and app/ into dist/

    'usemin'         // Updates the references in your HTML with the new files

]);

Führen Sie grunt build aus. Dadurch wird eine produktionsreife Version Ihrer App erstellt, die Sie veröffentlichen können. Lassen Sie uns das ausprobieren.

Grunt-Build

Fertig!

Wenn Sie nicht weiterkommen, können Sie sich eine vorgefertigte Version von polymer-blog unter https://github.com/addyosmani/polymer-blog ansehen.

Was haben wir noch auf Lager?

Webkomponenten befinden sich noch in der Entwicklung, ebenso wie die zugehörigen Tools.

Wir untersuchen derzeit, wie man HTML-Importe für eine bessere Ladeleistung über Projekte wie Vulcanize (ein Tool des Polymer-Projekts) verketten kann und wie das Ökosystem für Komponenten mit einem Paketmanager wie Bower funktionieren könnte.

Wir werden euch informieren, sobald wir bessere Antworten auf diese Fragen haben. Es stehen uns auf jeden Fall spannende Zeiten bevor.

Polymer eigenständig mit Bower installieren

Wenn Sie einen einfacheren Einstieg in Polymer bevorzugen, können Sie es eigenständig direkt über Bower installieren, indem Sie Folgendes ausführen:

bower install polymer

Dadurch wird es Ihrem Verzeichnis „bower_components“ hinzugefügt. Sie können dann manuell in Ihrem Anwendungsindex darauf verweisen und die Zukunft rocken.

Was ist Ihre Meinung dazu?

Jetzt wissen Sie, wie Sie mit Yeoman eine Polymer-Anwendung mit Webkomponenten erstellen. Wenn Sie Feedback zum Generator haben, teilen Sie uns dies bitte in den Kommentaren mit, melden Sie einen Fehler oder posten Sie im Yeoman Issue Tracker. Wir würden uns freuen, wenn du uns mitteilst, was wir am Generator noch verbessern können. Nur durch deine Nutzung und dein Feedback können wir uns verbessern. :)