Webanwendungen mit Yeoman und Polymer erstellen

Webanwendungen mit modernen Tools erstellen

Addy Osmani
Addy Osmani

Einführung

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

Glücklicherweise können moderne Front-End-Tools einen Großteil davon automatisieren, sodass Sie sich auf die Entwicklung einer tollen App konzentrieren können. In diesem Artikel erfahren Sie, wie Sie Yeoman verwenden, einen Workflow mit Tools für Webanwendungen, mit dem sich das Erstellen von Apps mit Polymer, einer Bibliothek mit Polyfills und Sugar für die Entwicklung von Apps mit Web-Komponenten, optimieren lässt.

Yeoman

Yo, Grunt und Bower

Yeoman ist ein Mann mit Hut, der drei Tools zur Steigerung Ihrer Produktivität hat:

  • yo ist ein Scaffolding-Tool, das ein System von frameworkspezifischen Scaffolds bietet, die sogenannten Generatoren. Mit diesen können einige der zuvor erwähnten mühsamen Aufgaben ausgeführt werden.
  • Mit grunt können Sie Ihr Projekt erstellen, eine Vorschau anzeigen und testen. Dabei werden Aufgaben verwendet, die vom Yeoman-Team und grunt-contrib zusammengestellt wurden.
  • Bower wird für die Abhängigkeitsverwaltung verwendet, sodass Sie Ihre Scripts 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 Bilder minimieren und zusammenführen und einen einfachen Webserver in Ihrem aktuellen Verzeichnis starten. Außerdem können damit Ihre Unit-Tests und vieles mehr ausgeführt werden.

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

Yeoman-Startseite

Wenn eine aktuelle Version von Node.js installiert ist, rufen Sie das nächste Terminal auf und führen Sie Folgendes aus:

$ npm install -g yo

Fertig! Sie haben jetzt Yo, Grunt und Bower und können sie direkt über die Befehlszeile ausführen. Hier ist die Ausgabe des Befehls 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 In-Flight-Spezifikationen weiter verbessert werden können.

Polymer-Generator-Startseite

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

Installieren Sie als Nächstes den Polymer-Generator mit dem Befehl:

$ npm install generator-polymer -g

Das war's. Jetzt hat Ihre App Superkräfte für Webkomponenten!

Unser neu installierter Generator bietet einige Funktionen, auf die du Zugriff hast:

  • 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 mit der Buildzeitkonfiguration 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-App 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 neueste Version von Polymer aus Bower abgerufen und eine index.html-Datei, eine Verzeichnisstruktur und Grunt-Aufgaben für Ihren Workflow werden erstellt. Warum holen Sie sich nicht einen Kaffee, während wir auf die Fertigstellung der App warten?

Okay, 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, ein benutzerdefiniertes Element bearbeiten und der Browser beim Speichern neu geladen wird. So erhalten Sie einen guten Echtzeitüberblick über den aktuellen Status Ihrer App.

Als Nächstes erstellen wir ein neues Polymer-Element für einen Blogpost.

$ yo polymer:element post
Beitragselement erstellen

Yeoman stellt uns einige 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. Lehnen wir die ersten beiden Optionen vorerst ab 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 braucht einen Ort, an dem neue Beiträge geschrieben und gelesen werden können. Zur Veranschaulichung der Arbeit mit einem echten Datendienst verwenden wir die Google Apps Spreadsheets API. So können wir den Inhalt jeder Tabelle, die mit Google Docs erstellt wurde, ganz einfach einlesen.

