Créer des applications Web avec Yeoman et Polymer

Structurer vos applications Web avec des outils modernes

Addy Osmani
Addy Osmani

Introduction

"Allo sur Allo". Tous les développeurs d'applications Web savent à quel point il est important de rester productif. Les tâches fastidieuses, comme trouver le code récurrent approprié, configurer un workflow de développement et de test, ou réduire et compresser toutes vos sources, représentent un véritable défi.

Heureusement, des outils frontaux modernes peuvent vous aider à automatiser une grande partie de ces tâches, ce qui vous permet de vous concentrer sur l'écriture d'une application plus efficace. Cet article vous explique comment utiliser Yeoman, un workflow d'outils pour applications Web permettant de simplifier la création d'applications à l'aide de Polymer, une bibliothèque de polyfills et de sucres conçue pour développer des applications à l'aide des composants Web.

Yeoman

Faites connaissance avec Yo, Grunt et Bower

Yeoman est un homme dans une casquette qui propose trois outils pour améliorer votre productivité:

  • yo est un outil d'échafaudage qui offre un écosystème d'échafaudages spécifiques au framework, appelés générateurs, qui peuvent être utilisés pour effectuer certaines des tâches fastidieuses mentionnées précédemment.
  • grunt est utilisé pour créer, prévisualiser et tester votre projet, grâce à des tâches préparées par l'équipe Yeoman et grunt-contrib.
  • bower est utilisé pour la gestion des dépendances. Vous n'avez donc plus à télécharger ni à gérer manuellement vos scripts.

Avec une ou deux commandes, Yeoman peut écrire du code récurrent pour votre application (ou pour des éléments individuels tels que des modèles), compiler votre Sass, réduire et concaténer vos CSS, JS, HTML et images, et déclencher un serveur Web simple dans votre répertoire actuel. Il peut également exécuter vos tests unitaires et plus encore.

Vous pouvez installer des générateurs à partir des modules empaquetés de nœuds (npm). Plus de 220 générateurs sont maintenant disponibles, dont beaucoup ont été écrits par la communauté Open Source. Parmi les générateurs populaires, citons générateur-angulaire, réseau backbone de générateurs et générateur-brève.

Page d'accueil de Yeoman

Avec une version récente de Node.js installée, accédez au terminal le plus proche et exécutez la commande suivante:

$ npm install -g yo

Et voilà ! Vous disposez désormais de Yo, Grunt et Bower, que vous pouvez exécuter directement à partir de la ligne de commande. Voici le résultat de l'exécution de yo:

Installation Yeoman

Générateur de polymères

Comme mentionné précédemment, Polymer est une bibliothèque de polyfills et de sucre qui permet d'utiliser des composants Web dans les navigateurs modernes. Ce projet permet aux développeurs de créer des applications à l'aide de la plate-forme de demain et d'informer le W3C des endroits où les spécifications des données de vol peuvent être encore améliorées.

Page d'accueil du générateur de polymères

generator-polymer est un nouveau générateur qui vous aide à structurer des applications Polymer à l'aide de Yeoman. Vous pouvez ainsi créer et personnaliser facilement des éléments Polymer (personnalisés) via la ligne de commande, puis les importer à l'aide d'importations HTML. Cela vous fait gagner du temps en écrivant le code récurrent à votre place.

Ensuite, installez le générateur de Polymer en exécutant la commande suivante:

$ npm install generator-polymer -g

C'est tout. Votre application dispose désormais de super-pouvoirs "Web Component".

Le générateur que nous venons d'installer possède quelques éléments spécifiques auxquels vous aurez accès:

  • polymer:element est utilisé pour échafauder de nouveaux éléments Polymer individuels. Par exemple : yo polymer:element carousel
  • polymer:app permet de structurer votre index.html initial, un fichier Gruntfile.js contenant la configuration de compilation pour votre projet, ainsi que les tâches Grunt et une structure de dossiers recommandée pour le projet. Vous pourrez également utiliser Sass Bootstrap pour les styles de votre projet.

