Tworzenie aplikacji internetowych za pomocą Yeoman i Polymer

Tworzenie szablonów stron internetowych za pomocą nowoczesnych narzędzi

Addy Osmani
Addy Osmani

Wprowadzenie

Allo, allo. Każdy, kto pisze aplikacje internetowe, wie, jak ważne jest, aby zachować produktywność. To wyzwanie, gdy trzeba się martwić o nudne zadania, takie jak znalezienie odpowiedniego szablonu, skonfigurowanie procesu tworzenia i testowania oraz zminimalizowanie i skompresowanie wszystkich źródeł.

Na szczęście nowoczesne narzędzia do front-endu mogą pomóc w zautomatyzowaniu wielu z tych zadań, dzięki czemu możesz skupić się na tworzeniu świetnej aplikacji. W tym artykule pokażemy Ci, jak używać Yeoman, czyli zestawu narzędzi do tworzenia aplikacji internetowych, który pozwala uprościć tworzenie aplikacji za pomocą Polymer, czyli biblioteki polyfillów i elementów ułatwiających tworzenie aplikacji za pomocą Web Components.

Yeoman

Poznaj Yo, Grunt i Bower

Yeoman to mężczyzna w kapeluszu, który oferuje 3 narzędzia do zwiększania produktywności:

  • yo to narzędzie do tworzenia szkieletu, które oferuje ekosystem szkieletów dla poszczególnych frameworków, zwanych generatorami, które można wykorzystać do wykonania niektórych żmudnych zadań, o których wspominałem wcześniej.
  • grunt służy do kompilowania, wyświetlania podglądu i testowania projektu dzięki zadaniom przygotowanym przez zespół Yeoman i grunt-contrib.
  • Bower służy do zarządzania zależnościami, dzięki czemu nie musisz już ręcznie pobierać skryptów ani nimi zarządzać.

Za pomocą zaledwie kilku poleceń Yeoman może napisać kod szablonowy dla aplikacji (lub poszczególnych elementów, takich jak modele), skompilować Sass, zminimalizować i zkonkatenować kod CSS, JS, HTML i obrazy oraz uruchomić prosty serwer internetowy w bieżącym katalogu. Może też uruchamiać testy jednostkowe i inne testy.

Generatory możesz instalować z Node Packaged Modules (npm). Obecnie dostępnych jest ponad 220 generatorów, z których wiele zostało napisanych przez społeczność zajmującą się oprogramowaniem open source. Popularne generatory to generator-angular, generator-backbone i generator-ember.

Strona główna Yeomana

Po zainstalowaniu najnowszej wersji Node.js otwórz terminal i uruchom:

$ npm install -g yo

Znakomicie. Teraz masz Yo, Grunt i Bower, które możesz uruchamiać bezpośrednio z poziomu wiersza poleceń. Oto dane wyjściowe po uruchomieniu yo:

Instalacja Yeoman

Polymer Generator

Jak już wspomniałem, Polymer to biblioteka elementów polyfill i sugar, która umożliwia korzystanie z komponentów sieciowych w nowoczesnych przeglądarkach. Projekt pozwala deweloperom tworzyć aplikacje na platformie przyszłości i informować W3C o miejscach, w których specyfikacje w trakcie lotu można jeszcze ulepszyć.

Strona główna generatora polimerów

generator-polymer to nowy generator, który pomaga tworzyć szablony aplikacji Polymer za pomocą Yeomana. Umożliwia on łatwe tworzenie i dostosowywanie elementów Polymer (niestandardowych) za pomocą wiersza poleceń oraz importowanie ich za pomocą importu HTML. Dzięki temu zaoszczędzisz czas, ponieważ nie musisz pisać szablonowego kodu.

Następnie zainstaluj generator Polymer, uruchamiając:

$ npm install generator-polymer -g

To wszystko. Twoja aplikacja ma teraz supermoce komponentu sieciowego.

Nasz nowo zainstalowany generator ma kilka elementów, do których będziesz mieć dostęp:

  • polymer:element służy do tworzenia nowych elementów Polymer. Na przykład: yo polymer:element carousel
  • polymer:app służy do tworzenia szablonu początkowego pliku index.html, pliku Gruntfile.js zawierającego konfigurację projektu na czas kompilacji, a także zadań Grunt i struktury folderów zalecanej dla projektu. Da Ci też możliwość korzystania z Sass Bootstrap do stylów projektu.

