Crea lo scaffolding delle tue app web con strumenti moderni
Introduzione
Allo’ Allo’. Chiunque scriva un'app web sa quanto sia importante mantenere la produttività. È una sfida quando devi preoccuparti di attività noiose come trovare il boilerplate giusto, configurare un workflow di sviluppo e test e minimizzare e comprimere tutte le tue fonti.
Fortunatamente, i moderni strumenti di front-end possono aiutare ad automatizzare gran parte di questo processo, lasciandoti libero di concentrarti sulla scrittura di un'app fantastica. Questo articolo ti mostrerà come utilizzare Yeoman, un flusso di lavoro di strumenti per app web per semplificare la creazione di app utilizzando Polymer, una libreria di polyfill e zucchero per lo sviluppo di app utilizzando Componenti web.
Ecco Yo, Grunt e Bower
Yeoman è un uomo con un cappello e tre strumenti per migliorare la tua produttività:
- yo è uno strumento di scaffolding che offre un ecosistema di scaffold specifici per i framework, chiamati generatori, che possono essere utilizzati per eseguire alcune delle attività noiose che ho menzionato in precedenza.
- grunt viene utilizzato per creare, visualizzare in anteprima e testare il progetto, grazie all'aiuto di attività curate dal team di Yeoman e da grunt-contrib.
- bower viene utilizzato per la gestione delle dipendenze, in modo che non sia più necessario scaricare e gestire manualmente gli script.
Con uno o due comandi, Yeoman può scrivere codice boilerplate per la tua app (o singole parti come i modelli), compilare il codice Sass, minimizzare e concatenare CSS, JS, HTML e immagini e avviare un semplice server web nella directory corrente. Può anche eseguire i test delle unità e altro ancora.
Puoi installare i generatori da Node Packaged Modules (npm). Al momento sono disponibili oltre 220 generatori, molti dei quali sono stati scritti dalla community open source. I generatori più utilizzati includono generator-angular, generator-backbone e generator-ember.
Con una versione recente di Node.js installata, vai al terminale più vicino ed esegui:
$ npm install -g yo
È tutto. Ora hai Yo, Grunt e Bower e puoi eseguirli direttamente dalla riga di comando. Ecco l'output dell'esecuzione di yo:
Generatore di polimeri
Come ho detto prima, Polymer è una libreria di polyfill e sugar che consente l'utilizzo di componenti web nei browser moderni. Il progetto consente agli sviluppatori di creare app utilizzando la piattaforma del futuro e di informare il W3C dei punti in cui le specifiche in corso di sviluppo possono essere ulteriormente migliorate.
generator-polymer è un nuovo generatore che ti aiuta a creare lo scaffolding delle app Polymer utilizzando Yeoman, consentendoti di creare e personalizzare facilmente gli elementi Polymer (personalizzati) tramite la riga di comando e di importarli utilizzando le importazioni HTML. In questo modo risparmi tempo perché il codice boilerplate viene scritto per te.
Poi, installa il generatore di Polymer eseguendo:
$ npm install generator-polymer -g
È tutto. Ora la tua app ha i superpoteri dei componenti web.
Il nostro generatore appena installato ha alcuni elementi specifici a cui avrai accesso:
polymer:elementviene utilizzato per creare nuovi singoli elementi Polymer. Ad esempio:yo polymer:element carouselpolymer:appviene utilizzato per creare lo scaffolding del file index.html iniziale, un file Gruntfile.js contenente la configurazione del tempo di build per il progetto, nonché le attività Grunt e una struttura di cartelle consigliata per il progetto. Ti darà anche la possibilità di utilizzare Sass Bootstrap per gli stili del tuo progetto.
Creiamo un'app Polymer
Creeremo un semplice blog utilizzando alcuni elementi Polymer personalizzati e il nostro nuovo generatore.
Per iniziare, vai al terminale, crea una nuova directory e accedi tramite cd utilizzando mkdir my-new-project && cd $_. Ora puoi avviare l'app Polymer eseguendo:
$ yo polymer
In questo modo, viene scaricata l'ultima versione di Polymer da Bower e viene creata una struttura di directory, un file index.html e attività Grunt per il tuo workflow. Perché non prendi un caffè mentre aspettiamo che l'app sia pronta?
Ora possiamo eseguire grunt server per visualizzare l'anteprima dell'app:
Il server supporta LiveReload, il che significa che puoi aprire un editor di testo, modificare un elemento personalizzato e il browser si ricaricherà al salvataggio. In questo modo, avrai una panoramica in tempo reale dello stato attuale della tua app.
Ora creiamo un nuovo elemento Polymer per rappresentare un post del blog.
$ yo polymer:element post
Yeoman ci pone alcune domande, ad esempio se vogliamo includere un costruttore o utilizzare un'importazione HTML per includere l'elemento post in index.html. Per il momento, diciamo No alle prime due opzioni e lasciamo vuota la terza.
$ 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
Verrà creato un nuovo elemento Polymer nella directory /elements denominato post.html:
<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>
Contiene:
- Codice boilerplate per l'elemento personalizzato, che ti consente di utilizzare un tipo di elemento DOM personalizzato nella pagina (ad es.
<post-element>) - Un tag template per i modelli lato client "nativi" e stili con ambito di esempio per incapsulare gli stili del tuo elemento
- Boilerplate di registrazione degli elementi ed eventi del ciclo di vita.
Utilizzo di un'origine dati reale
Il nostro blog avrà bisogno di un posto dove scrivere e leggere nuovi post. Per dimostrare come lavorare con un servizio di dati reale, utilizzeremo l'API Google Apps Spreadsheets. In questo modo possiamo leggere facilmente i contenuti di qualsiasi foglio di lavoro creato con Documenti Google.
Procediamo con la configurazione:
Nel browser (per questi passaggi è consigliato Chrome), apri questo foglio di lavoro di Documenti Google. Contiene dati di esempio dei post nei seguenti campi:
- ID
- Titolo
- Autore
- Contenuti
- Data
- Parole chiave
- Indirizzo email (dell'autore)
- Slug (per l'URL dello slug del post)
Vai al menu File e seleziona Crea una copia per creare la tua copia del foglio di lavoro. Puoi modificare i contenuti a tuo piacimento, aggiungendo o rimuovendo post.
Vai di nuovo al menu File e seleziona Pubblica sul web.
Fai clic su Inizia la pubblicazione.
In Ottieni un link ai dati pubblicati, nell'ultima casella di testo, copia la parte key dell'URL fornito. Ecco un esempio: https://docs.google.com/spreadsheet/ccc?key=0AhcraNy3sgspdDhuQ2pvN21JVW9NeVA0M1h4eGo3RGc#gid=0
Incolla la chiave nel seguente URL, dove è presente la dicitura your-key-goes-here: https://spreadsheets.google.com/feeds/list/your-key-goes-here/od6/public/values?alt=json-in-script&callback=. Un esempio che utilizza la chiave precedente potrebbe essere https://spreadsheets.google.com/feeds/list/0AhcraNy3sgspdDhuQ2pvN21JVW9NeVA0M1h4eGo3RGc/od6/public/values?alt=json-in-script.
Puoi incollare l'URL nel browser e accedervi per visualizzare la versione JSON dei contenuti del tuo blog. Prendi nota dell'URL, quindi dedica un po' di tempo a esaminare il formato di questi dati, poiché dovrai eseguirne l'iterazione per visualizzarli sullo schermo in un secondo momento.
L'output JSON nel browser potrebbe sembrare un po' scoraggiante, ma non preoccuparti. Ci interessano solo i dati relativi ai tuoi post.
L'API Google Spreadsheets restituisce ciascuno dei campi del foglio di lavoro del blog con un prefisso speciale post.gsx$. Ad esempio: post.gsx$title.$t, post.gsx$author.$t, post.gsx$content.$t e così via. Quando eseguiamo l'iterazione su ogni "riga" dell'output JSON, faremo riferimento a questi campi per recuperare i valori pertinenti per ogni post.
Ora puoi modificare l'elemento del post appena creato per associare parti di markup ai dati nel foglio di lavoro. Per farlo, introduciamo un attributo post, che leggerà il titolo, l'autore, i contenuti e altri campi del post che abbiamo creato in precedenza. L'attributo selected (che compileremo in un secondo momento) viene utilizzato per mostrare un post solo se un utente accede allo slug corretto.
<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>
Ora creiamo un elemento del blog che contenga sia una raccolta di post sia il layout del tuo blog eseguendo 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
Questa volta importiamo il blog in index.html utilizzando le importazioni HTML, in modo che venga visualizzato nella pagina. In particolare, per il terzo prompt, specifichiamo post.html come elemento che vorremmo includere.
Come prima, viene creato un nuovo file di elementi (blog.html) e aggiunto a /elements, questa volta importando post.html e includendo <post-element> all'interno del tag template:
<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>
Poiché abbiamo chiesto di importare l'elemento del blog utilizzando le importazioni HTML (un modo per includere e riutilizzare i documenti HTML in altri documenti HTML) nel nostro indice, possiamo anche verificare che sia stato aggiunto correttamente al documento <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>
Fantastico.
Aggiunta di dipendenze utilizzando Bower
Ora modifichiamo il nostro elemento in modo che utilizzi l'elemento di utilità Polymer JSONP per leggere posts.json. Puoi ottenere l'adattatore clonando il repository con Git o installando polymer-elements tramite Bower eseguendo bower install polymer-elements.
Una volta ottenuta l'utilità, dovrai includerla come importazione nell'elemento blog.html con:
<link rel="import" href="../bower_components/polymer-jsonp/polymer-jsonp.html">
Poi, includi il tag corrispondente e fornisci l'url al nostro foglio di lavoro dei blog post di prima, aggiungendo &callback= alla fine:
<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>
A questo punto, possiamo aggiungere i modelli per eseguire l'iterazione sul foglio di lavoro una volta letto. Il primo genera un sommario, con un titolo collegato per un post che rimanda allo slug corrispondente.
<!-- 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>
Il secondo esegue il rendering di un'istanza di post-element per ogni voce trovata, trasmettendo di conseguenza i contenuti del post. Tieni presente che stiamo passando un attributo post che rappresenta i contenuti del post per una singola riga del foglio di lavoro e un attributo selected che verrà compilato con un percorso.
<!-- Post content -->
<template repeat="[[post in posts.feed.entry]]">
<post-element post="[[post]]" selected="[[route]]"></post-element>
</template>
L'attributo repeat che vedi utilizzato nel nostro template crea e gestisce un'istanza con [[ bindings ]] per ogni elemento nella raccolta di array dei nostri post, quando viene fornito.
Ora, per compilare l'oggetto [[route]] corrente, utilizzeremo un piccolo trucco e useremo una libreria chiamata Flatiron director che si associa a [[route]] ogni volta che l'hash dell'URL cambia.
Fortunatamente, esiste un elemento Polymer (parte del pacchetto more-elements) che possiamo utilizzare. Una volta copiato nella directory /elements, possiamo farvi riferimento con <flatiron-director route="[[route]]" autoHash></flatiron-director>, specificando route come proprietà a cui vogliamo eseguire il binding e indicandogli di leggere automaticamente il valore di eventuali modifiche dell'hash (autoHash).
Mettendo insieme tutti gli elementi, otteniamo:
<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>
Evviva! Ora abbiamo un blog semplice che legge i dati da JSON e utilizza due elementi Polymer con scaffolding di Yeoman.
Utilizzo di elementi di terze parti
L'ecosistema di elementi attorno ai componenti web è cresciuto di recente con la comparsa di siti di gallerie di componenti come customelements.io. Esaminando gli elementi creati dalla community, ne ho trovato uno per recuperare i profili Gravatar e possiamo effettivamente prenderlo e aggiungerlo anche al nostro sito del blog.
Copia le origini degli elementi gravatar nella directory /elements, includile tramite le importazioni HTML in post.html e poi aggiungi
<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>
Vediamo cosa ci offre:
Bella!
In un tempo relativamente breve, abbiamo creato una semplice applicazione composta da diversi componenti web senza doverci preoccupare di scrivere codice boilerplate, scaricare manualmente le dipendenze o configurare un server locale o un flusso di lavoro di build.
Ottimizzazione dell'applicazione
Il flusso di lavoro di Yeoman include un altro progetto open source chiamato Grunt, un task runner che può eseguire una serie di attività specifiche della build (definite in un Gruntfile) per produrre una versione ottimizzata della tua applicazione. L'esecuzione di grunt da solo eseguirà un'attività default che il generatore ha configurato per l'analisi lint, il test e la build:
grunt.registerTask('default', [
'jshint',
'test',
'build'
]);
L'attività jshint precedente controllerà il file .jshintrc per conoscere le tue preferenze, quindi lo eseguirà su tutti i file JavaScript del tuo progetto. Per un elenco completo delle opzioni disponibili con JSHint, consulta la documentazione.
L'attività test è simile a questa e può creare e pubblicare la tua app per il framework di test che consigliamo, Mocha. Inoltre, eseguirà i test per te:
grunt.registerTask('test', [
'clean:server',
'createDefaultTemplate',
'jst',
'compass',
'connect:test',
'mocha'
]);
Poiché la nostra app in questo caso è piuttosto semplice, ti lasceremo scrivere i test come esercizio separato. Ci sono alcune altre cose che dobbiamo far gestire al nostro processo di build, quindi diamo un'occhiata a cosa farà l'attività grunt build definita nel nostro 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
]);
Esegui grunt build per creare una versione della tua app pronta per la produzione e la spedizione. Proviamo.
Operazione riuscita.
Se hai difficoltà, puoi consultare una versione predefinita di polymer-blog all'indirizzo https://github.com/addyosmani/polymer-blog.
Cosa abbiamo in serbo per il futuro?
I componenti web sono ancora in fase di evoluzione e lo stesso vale per gli strumenti che li circondano.
Al momento stiamo valutando come concatenare le importazioni HTML per migliorare le prestazioni di caricamento tramite progetti come Vulcanize (uno strumento del progetto Polymer) e come l'ecosistema dei componenti potrebbe funzionare con un gestore di pacchetti come Bower.
Ti faremo sapere quando avremo risposte migliori a queste domande, ma ci aspettano tempi entusiasmanti.
Installazione di Polymer autonomo con Bower
Se preferisci un approccio più leggero a Polymer, puoi installarlo in modo autonomo direttamente da Bower eseguendo:
bower install polymer
che lo aggiungerà alla directory bower_components. Puoi quindi farvi riferimento manualmente nell'indice dell'applicazione e prepararti per il futuro.
Cosa ne pensi?
Ora sai come creare lo scaffolding di un'app Polymer utilizzando i componenti web con Yeoman. Se hai feedback sul generatore, faccelo sapere nei commenti, segnala un bug o pubblica un post nell'Issue Tracker di Yeoman. Ci piacerebbe sapere se c'è qualcos'altro che vorresti che il generatore facesse meglio, poiché è solo attraverso il tuo utilizzo e il tuo feedback che possiamo migliorare :)