Webanwendungen mit Yeoman und Polymer erstellen

Bauen Sie Ihre Webanwendungen mit modernen Tools auf.

Addy Osmani
Addy Osmani

Einleitung

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 Suchen des richtigen Textbausteins, das Einrichten eines Entwicklungs- und Test-Workflows sowie das Komprimieren und Komprimieren Ihrer Quellen kümmern müssen.

Glücklicherweise können moderne Frontend-Tools viele dieser Aufgaben automatisieren, sodass Sie sich auf das Schreiben einer überzeugenden App konzentrieren können. In diesem Artikel erfahren Sie, wie Sie Yeoman verwenden, ein Workflow mit Tools für Webanwendungen, mit denen Sie die App-Erstellung mithilfe von Polymer optimieren können. Polymer ist eine Bibliothek mit Polyfills und Zucker für die Entwicklung von Apps mit Webkomponenten.

Yeoman

Yo, Grunt und Bower

Yeoman ist ein Mann mit Hut und drei Tools, mit denen du deine Produktivität steigern kannst:

  • yo ist ein Scaffolding-Tool, das ein Ökosystem aus frameworkspezifischen Gerüsten, sogenannten Generatoren, bietet. Damit können einige der mühsamen Aufgaben ausgeführt werden, die ich zuvor erwähnt habe.
  • grunt wird verwendet, um dein Projekt zu erstellen, als Vorschau anzusehen und zu testen – mithilfe von Aufgaben, die vom Yeoman-Team zusammengestellt wurden, und grunt-contrib.
  • bower wird für das Abhängigkeitsmanagement 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 deine App (oder einzelne Teile wie Modelle) schreiben, deinen SASS-Code kompilieren, CSS, JS, HTML und Bilder minimieren und verketten und einen einfachen Webserver in deinem aktuellen Verzeichnis starten. Es kann auch Einheitentests und mehr ausführen.

Sie können Generatoren aus Node Packaged Modules (npm) installieren. Es gibt jetzt mehr als 220 Generatoren, von denen viele von der Open-Source-Community stammen. Beliebte Generatoren sind Generator-Angular, Generator-Backbone und generator-ember.

Yeoman-Startseite

Wenn Sie eine aktuelle Version von Node.js installiert haben, gehen Sie zum nächsten Terminal und führen Sie folgenden Befehl aus:

$ npm install -g yo

Fertig! Du hast jetzt Yo, Grunt und Bower und kannst sie direkt über die Befehlszeile ausführen. Die Ausgabe von yo sieht so aus:

Yeoman-Installation

Polymergenerator

Wie bereits erwähnt, ist Polymer eine Bibliothek mit Polyfills und Zucker, die die Verwendung von Webkomponenten in modernen Browsern ermöglicht. Das Projekt ermöglicht es Entwicklern, mit der Plattform von morgen Apps zu entwickeln und das W3C darüber zu informieren, wo die Spezifikationen während des Flugs weiter verbessert werden können.

Startseite des Polymergenerators

generator-polymer ist ein neuer Generator, mit dem Sie mithilfe von Yeoman Polymer-Apps erstellen und anpassen können. Über die Befehlszeile können Sie benutzerdefinierte Polymer-Elemente erstellen und anpassen und sie dann mithilfe von HTML-Importen importieren. Dies spart Zeit, da der Boilerplate-Code automatisch geschrieben wird.

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

$ npm install generator-polymer -g

Das wars. Ihre Anwendung verfügt jetzt über Superkräfte für die Webkomponente!

Unser neu installierter Generator hat einige spezifische Bits, auf die du Zugriff hast:

  • polymer:element wird für das Gerüst neuer einzelner Polymer-Elemente verwendet. Beispiel: yo polymer:element carousel
  • polymer:app wird für das Gerüst der anfänglichen index.html verwendet, einer Gruntfile.js-Datei, die die Build-Konfiguration für Ihr Projekt sowie Grunt-Aufgaben und eine für das Projekt empfohlene Ordnerstruktur enthält. 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

Rufen Sie das Terminal auf, erstellen Sie ein neues Verzeichnis und rufen Sie es mit mkdir my-new-project && cd $_ auf. Sie können Ihre Polymer-App jetzt starten, indem Sie folgenden Befehl ausführen:

$ yo polymer
App-Entwicklung mit Polymer

Dies ruft die neueste Version von Polymer von Bower ab und erstellt ein Gerüst für eine index.html, eine Verzeichnisstruktur und Grunt-Aufgaben für Ihren Workflow. Wie wäre es mit einem Kaffee, während wir darauf warten, dass die App fertig ist?