Tworzenie aplikacji Polymer

Utworzymy prosty blog, korzystając z niestandardowych elementów Polymer i nowego generatora.

Aplikacja Polymer

Aby rozpocząć, otwórz terminal, utwórz nowy katalog i przejdź do niego za pomocą polecenia mkdir my-new-project && cd $_. Teraz możesz rozpocząć tworzenie aplikacji Polymer, wykonując te czynności:

$ yo polymer
Kompilowanie aplikacji Polymer

Pobiera najnowszą wersję Polymera z Bowera i tworzy szablony index.html, strukturę katalogu oraz zadania Grunta do użycia w Twoim przepływie pracy. Może podczas oczekiwania na przygotowanie aplikacji napijesz się kawy?

Teraz możemy uruchomić grunt server, aby zobaczyć, jak wygląda aplikacja:

Grunt server

Serwer obsługuje funkcję LiveReload, co oznacza, że możesz uruchomić edytor tekstu, edytować element niestandardowy, a przeglądarka ponownie wczyta stronę po zapisaniu. Dzięki temu możesz zobaczyć bieżący stan aplikacji w czasie rzeczywistym.

Następnie utwórz nowy element Polymer reprezentujący wpis na blogu.

$ yo polymer:element post
Tworzenie elementu posta

Yeoman zadaje nam kilka pytań, np. czy chcemy umieścić konstruktor czy użyć importu HTML, aby dodać element post w index.html. Na razie odłóżmy na bok pierwsze 2 opcje i pozostawmy puste pole trzeciej opcji.

$ 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

Spowoduje to utworzenie nowego elementu Polymer w katalogu /elements o nazwie 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>

Tabela zawiera:

Praca z rzeczywistym źródłem danych

Nasz blog będzie potrzebował miejsca na pisanie i czytanie nowych postów. Aby zademonstrować pracę z prawdziwą usługą danych, użyjemy interfejsu Google Apps Spreadsheets API. Dzięki temu możemy łatwo odczytać zawartość dowolnego arkusza kalkulacyjnego utworzonego w Dokumentach Google.

Przejdźmy do konfiguracji:

  1. W przeglądarce (zalecamy Chrome) otwórz arkusz kalkulacyjny Dokumentów Google. Zawiera on przykładowe dane postu w tych polach:

    • Identyfikator
    • Tytuł
    • Autor
    • Treść
    • Data
    • Słowa kluczowe
    • Adres e-mail (autora)
    • Slug (adres URL slugu posta)
  2. Aby utworzyć własną kopię arkusza kalkulacyjnego, otwórz menu Plik i kliknij Utwórz kopię. Możesz edytować treści według własnego uznania, dodając lub usuwając posty.

  3. Ponownie otwórz menu Plik i wybierz Opublikuj w internecie.

  4. Kliknij Rozpocznij publikowanie.

  5. W sekcji Pobierz link do opublikowanych danych w ostatnim polu tekstowym skopiuj klucz z podawanego adresu URL. Wygląda on tak: https://docs.google.com/spreadsheet/ccc?key=0AhcraNy3sgspdDhuQ2pvN21JVW9NeVA0M1h4eGo3RGc#gid=0

  6. Wklej klucz w miejscu, w którym w tym adresie URL widnieje tekst twoj-klucz-znajduje-się-tutaj: https://spreadsheets.google.com/feeds/list/your-key-goes-here/od6/public/values?alt=json-in-script&callback=. Przykładowy adres URL z użyciem tego klucza może wyglądać tak: https://spreadsheets.google.com/feeds/list/0AhcraNy3sgspdDhuQ2pvN21JVW9NeVA0M1h4eGo3RGc/od6/public/values?alt=json-in-script.

  7. Możesz wkleić adres URL w przeglądarce i przejść do niego, aby wyświetlić wersję treści bloga w formacie JSON. Zapisz adres URL, a potem poświęć trochę czasu na zapoznanie się z formatem tych danych, ponieważ będziesz musiał je później wyświetlać na ekranie.

Dane wyjściowe w formacie JSON w przeglądarce mogą wyglądać nieco przerażająco, ale nie martw się. Interesują nas tylko dane dotyczące Twoich postów.