Créons une application Polymer

Nous allons créer un blog simple à l'aide d'éléments Polymer personnalisés et de notre nouveau générateur.

Application Polymer

Pour commencer, accédez au terminal, créez un répertoire et utilisez la commande cd pour y accéder à l'aide de mkdir my-new-project && cd $_. Vous pouvez maintenant lancer votre application Polymer en exécutant la commande suivante:

$ yo polymer
Créer une application Polymer

Bower obtient ainsi la dernière version de Polymer et crée un index.html, une structure de répertoires et des tâches Grunt pour votre workflow. Pourquoi ne pas prendre un café en attendant que l'application soit prête ?

Nous pouvons maintenant exécuter grunt server pour prévisualiser l'application:

Serveur grunt

Le serveur est compatible avec LiveReload, ce qui signifie que vous pouvez lancer un éditeur de texte et modifier un élément personnalisé. Le navigateur s'actualise lors de l'enregistrement. Vous bénéficiez ainsi d'une belle vue en temps réel de l'état actuel de votre application.

Créons maintenant un élément Polymer pour représenter un article de blog.

$ yo polymer:element post
Créer un élément de post

Yeoman nous demande si nous souhaitons inclure un constructeur ou utiliser une importation HTML pour inclure l'élément post dans index.html. Disons Non aux deux premières options pour le moment et laissons la troisième option vide.

$ 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

Cette opération crée un élément Polymer nommé post.html dans le répertoire /elements:

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

Il comprend :

Travailler avec une véritable source de données

Notre blog aura besoin d'un endroit pour écrire et lire les nouveaux articles. Pour illustrer l'utilisation d'un service de données réel, nous allons utiliser l'API Google Apps Sheets. Ainsi, vous pouvez facilement lire le contenu d'une feuille de calcul créée à l'aide de Google Docs.