Jetzt können wir grunt server ausführen, um sich eine Vorschau der App anzusehen:

Grunt-Server

Der Server unterstützt LiveRefresh, d. h., du kannst einen Texteditor starten, ein benutzerdefiniertes Element bearbeiten und der Browser wird beim Speichern neu geladen. So erhalten Sie einen Echtzeit-Überblick über den 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 einige Fragen, z. B. ob wir einen Konstruktor hinzufügen oder einen HTML-Import verwenden möchten, um das Post-Element in index.html aufzunehmen. Sagen wir zu den ersten beiden Optionen „Nein“ und lassen Sie 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 /elements-Verzeichnis ein neues Polymer-Element namens 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 wir neue Posts schreiben und lesen können. Um die Arbeit mit einem echten Datendienst zu demonstrieren, verwenden wir die Google Apps Tables API. So können wir den Inhalt von Tabellen, die mit Google Docs erstellt wurden, einfach lesen.

Beginnen wir mit der Einrichtung:

  1. Öffnen Sie in Ihrem Browser diese Google Docs-Tabelle. Wir empfehlen dafür Chrome. Es enthält Beispielbeitragsdaten in den folgenden Feldern:

    • ID
    • Titel
    • Verfasser
    • Inhalte
    • Datum
    • Keywords
    • E-Mail-Adresse (des Autors)
    • Slug (für die Slug-URL Ihres Beitrags)
  2. Wenn Sie eine eigene Kopie der Tabelle erstellen möchten, wählen Sie im Menü Datei die Option Kopie erstellen aus. Sie können die Inhalte nach Belieben bearbeiten und Beiträge hinzufügen oder entfernen.

  3. Öffnen Sie das Menü Datei noch einmal und wählen Sie Im Web veröffentlichen aus.

  4. Klicke auf Veröffentlichung starten.

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

  6. Fügen Sie den Schlüssel an der Stelle Ihr-Schlüssel-für-Schritt 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 für die Verwendung des obigen Schlüssels könnte wie folgt 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 dorthin navigieren, um die JSON-Version Ihres Bloginhalts zu sehen. Notieren Sie sich die URL und überprüfen Sie dann das Format dieser Daten, da Sie diese iterieren müssen, um sie später auf dem Bildschirm anzuzeigen.

Die JSON-Ausgabe in Ihrem Browser mag etwas einschüchternd wirken, aber keine Sorge! Uns interessieren nur die Daten Ihrer Beiträge.

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

Sie können jetzt das neue Scaffold-Beitrag-Element bearbeiten, um Markup-Teile mit den Daten in Ihrer Tabelle bind. Dazu führen wir das Attribut post ein, das für den Titel des Posts, den Autor, den Inhalt und andere Felder vorliest, die wir zuvor erstellt haben. Das Attribut selected (das wir später hinzufügen) wird verwendet, um einen Beitrag nur einzublenden, 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 wir mit yo polymer:element blog ein Blog-Element, das sowohl eine Sammlung von Posts 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 mithilfe von HTML-Importen in die Datei index.html, wie er auf der Seite angezeigt werden soll. Für die dritte Aufforderung geben wir post.html als das Element an, das wir aufnehmen möchten.

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

<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, dass das Blog-Element mithilfe von HTML-Importen (eine Möglichkeit, HTML-Dokumente in andere HTML-Dokumente einzubinden und wiederzuverwenden) in unseren Index importiert wird, können wir auch überprü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

Als Nächstes bearbeiten wir das Element und verwenden das Dienstprogrammelement Polymer JSONP zum Lesen in „posts.json“. Sie können den Adapter abrufen, indem Sie das Repository mit Git klonen, oder polymer-elements über Bower installieren, indem Sie bower install polymer-elements ausführen.

Bower-Abhängigkeiten

Sobald Sie über das Dienstprogramm verfügen, müssen Sie es mit den folgenden Informationen 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 fügen Sie url in unserer Blogpost-Tabelle von vorhin an, wobei Sie am Ende &callback= hinzufügen:

<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>

Damit können wir nun Vorlagen hinzufügen, um unsere Tabellenkalkulation zu iterieren, nachdem sie eingelesen wurde. Im ersten Beispiel wird ein Inhaltsverzeichnis mit einem verlinkten Titel für einen Beitrag ausgegeben, 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 für jeden gefundenen Eintrag eine Instanz von post-element und gibt den Beitragsinhalt entsprechend weiter. Wie Sie sehen, werden ein post-Attribut übergeben, das den Post-Inhalt für eine einzelne Tabellenzeile darstellt, und ein selected-Attribut, dem wir eine Route hinzufügen.

<!-- Post content -->

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

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

</template>