Interfejs Google Sheets API zwraca każde pole w arkuszu bloga ze specjalnym prefiksem post.gsx$. Na przykład: post.gsx$title.$t, post.gsx$author.$t, post.gsx$content.$t itd. Gdy przetwarzamy każdy „wiersz” w wyniku w formacie JSON, odwołujemy się do tych pól, aby pobrać odpowiednie wartości dla każdego posta.

Możesz teraz edytować element posta utworzony na podstawie szablonu, aby połączyć fragmenty znaczników z danymi w arkuszu kalkulacyjnym. W tym celu wprowadzamy atrybut post, który będzie odczytywany w przypadku tytułu, autora, treści i innych wcześniej utworzonych pól. Atrybut selected (który wypełnimy później) służy do wyświetlania posta tylko wtedy, gdy użytkownik przejdzie do jego prawidłowej slugi.

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

Teraz utwórz element bloga, który zawiera zarówno kolekcję postów, jak i schemat bloga. Aby to zrobić, uruchom 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

Tym razem importujemy blog do pliku index.html za pomocą importu kodu HTML, aby uzyskać pożądany wygląd strony. W przypadku trzeciego promptu jako element do uwzględnienia wskazujemy post.html.

Podobnie jak poprzednio, tworzymy nowy plik elementu (blog.html) i dodajemy go do folderu /elements. Tym razem importujemy plik post.html i wstawiamy tag szablonu <post-element>:

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

Ponieważ poprosiliśmy o zaimportowanie elementu bloga do naszego indeksu za pomocą importu HTML (sposób uwzględniania i ponownego używania dokumentów HTML w innych dokumentach HTML), możemy też sprawdzić, czy został on prawidłowo dodany do dokumentu <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>

Fantastycznie.

Dodawanie zależności za pomocą Bower

Następnie zmodyfikujemy nasz element, aby używał elementu narzędziowego Polymer JSONP do odczytu pliku posts.json. Możesz pobrać adapter, klonując repozytorium za pomocą git lub instalując polymer-elements za pomocą Bower, uruchamiając bower install polymer-elements.

Zależności Bower

Po pobraniu narzędzia musisz je zaimportować do elementu blog.html, podając:

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

Następnie dodaj tag i wartość url do naszego arkusza z postami na blogu, dodając na końcu &callback=:

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

Dzięki temu możemy teraz dodawać szablony, które będą iterować w arkuszu kalkulacyjnym po jego odczytaniu. Pierwszy z nich wyświetla spis treści z linkiem do tytułu posta, który wskazuje na jego slug.

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

Drugi renderuje jedną instancję post-element dla każdego znalezionego wpisu, przekazując mu odpowiednio treść posta. Zwróć uwagę, że przekazujemy atrybut post, który reprezentuje treść posta w pojedynczym wierszu arkusza kalkulacyjnego, oraz atrybut selected, który wypełnimy trasą.

<!-- Post content -->

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

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

</template>

Atrybut repeat, który jest używany w naszym szablonie, tworzy i utrzymuje instancję za pomocą [[ bindings ]] dla każdego elementu w zbiorze tablic naszych postów, gdy jest on podany.

Aplikacja Polymer

Aby wypełnić bieżącą wartość [[route]], użyjemy skrótu i biblioteki Flatiron director, która łączy się z [[route]] za każdym razem, gdy zmienia się hasz adresu URL.

Na szczęście mamy element polimerowy (część pakietu więcej elementów), który możemy wykorzystać. Po skopiowaniu do katalogu /elements możemy się do niego odwoływać za pomocą <flatiron-director route="[[route]]" autoHash></flatiron-director>, podając route jako właściwość, do której chcemy ją powiązać, i podając instrukcję automatycznego odczytywania wartości wszelkich zmian haszowania (autoHash).

Po połączeniu wszystkich elementów otrzymujemy:

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

Super! Mamy teraz prosty blog, który odczytuje dane z pliku JSON i użyje 2 elementów Polymer utworzonych za pomocą Yeomana.

Praca z elementami innych firm