So richten Sie die Funktion ein:

  1. Öffne in deinem Browser (für diese Schritte wird Chrome empfohlen) diese Google Docs-Tabelle. Es enthält Beispieldaten für Beiträge in den folgenden Feldern:

    • ID
    • Titel
    • Autor
    • Inhalt
    • Datum
    • Keywords
    • E-Mail-Adresse (des Autors)
    • Slug (für die Slug-URL deines 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 jederzeit bearbeiten, Beiträge hinzufügen oder entfernen.

  3. Öffnen Sie noch einmal das 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 im letzten Textfeld den Teil key der angegebenen URL. Sie sieht so aus: https://docs.google.com/spreadsheet/ccc?key=0AhcraNy3sgspdDhuQ2pvN21JVW9NeVA0M1h4eGo3RGc#gid=0

  6. Fügen Sie den Schlüssel anstelle von Ihr-Schlüssel-wird-hier-eingefügt in die folgende URL ein: 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 Ihrer Bloginhalte aufzurufen. Notieren Sie sich die URL und sehen Sie sich das Format dieser Daten an, da Sie sie später durchgehen müssen, um sie auf dem Bildschirm anzuzeigen.

Die JSON-Ausgabe in Ihrem Browser mag auf den ersten Blick etwas beängstigend wirken, aber keine Sorge. Wir sind nur an den Daten für deine Beiträge interessiert.

Die Google Tabellen API gibt jedes Feld in Ihrer Blogtabelle mit einem 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 durchgehen, verweisen wir auf diese Felder, um die relevanten Werte für jeden Beitrag zurückzugeben.

Sie können das neu erstellte Beitragselement jetzt bearbeiten, um Markup-Abschnitte an die Daten in Ihrer Tabelle zu binden. Dazu führen wir das Attribut post ein, das den Titel, den Autor, den Inhalt und andere Felder liest, die wir zuvor erstellt haben. Das Attribut selected (das wir später ausfüllen) wird verwendet, um einen Beitrag nur dann anzuzeigen, wenn ein Nutzer die richtige Slug dafür aufruft.

<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 wir ein Blogelement, das sowohl eine Sammlung von Beiträgen als auch das Layout für Ihren Blog enthält. Führen Sie dazu yo polymer:element blog aus.

$ 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 mithilfe von HTML-Importen in index.html, wie er auf der Seite erscheinen soll. Für den dritten Prompt geben wir post.html als Element an, das wir einbeziehen möchten.

Wie zuvor wird eine neue Elementdatei (blog.html) erstellt und /elements hinzugefügt. Diesmal wird post.html importiert und <post-element> 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 Sie gebeten haben, das Blogelement mithilfe von HTML-Importen (eine Möglichkeit, HTML-Dokumente in anderen HTML-Dokumenten einzubinden 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>

Sehr gut.

Abhängigkeiten mit Bower hinzufügen

Bearbeiten wir nun unser Element, um das Dienstelement Polymer JSONP zum Lesen von posts.json zu verwenden. Sie können den Adapter entweder durch Klonen des Repositorys mit Git oder durch Ausführen von bower install polymer-elements über Bower installieren.polymer-elements

Bower-Abhängigkeiten

Nachdem du das Dienstprogramm hast, musst du es als Import in das Element „blog.html“ mit folgendem Code einfügen:

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

Fügen Sie als Nächstes das Tag für das Bild ein und geben Sie die url in die Tabelle mit den Blogbeiträgen aus der vorherigen Übung ein. 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 nach dem Lesen zu durchlaufen. Das erste gibt ein Inhaltsverzeichnis mit einem verlinkten Titel für einen Beitrag aus, der auf den Slug des Beitrags 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 für jeden gefundenen Eintrag eine Instanz von post-element und gibt den Inhalt des Beitrags entsprechend an sie weiter. Beachten Sie, dass wir ein post-Attribut übergeben, das den Inhalt des Beitrags 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>

Mit dem repeat-Attribut, das in unserer Vorlage verwendet wird, wird für jedes Element in der Arrayspalte unserer Beiträge eine Instanz mit [[ Bindungen ]] erstellt und beibehalten, sofern vorhanden.

Polymer-App

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

Glücklicherweise gibt es ein Polymer-Element (Teil des Pakets more-elements), das wir verwenden können. Nachdem wir es in das Verzeichnis „/elements“ kopiert haben, können wir es mit <flatiron-director route="[[route]]" autoHash></flatiron-director> referenzieren, route als die Property angeben, an die wir es binden möchten, und angeben, dass der Wert automatisch gelesen werden soll, wenn sich der Hash ändert (autoHash).

Zusammenfassend ergibt sich jetzt 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 mit Yeoman erstellte Polymer-Elemente verwendet.

Mit Drittanbieterelementen arbeiten

Das Element-Ökosystem rund um Webkomponenten wächst in letzter Zeit. Es gibt immer mehr Komponentengalerien wie customelements.io. Bei der Suche nach Elementen, die von der Community erstellt wurden, habe ich eines zum Abrufen von Gravatar-Profilen gefunden. Wir können es auch abrufen und unserer Blog-Website hinzufügen.

Startseite für benutzerdefinierte Elemente

Kopiere die Gravatar-Elementquellen in dein /elements-Verzeichnis, füge sie über HTML-Importe in „post.html“ ein und füge dann zu deiner Vorlage hinzu. Gib dabei das E-Mail-Feld aus unserer Tabelle als Quelle für den Nutzernamen an. Boom!

<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 damit erreichen:

Polymer-App mit benutzerdefinierten Elementen

Wunderschön!

In relativ kurzer Zeit haben wir eine einfache Anwendung mit mehreren Webkomponenten erstellt, ohne uns um Boilerplate-Code, das manuelle Herunterladen von Abhängigkeiten oder die Einrichtung eines lokalen Servers oder Build-Workflows kümmern zu müssen.

Anwendung optimieren

Der Yeoman-Workflow umfasst ein weiteres Open-Source-Projekt namens Grunt. Dies ist ein Task-Runner, mit dem eine Reihe von buildspezifischen Aufgaben (in einer Gruntfile definiert) ausgeführt werden können, um eine optimierte Version Ihrer Anwendung zu erstellen. Wenn Sie grunt alleine ausführen, wird eine default-Aufgabe ausgeführt, die der Generator für das Linieren, Testen und Erstellen eingerichtet hat:

grunt.registerTask('default', [

    'jshint',

    'test',

    'build'

]);

Die obige jshint-Aufgabe liest Ihre Einstellungen aus der .jshintrc-Datei und führt sie dann auf alle JavaScript-Dateien in Ihrem Projekt aus. Eine vollständige Übersicht über die Optionen mit JSHint finden Sie in der Dokumentation.

Die test-Aufgabe sieht in etwa so aus und kann Ihre App für das von uns empfohlene Testframework 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 unser Build-Prozess erledigen muss. Sehen wir uns an, was die in unserer 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. Daraufhin sollte eine für die Produktion geeignete Version Ihrer App erstellt werden, die Sie veröffentlichen können. Probieren wir es aus.

Grunt-Build

Fertig!

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

Was haben wir sonst noch zu bieten?

Webkomponenten befinden sich noch in der Entwicklungsphase und ebenso die zugehörigen Tools.

Wir prüfen derzeit, wie sich HTML-Importe mithilfe von Projekten wie Vulcanize (ein Tool des Polymer-Projekts) zusammenführen lassen, um die Ladeleistung zu verbessern. Außerdem untersuchen wir, wie das Komponentensystem mit einem Paketmanager wie Bower funktionieren könnte.

Wir halten dich auf dem Laufenden, sobald wir bessere Antworten auf diese Fragen haben. Aber es stehen aufregende Zeiten bevor.

Polymer als eigenständige Anwendung mit Bower installieren

Wenn Sie mit Polymer lieber ganz einfach beginnen möchten, können Sie es direkt über Bower als eigenständiges Paket installieren. Führen Sie dazu Folgendes aus:

bower install polymer

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

Was ist Ihre Meinung dazu?

Jetzt wissen Sie, wie Sie eine Polymer-Anwendung mit Webkomponenten mit Yeoman erstellen. Wenn Sie Feedback zum Generator haben, teilen Sie uns dies bitte in den Kommentaren mit oder melden Sie einen Fehler oder posten Sie im Yeoman-Issue-Tracker. Wir würden gern wissen, ob du dir noch etwas anderes von dem Generator wünschst. Nur durch deine Nutzung und dein Feedback können wir ihn verbessern.