Das Attribut repeat, das in unserer Vorlage verwendet wird, erstellt und verwaltet eine Instanz mit [[ bindings ]] für jedes Element in der Arraysammlung unserer Beiträge, wenn es angegeben wird.

Polymer App

Damit wir nun die aktuelle [[route]] ausfüllen, versuchen wir, eine Bibliothek namens Flatiron Director zu verwenden, die sich immer dann an [[route]] bindet, wenn sich der URL-Hash ändert.

Glücklicherweise gibt es ein Polymer-Element (Teil des more-elements-Pakets), das wir abrufen können. Nach dem Kopieren in das Verzeichnis „/elements“ können wir mit <flatiron-director route="[[route]]" autoHash></flatiron-director> darauf verweisen. Geben Sie dabei route als Eigenschaft für die Bindung an und weisen Sie das System an, den Wert von Hash-Änderungen automatisch zu lesen (autoHash).

Wenn wir alles zusammenfügen, erhalten wir:

    <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

Hurra! Wir haben jetzt einen einfachen Blog, der Daten aus JSON liest und zwei Polymer-Elemente verwendet, auf denen Yeoman das Gerüst basiert.

Mit Elementen von Drittanbietern arbeiten

Das Elementsystem rund um Webkomponenten hat in letzter Zeit immer mehr zugenommen. Seit Kurzem werden auch Komponentengalerie-Websites wie customelements.io angezeigt. Beim Durchsehen der von der Community erstellten Elemente habe ich eines zum Abrufen von gravatar-Profilen gefunden. Wir können es auch in unsere Blog-Website einfügen.

Startseite für benutzerdefinierte Elemente

Kopieren Sie die Quellen des Gravatar-Elements in das Verzeichnis /elements, fügen Sie sie über HTML-Importe in post.html hinzu und fügen Sie zu Ihrer Vorlage hinzu. Übergeben Sie dabei das E-Mail-Feld aus unserer Tabelle als Quelle des Nutzernamens. Super!

<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 daraus ziehen:

Polymer-App mit benutzerdefinierten Elementen

Wunderschön!

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

Anwendung optimieren

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

grunt.registerTask('default', [

    'jshint',

    'test',

    'build'

]);

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

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

grunt.registerTask('test', [

    'clean:server',

    'createDefaultTemplate',

    'jst',

    'compass',

    'connect:test',

    'mocha'

]);

Da unsere App in diesem Fall ziemlich simpel ist, überlassen wir das Schreiben von Tests Ihnen als separate Übung. Es gibt noch einige andere Dinge, die wir für den Build-Prozess brauchen. Sehen wir uns also an, was die in Gruntfile.js definierte Aufgabe grunt build bewirkt:

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

]);

Wenn Sie grunt build ausführen, sollte eine produktionsreife Version Ihrer App erstellt werden, die sofort versendet werden kann. Probieren wir es aus.

Grunt-Build

Fertig!

Wenn Sie nicht weiterkommen, finden Sie eine vorgefertigte Version von polymer-blog unter https://github.com/addyosmani/polymer-blog.

Was haben wir noch auf Lager?

Webkomponenten befinden sich immer noch in einem Entwicklungsstadium, daher befinden sie sich auch in den Tools, die sie umgeben.

Wir überlegen derzeit, wie man mithilfe von Projekten wie Vulcanize (einem Tool des Polymer-Projekts) HTML-Importe verketten kann, um eine bessere Ladeleistung zu erzielen, und wie das Ökosystem für Komponenten mit einem Paketmanager wie Bower funktionieren könnte.

Wir werden Sie informieren, sobald wir bessere Antworten auf diese Fragen haben, aber es steht noch viele spannende Herausforderungen an.

Polymer eigenständig mit Bower installieren

Wenn Sie statt Polymer einen leichteren Start bevorzugen, können Sie ihn direkt aus Bower eigenständig installieren. Dazu führen Sie folgenden Befehl aus:

bower install polymer

Dadurch wird es dem Verzeichnis bower_components hinzugefügt. Anschließend können Sie manuell in Ihrem Anwendungsindex darauf verweisen und die Zukunft starten.

Was ist Ihre Meinung dazu?

Jetzt wissen Sie, wie Sie mithilfe von Webkomponenten in Yeoman das Gerüst einer Polymer-App erstellen. Wenn Sie Feedback zum Generator haben, teilen Sie uns dies in den Kommentaren mit, melden Sie einen Fehler oder posten Sie einen Beitrag im Yeoman Issue Tracker. Wir würden uns freuen, wenn du uns noch etwas anderes wünschst, als wir den Generator durch deine Nutzung und dein Feedback verbessern könnten.