W ostatnim czasie rośnie ekosystem elementów związanych z komponentami internetowymi. Pojawiają się też strony z galeriami komponentów, takie jak customelements.io. Przeglądając elementy stworzone przez społeczność, znalazłem jeden, który służy do pobierania profili Gravatar. Możemy go też pobrać i dodać do naszego bloga.

Strona główna elementów niestandardowych

Skopiuj źródła elementów gravatara do katalogu /elements, dodaj je za pomocą importu HTML w pliku post.html, a następnie dodaj do szablonu, podając pole adresu e-mail z naszego arkusza kalkulacyjnego jako źródło nazwy użytkownika. 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>

Zobaczmy, co nam to daje:

Aplikacja Polymer z elementami niestandardowymi

Pięknie.

W relatywnie krótkim czasie udało nam się utworzyć prostą aplikację składającą się z kilku komponentów internetowych bez konieczności pisania szablonowego kodu, ręcznego pobierania zależności czy konfigurowania serwera lokalnego lub procesu kompilacji.

Optymalizacja aplikacji

Przepływ pracy Yeoman obejmuje inny projekt typu open source o nazwie Grunt – narzędzie do wykonywania zadań, które może wykonywać wiele zadań związanych z kompilacją (zdefiniowanych w pliku Grunt), aby wygenerować zoptymalizowaną wersję aplikacji. Uruchamianie grunt spowoduje wykonanie zadania default skonfigurowanego przez generatora do sprawdzania błędów, testowania i tworzenia kompilacji:

grunt.registerTask('default', [

    'jshint',

    'test',

    'build'

]);

Opisane powyżej zadanie jshint sprawdzi plik .jshintrc, aby poznać Twoje preferencje, a potem uruchomi je we wszystkich plikach JavaScript w Twoim projekcie. Aby uzyskać pełną listę opcji JSHint, zapoznaj się z dokumentacją.

Zadanie test wygląda mniej więcej tak i może tworzyć i obsługiwać aplikację w ramach zalecanej przez nas platformy testowej Mocha. Skrypt wykona też za Ciebie testy:

grunt.registerTask('test', [

    'clean:server',

    'createDefaultTemplate',

    'jst',

    'compass',

    'connect:test',

    'mocha'

]);

Ponieważ nasza aplikacja jest w tym przypadku dość prosta, testy napiszecie samodzielnie. Proces kompilacji musi też wykonać kilka innych zadań. Zobaczmy, co zrobi zadanie grunt build zdefiniowane w naszym pliku 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

]);

Uruchom grunt build, a wersja produkcyjna aplikacji powinna zostać skompilowana i gotowa do wdrożenia. Wypróbujmy to.

Grunt build

Gotowe!

Jeśli utkniesz, możesz skorzystać z gotowej wersji aplikacji polymer-blog, którą znajdziesz na stronie https://github.com/addyosmani/polymer-blog.

Co jeszcze mamy do zaoferowania?

Komponenty internetowe są wciąż rozwijane, podobnie jak narzędzia do ich obsługi.

Obecnie badamy, jak można łączyć importowane pliki HTML, aby poprawić wydajność wczytywania za pomocą takich projektów jak Vulcanize (narzędzie projektu Polymer) oraz jak ekosystem komponentów może współpracować z menedżerem pakietów takim jak Bower.

Poinformujemy Cię, gdy będziemy mieć więcej informacji na ten temat. Przed nami wiele ekscytujących wydarzeń.

Instalowanie samodzielnego pakietu Polymer za pomocą Bower

Jeśli wolisz lżejszą wersję Polymer, możesz zainstalować ją samodzielnie bezpośrednio z Bowera, wykonując te czynności:

bower install polymer

co spowoduje dodanie go do katalogu bower_components. Następnie możesz ręcznie odnieść się do niego w indeksie aplikacji i zacząć korzystać z nowych możliwości.

Co myślisz?

Teraz już wiesz, jak za pomocą Yeomana utworzyć szablon aplikacji Polymer, korzystając z Web Components. Jeśli chcesz podzielić się opinią na temat generatora, daj nam znać w komentarzach lub prześlij zgłoszenie błędu lub wiadomość do śledzenia problemów w Yeoman. Chcielibyśmy się dowiedzieć, czy jest coś, co według Ciebie generator powinien robić lepiej. Tylko dzięki korzystaniu z niego i Twoim opiniom możemy go ulepszać.