Passons à la configuration:

  1. Dans votre navigateur (nous vous recommandons d'utiliser Chrome pour suivre cette procédure), ouvrez cette feuille de calcul Google Docs. Elle contient des exemples de données de posts dans les champs suivants:

    • ID
    • Titre
    • Auteur
    • Contenus
    • Date
    • Mots clés
    • Adresse e-mail (de l'auteur)
    • Slug (pour l'URL slug de votre post)
  2. Accédez au menu Fichier et sélectionnez Créer une copie pour créer votre propre copie de la feuille de calcul. Vous êtes libre de modifier le contenu à votre guise, en ajoutant ou en supprimant des posts.

  3. Accédez de nouveau au menu Fichier, puis sélectionnez Publier sur le Web.

  4. Cliquez sur Démarrer la publication.

  5. Sous Obtenir un lien vers les données publiées, copiez la partie clé de l'URL fournie dans la dernière zone de texte. Il se présente comme suit: https://docs.google.com/spreadsheet/ccc?key=0AhcraNy3sgspdDhuQ2pvN21JVW9NeVA0M1h4eGo3RGc#gid=0

  6. Collez la clé dans l'URL suivante, à l'endroit où elle indique votre-clé-goes-here : https://spreadsheets.google.com/feeds/list/your-key-goes-here/od6/public/values?alt=json-in-script&callback=. L'exemple d'utilisation de la clé ci-dessus peut se présenter comme suit : https://spreadsheets.google.com/feeds/list/0AhcraNy3sgspdDhuQ2pvN21JVW9NeVA0M1h4eGo3RGc/od6/public/values?alt=json-in-script

  7. Vous pouvez coller l'URL dans votre navigateur et y accéder pour afficher la version JSON du contenu de votre blog. Notez l'URL, puis prenez le temps d'examiner le format de ces données, car vous devrez les itérer afin de les afficher à l'écran ultérieurement.

La sortie JSON dans votre navigateur peut sembler un peu intimidante, mais ne vous inquiétez pas ! Nous ne nous intéressons qu'aux données de vos posts.

L'API Google Sheets affiche chacun des champs de la feuille de calcul de votre blog avec un préfixe spécial post.gsx$. Exemples: post.gsx$title.$t, post.gsx$author.$t, post.gsx$content.$t, etc. Lorsque nous effectuons une itération sur chaque "ligne" de notre sortie JSON, nous faisons référence à ces champs pour obtenir les valeurs pertinentes pour chaque article.

Vous pouvez désormais modifier l'élément de votre article nouvellement créé pour bind des parties du balisage aux données de votre feuille de calcul. Pour ce faire, nous introduisons un attribut post, qui sera lu pour le titre de l'article, l'auteur, le contenu et d'autres champs que nous avons créés précédemment. L'attribut selected (que nous insérerons ultérieurement) permet d'afficher un post uniquement si un utilisateur accède au slug approprié.

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

Créons maintenant un élément de blog qui contient à la fois une collection d'articles et la mise en page de votre blog en exécutant yo polymer:element blog.

$ 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

Cette fois, nous importons le blog dans le fichier index.html à l'aide d'importations HTML telles que nous aimerions qu'il apparaisse sur la page. Pour la troisième invite, nous spécifions post.html comme élément à inclure.

Comme précédemment, un fichier d'élément (blog.html) est créé et ajouté à /elements, cette fois en important le fichier post.html et en incluant <post-element> dans la balise du modèle:

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

Comme nous avons demandé que l'élément de blog soit importé à l'aide d'importations HTML (un moyen d'inclure et de réutiliser des documents HTML dans d'autres documents HTML) dans notre index, nous pouvons également vérifier qu'il a été correctement ajouté au document <head>:

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

Parfait.

Ajouter des dépendances à l'aide de Bower

Modifions à présent l'élément pour qu'il utilise l'élément utilitaire Polymer JSONP et le lit dans le fichier "posts.json". Vous pouvez obtenir l'adaptateur en clonant le dépôt Git ou en installant polymer-elements via Bower en exécutant bower install polymer-elements.

Dépendances Bower

Une fois que vous disposez de l'utilitaire, vous devez l'inclure en tant qu'importation dans l'élément blog.html avec:

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

Incluez ensuite la balise correspondante et fournissez le url à la feuille de calcul de nos articles de blog que nous avons créée précédemment, en ajoutant &callback= à la fin:

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

Une fois cela en place, nous pouvons maintenant ajouter des modèles pour itérer sur notre feuille de calcul une fois qu'elle a été lue. La première génère une table des matières, avec un titre lié à un article pointant vers le slug correspondant.

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

La seconde affiche une instance de post-element pour chaque entrée trouvée, en lui transmettant le contenu de la publication en conséquence. Notez que nous transmettons un attribut post représentant le contenu de l'article pour une seule ligne de feuille de calcul et un attribut selected que nous remplirons avec un itinéraire.

<!-- Post content -->

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

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

</template>

L'attribut repeat que vous voyez utilisé dans notre modèle crée et gère une instance avec [[bindings ]] pour chaque élément de la collection de tableaux de nos articles, lorsqu'il est fourni.

Application Polymer

Maintenant, pour que la route [[route]] actuelle soit renseignée, nous allons tricher et utiliser une bibliothèque appelée Flatiron Director qui se lie à [[route]] chaque fois que le hachage de l'URL change.

Heureusement, il existe un élément Polymer (qui fait partie du paquet more-elements) que nous pouvons récupérer. Une fois le répertoire /elements copié, nous pouvons le référencer avec <flatiron-director route="[[route]]" autoHash></flatiron-director>, en spécifiant route comme propriété à laquelle nous souhaitons établir la liaison et en lui indiquant de lire automatiquement la valeur de toutes les modifications de hachage (autoHash).

En regroupant tout, nous obtenons maintenant:

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

Bravo ! Nous disposons à présent d'un blog simple qui lit des données à partir d'un fichier JSON et utilise deux éléments Polymer échafaudés avec Yeoman.

Travailler avec des éléments tiers

L'écosystème des composants Web s'est développé récemment, et des sites de galerie de composants tels que customelements.io commencent à apparaître. En parcourant les éléments créés par la communauté, j'en ai trouvé un pour récupérer des profils gravatar. Nous pouvons également le récupérer et l'ajouter à notre site de blog.

Page d&#39;accueil &quot;Éléments personnalisés&quot;

Copiez les sources de l'élément gravatar dans votre répertoire /elements, incluez-les à l'aide d'importations HTML dans post.html, puis ajoutez à votre modèle, en transmettant le champ d'adresse e-mail de notre feuille de calcul comme source du nom d'utilisateur. Génial !

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

Voyons ce que cela donne:

Application Polymer avec éléments personnalisés

Vraiment ?

En peu de temps, nous avons créé une application simple composée de plusieurs composants Web, sans avoir à écrire du code récurrent, à télécharger manuellement les dépendances, ni à configurer un serveur local ou un workflow de compilation.

Optimisez votre application

Le workflow Yeoman comprend un autre projet Open Source appelé Grunt, un exécuteur de tâches capable d'exécuter un certain nombre de tâches spécifiques au build (définies dans un fichier Gruntfile) pour produire une version optimisée de votre application. L'exécution seule de grunt exécute une tâche default que le générateur a configurée pour les fonctions d'analyse lint, de test et de compilation:

grunt.registerTask('default', [

    'jshint',

    'test',

    'build'

]);

La tâche jshint ci-dessus vérifie votre fichier .jshintrc pour connaître vos préférences, puis l'exécute sur tous les fichiers JavaScript de votre projet. Pour connaître toutes les options disponibles avec JSHint, consultez la documentation.

La tâche test ressemble un peu à ceci, et peut créer et diffuser votre application pour le framework de test que nous recommandons immédiatement : Mocha. Il exécutera également vos tests pour vous:

grunt.registerTask('test', [

    'clean:server',

    'createDefaultTemplate',

    'jst',

    'compass',

    'connect:test',

    'mocha'

]);

Notre application étant dans ce cas assez simpliste, nous vous laissons l'écriture de tests comme un exercice distinct. Il existe d'autres éléments que notre processus de compilation doit gérer. Examinons donc ce que va faire la tâche grunt build définie dans notre Gruntfile.js:

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

]);

Exécutez grunt build pour créer une version de votre application prête pour la production, prête à être expédiée. Essayons-la.

Compilation avec grognements

Opération réussie.

Si vous rencontrez des difficultés, vous pouvez consulter une version prédéfinie de polymer-blog sur https://github.com/addyosmani/polymer-blog.

Que avons-nous de plus en réserve ?

Les composants Web sont encore en pleine évolution, tout comme les outils qui les entourent.

Nous étudions actuellement la possibilité de concaténer leurs importations HTML pour améliorer les performances de chargement via des projets tels que Vulcanize (un outil du projet Polymer) et comment l'écosystème des composants peut fonctionner avec un gestionnaire de packages comme Bower.

Nous vous préviendrons dès que nous aurons de meilleures réponses à ces questions, mais de nombreuses perspectives intéressantes nous attendent.

Installer Polymer de manière autonome avec Bower

Si vous préférez un démarrage plus léger pour Polymer, vous pouvez l'installer de manière autonome directement à partir de Bower en exécutant la commande suivante:

bower install polymer

ce qui l'ajoutera à votre répertoire bower_components. Vous pouvez ensuite y faire référence manuellement dans l'index de votre application et ainsi agir pour le futur.

Qu'en pensez-vous ?

Vous savez désormais comment créer une application Polymer à l'aide de composants Web avec Yeoman. Si vous avez des commentaires sur le générateur, n'hésitez pas à nous en faire part en envoyant un bug ou en publiant un message sur l'outil de suivi des problèmes Yeoman. Nous aimerions savoir si vous avez besoin d'autre chose pour voir ce générateur faire encore mieux. En effet, nous ne pouvons l'améliorer que grâce à votre utilisation et vos commentaires.