Ha(c)ki na… HTML 5


Hack No.76

Skonfiguruj Amazon S3 do udostępniania zasobów między źródłami, aby hostować czcionkę internetową

Udostępnianie zasobów między źródłami (CORS) to specyfikacja, która umożliwia aplikacji wysyłanie żądań do innych domen z poziomu przeglądarki. Dzięki CORS masz bezpieczne i łatwe do wdrożenia podejście do omijania tej samej zasady pochodzenia przeglądarki.

W tym hacku przyjrzymy się hostowaniu czcionek internetowych na dysku w chmurze. Aby to zrobić, nauczymy się, jak skonfigurować zasobnik Amazon S3, aby akceptował żądania z innych domen. Jeśli nie znasz jeszcze czcionek internetowych i @ font-face. W następnej sekcji przedstawiam nieco więcej informacji na temat Amazon S3 i tych samych zasad pochodzenia, zanim przejdziemy do szczegółów CORS.

Co to jest Amazon S3 Bucket?

Amazon S3 (Simple Storage Service) to po prostu dysk w chmurze. Za pomocą tej usługi można przechowywać wszelkiego rodzaju pliki, ale programiści aplikacji internetowych często używają jej do przechowywania zasobów statycznych, takich jak obrazy, pliki JavaScript i arkusze stylów. Aby zwiększyć wydajność, twórcy stron internetowych lubią używać sieci dostarczania treści (CDN) do obsługi swoich plików statycznych. Chociaż Amazon S3 sam w sobie nie jest CDN, łatwo go aktywować jako jeden za pomocą CloudFront. Zasobnik odnosi się do nazwy katalogu, który wybierasz do przechowywania plików statycznych. Aby rozpocząć, załóżmy konto w Amazon i przejdź do Amazon Management Console. Jeśli klikniemy Utwórz wiadro, powinien pojawić się pokazany monit. Nazwijmy zasobnik i wybierzmy region. Jak wspomniałem wcześniej, możesz wybrać region, aby zoptymalizować opóźnienia, zminimalizować koszty lub spełnić wymagania prawne. Pójdziemy dalej i nazwiemy nasze wiadro niczym innym jak "html5hacks". Powinieneś teraz zobaczyć ekran administratora, który pokazuje pusty system plików. Cóż, to było proste. Więc dlaczego to robimy? Zacznijmy od prostego zabezpieczenia przeglądarki, nazywanego tą samą polityką pochodzenia.

Polityka tego samego pochodzenia

W miarę jak przeglądarka staje się coraz bardziej platformą aplikacji, twórcy aplikacji mają istotne powody, aby pisać kod, który wysyła żądania do innych domen w celu bezpośredniej interakcji z zawartością. Wikipedia definiuje te same zasady pochodzenia w następujący sposób:

W przypadku komputerów ta sama zasada pochodzenia jest ważną koncepcją zabezpieczeń dla wielu języków programowania po stronie przeglądarki, takich jak JavaScript. Polityka zezwala na uruchamianie skryptów na stronach, z których pochodzi z tej samej witryny, aby uzyskać wzajemny dostęp do metod i właściwości bez żadnych szczególnych ograniczeń, ale uniemożliwiają dostęp do większości metod i właściwości na stronach w różnych witrynach.

Jak stwierdzono w definicji Wikipedii, ta sama polityka pochodzenia jest dobra; chroni użytkownika końcowego przed atakami bezpieczeństwa. Ale stwarza pewne wyzwania dla twórców stron internetowych. I tu pojawia się CORS. CORS umożliwia deweloperom zdalnych danych i treści wyznaczenie, które domeny (za pośrednictwem białej listy) mogą wchodzić w interakcje z ich zawartością.

Korzystanie z czcionek internetowych w aplikacji

Istnieje wiele sposobów używania czcionki internetowej na swoich stronach internetowych, takich jak wywołanie usługi @ font-face, łączenie czcionki w aplikacji, hostowanie czcionki internetowej we własnym zasobniku Amazon S3 (więcej na ten temat później), lub konwertowanie pliku do Base64 i osadzanie danych w tekście Data-Uri. Nawiasem mówiąc, ostatnia technika jest podobna do tej opisanej wcześniej w innym hacku. Każda z tych technik ma ograniczenia.

•  Dzwoniąc do usługi @ font-face jesteś ograniczony do czcionek z bazy danych usługi.

•  Pakowanie czcionki w aplikacji nie korzysta z protokołu buforowania HTTP, więc aplikacja będzie nadal pobierać plik czcionki przy każdym żądaniu strony. Ponadto nie można ponownie używać czcionki w innych aplikacjach.

•  Hostowanie czcionki w zasobniku Amazon S3 działa świetnie, z wyjątkiem przeglądarki Firefox, która wymusza tę samą zasadę pochodzenia na wszystkich zasobach. Więc odpowiedź ze zdalnego serwera zostanie odrzucona.

•  Konwersja czcionki na Base64 dodaje dodatkową wagę do arkusza stylów i nie wykorzystuje buforowania.

Omówienie różnych typów czcionek internetowych wykracza poza zakres tego hacka, więc założę, że wybrałeś już czcionkę internetową BebasNeue.otf. Możesz pobrać bezpłatne i otwarte czcionki z witryn, takich jak dafont.com.

Przesyłanie czcionki do Amazon S3 Bucket

Teraz wszystko, co musimy zrobić, to przesłać czcionkę do naszego systemu plików w chmurze

Dodawanie czcionki internetowej do strony internetowej

Aby dodać czcionkę internetową do naszej strony, musimy dodać pojedynczy arkusz stylów do strony HTML. Oto nasza strona. Nazwijmy to index.html i dodajmy tag wskazujący na nasz podstawowy arkusz stylów, styles.css.

< html >

< head >

< title > S3 - font< /title >

< meta charset="utf-8" / >

< link rel="stylesheet" type="text/css" href="styles.css" >

< /head >

< body >

< h1 class="test" >HTML5 Hacks< / >

< /body >

< /html >

W naszym styles.css dodajmy następujący i wskaż nasz przesłany plik. Przypiszmy też czcionkę do naszego nagłówka H1 poprzez nazwę klasy testowej.

@font-face { font-family: BebasNeue; src:

url('https://s3.amazonaws.com/html5hacks/BebasNeue.otf'); }

.test {

font-family: 'BebasNeue';

}

Teraz otworzymy przeglądarkę i wskażemy naszą nowo utworzoną stronę HTML. W przeglądarce Opera, Safari i Chrome nasz tag nagłówka jest prawidłowo stylizowany. Ale jeśli zobaczymy to w przeglądarce Firefox, mamy problemy. Jeśli przeanalizujemy żądanie dotyczące naszej czcionki na karcie Chrome Dev Tools Network, zobaczymy, że odpowiedź z serwera jest pusta. Co daje? Cóż, domyślnie Firefox akceptuje tylko linki z tej samej domeny, co strona hosta. Jeśli chcemy dołączyć czcionki z różnych domen, musimy dodać do czcionki nagłówek Access-Control-Allow-Origin. Tak więc, jeśli spróbujesz obsługiwać czcionki z dowolnego CDN, Firefox ich nie załaduje.

Co to jest CORS?

Specyfikacja CORS używa obiektu XMLHttpRequest do wysyłania i odbierania nagłówków ze źródłowej strony internetowej do serwera, który jest odpowiednio skonfigurowany w celu umożliwienia obsługi żądań między lokacjami. Serwer akceptujący żądanie musi odpowiedzieć poleceniem Access-Control- Header Allow-Origin z symbolem wieloznacznym (*) lub prawidłową domeną pochodzenia przesłaną przez źródłową stronę internetową jako wartość. Jeśli wartość nie zostanie uwzględniona, żądanie zakończy się niepowodzeniem. Ponadto w przypadku metod HTTP innych niż GET lub POST, takich jak PUT, konieczne jest żądanie inspekcji wstępnej, w którym przeglądarka wysyła żądanie HTTP OPTIONS w celu nawiązania uzgodnienia z serwerem przed zaakceptowaniem żądania PUT. Na szczęście, po wystarczającej reakcji społeczności programistów, Amazon udostępnił konfigurację CORS na Amazon S3 za pośrednictwem bardzo prostej konfiguracji XML.

Zacznijmy.

Konfigurowanie CORS w Amazon S3

Powinieneś już być w swojej konsoli zarządzania Amazon. Kliknij Właściwości ? Uprawnienia ? Edytuj konfigurację CORS. Powinien pojawić się monit modalny. Konfiguracja może przyjąć do 100 definicji reguł, ale dla naszej czcionki internetowej będziemy potrzebować tylko kilku. W tym przykładzie użyjemy symbolu wieloznacznego, ale jeśli robisz to w środowisku produkcyjnym, powinieneś dodać domeny do białej listy, aby uniemożliwić innym wyświetlanie Twojej czcionki z konta S3 na ich własnych stronach internetowych. To nie byłby koniec świata, ale może być kosztowny. Pierwsza reguła zezwala na żądania GET między źródłami z dowolnego źródła. Reguła zezwala również na wszystkie nagłówki w żądaniu OPCJI inspekcji wstępnej za pośrednictwem polecenia Access-Header Control-Request-Headers. W odpowiedzi na każde żądanie OPCJI inspekcji wstępnej Amazon S3 zwróci żądane nagłówki. Druga reguła zezwala na żądania GET z różnych źródeł ze wszystkich źródeł. * znak wieloznaczny odnosi się do wszystkich źródeł.

< CORSConfiguration >

< CORSRule >

< AllowedOrigin > * / AllowedOrigin >

< AllowedMethod > GET < /AllowedMethod >

< /CORSRule >

< /CORSConfiguration >

Dlatego dodajmy nową konfigurację do naszego Edytora i zapiszmy. Wróćmy teraz do przeglądarki Firefox i ponownie załaduj stronę. Powinniśmy teraz zobaczyć czcionkę nagłówka ze stylem naszej czcionki internetowej BebasNeue. Można dowiedzieć się o wiele więcej o CORS, w szczególności o używaniu HTTP POST z określonymi typami MIME oraz wysyłaniu plików cookie i danych uwierzytelniających HTTP z żądaniami, jeśli zażąda tego serwer obsługujący CORS. Więc wyjdź i zacznij tworzyć własne hacki CORS.

Hack No.77

Kontroluj prezentację HTML5 za pomocą Robodeck

Robodeck wykorzystuje mobilną aplikację internetową do zdalnego sterowania HTML5 Sencha 2.0, aby umożliwić sterowanie prezentacją prezentacji Deck.js HTML5 za pośrednictwem gniazd internetowych i XHR. Robodeck działa na Node.js, używa frameworku aplikacji Express.js do obsługi HTML, JavaScript i CSS oraz używa Socket.IO do obsługi gniazd sieciowych. Robodeck demonstruje również użycie interfejsów API geolokalizacji HTML5.

Czy kiedykolwiek chciałeś zbudować prezentację przy użyciu tylko HTML5, JavaScript i CSS, która konkuruje z możliwościami wizualnymi PowerPoint i Keynote, lub dostarczyć tę prezentację, przechodząc do adresu URL w przeglądarce internetowej? A co powiesz na sterowanie prezentacją za pomocą urządzenia mobilnego? A może inne osoby logują się do Twojej prezentacji i wprowadzają aktualizacje, takie jak ich lokalizacja, podczas prezentacji? Robodeck to kulminacja hacków HTML5, wszystko opakowane w jeden framework. Jest to projekt hostowany na GitHub i pokazany w Heroku, który stanowi punkt wyjścia do tworzenia takich prezentacji. Ten hack przejdzie przez proces tworzenia tej struktury. Ten hack ujawnia bardziej szczegółowo zarówno implementację po stronie serwera, jak i JavaScript klienta, niż poprzednie. Piękno Node.js polega na tym, że możemy pisać i utrzymywać nasz kod aplikacji w jednym języku: JavaScript. Ta prostota sprawia, że Node.js jest idealną implementacją

Deck.js

Deck.js to jeden z wielu frameworków do prezentacji HTML5, które wykorzystują przejścia i animacje 2D / 3D JavaScript i CSS3 do tworzenia eleganckich prezentacji dla sieci Web bez potrzeby korzystania z programu PowerPoint lub Keynote. Robodeck używa Deck.js ze względu na jego potężne API, które ułatwia przesuwanie slajdów i wykonywanie innych czynności na pokładzie. Bardziej szczegółowe spojrzenie na API jest dostępne online. Pobierzmy Deck.js do lokalizacji na naszym komputerze i poczekajmy, aż zbudujemy podstawową aplikację, zanim przeniesiemy te pliki do odpowiedniego katalogu.

Node.js i Express

Będziemy używać Node.js jako naszego prostego serwera internetowego do obsługi żądań i odpowiedzi z przeglądarki oraz frameworka Express do dostarczania HTML i znaczników do przeglądarki.

Aby rozpocząć, przejdź do katalogu projektów z wiersza poleceń i uruchom generator aplikacji Express.

$ cd your-projects-directory

$ express robodeck

$ cd robodeck

Teraz możesz wyświetlić listę plików w swoim katalogu za pomocą polecenia ls:

$ ls

Procfile README app.js node_modules package.json

Public routes views

Powinieneś teraz mieć pliki i katalogi niezbędne do zbudowania i uruchomienia prostej aplikacji Node.js / Express. Najpierw musimy uwzględnić niezbędne moduły w naszym pliku manifestu, package.json:

{

"name": "robodeck"

, "version": "0.0.1"

, "private": true

, "dependencies": {

"express": "2.5.8"

, "jade": ">= 0.0.1"

, "socket.io": "latest"

, "useragent": "latest"

, "googlemaps": "latest"

}

}

Możesz zobaczyć, że dołączyliśmy kilka modułów do zbudowania tego projektu. Wprowadziliśmy już Express, ale będziemy również używać Jade jako naszego metajęzyka HTML. Uwzględniamy również Socket.IO, o którym będziemy mówić w następnej sekcji, oraz Mapy Google, aby włączyć usługę internetową do wykonywania odwrotnego geokodowania. Po wypełnieniu naszego manifestu użyjemy Node Package Manager do zainstalowania modułów:

$ cd robodeck

$ npm install

$ node app.js

Serwer Express nasłuchujący na porcie 3000

Na koniec uruchomimy przeglądarkę i przejdziemy do http: // localhost: 3000.

Wyznaczanie tras

Mamy teraz początki podstawowej aplikacji internetowej Express. Otwórzmy plik app.js i przyjrzyjmy się. Widzimy, że nasza podstawowa aplikacja Express akceptuje żądania tylko z jednego schematu adresu URL, z katalogu głównego. Dlatego właśnie otrzymujemy podstawową odpowiedź, kiedy przechodzimy do http: // localhost: 3000.

// Trasy

app.get ('/', route.index);

app.listen (3000, function () {

console.log ("Serwer Express nasłuchuje na porcie% d w trybie% s",

app.address (). port, app.settings.env);

});

Aby obsługiwać dwie oddzielne aplikacje, musimy akceptować żądania przychodzące z dwóch oddzielnych adresów URL: jeden dla naszej aplikacji Deck.js, która będzie nadal używać głównego adresu URL, a drugi, który utworzymy dla naszej aplikacji mobilnej Sencha 2.0. Dla uproszczenia użyjemy http: // localhost: 3000 / x. Będziemy również musieli zaakceptować żądania XMLHttpRequests z naszej aplikacji mobilnej Sencha 2.0: jeden z adresu URL http: // localhost / next, aby przesunąć prezentację, a drugi z adresu URL http: // localhost / prev, aby przesunąć prezentację z powrotem. Jeśli więc chcesz uzyskać pełne informacje na temat działania tras, przeczytaj ten konkretny hack. Jeśli nie, możesz skopiować następujący kod do pliku app.js:

app.get('/', function(req, res) {

routes.desktop(req, res);

});

app.get('/x', function(req, res) {

routes.iphone(req, res);

});

///////// ACCEPT XHR CALLS FROM REMOTE MOBILE APP

app.get('/next', function(req, res) {

console.log('NEXT- ' + 'server time: ' + getTime() + ',

client time: ' + req);

send(JSON.stringify({ "cmd": 'next' }));

});

app.get('/back', function(req, res) {

console.log('PREV ' + getTime());

send(JSON.stringify({ "cmd": 'prev' }));

});

Teraz ustaliliśmy cztery niezależne trasy dla żądań przychodzących. W naszym katalogu tras będziemy przechowywać plik index.js, który będzie zawierał logikę odpowiadającą na trasy:

export.desktop = function (req, res) {

res.render ('desktop', {układ: 'basic'});

};

exports.iphone = function (req, res) {

res.render ('smartphone', {layout: 'mobile'});

};

Tutaj powiedzieliśmy Express, gdzie szukać widoków, które zbudują znacznik HTML, aby powrócić do przeglądarki klienta, w oparciu o określoną wywołaną trasę. Zwróć uwagę, że istnieją cztery trasy, ale tylko dwa widoki układu. Dzieje się tak, ponieważ dwie z naszych tras, / next i / previous, nie będą musiały zwracać odpowiedzi w postaci znaczników HTML. Te trasy z kolei wygenerują komunikat gniazda sieciowego, który zostanie wysłany tylko do nasłuchujących klientów. Klienci, którzy połączyli się z głównym adresem URL i otrzymali widok pulpitu, otrzymają również znacznik script ze skryptem Socket.IO, który instruuje klienta, aby nasłuchiwał przychodzących komunikatów gniazda sieciowego. W wywołaniu zwrotnym tego modułu obsługi zdarzeń subskrybenta, będziemy wywoływać interfejsy API Deck.js, które będą wykonywać

Przejście CSS na slajdzie.

Tworzenie widoków stacjonarnych i mobilnych

Rozbudujmy więc nasze dwa widoki. Ale ze względu na ten hack możemy użyć następującego kodu, aby utworzyć dwa układy i widoki: jeden dla naszej aplikacji mobilnej, a drugi do wyświetlenia naszej aplikacji Deck.js. Jak pamiętacie, w naszych trasach wyznaczyliśmy dwa widoki: komputer stacjonarny i iPhone. Każdy widok używa również innego widoku układu. Możesz myśleć o układzie jako o kontenerze, a widok jako o wewnętrznym znaczniku zawartym w kontenerze. Najpierw przyjrzyjmy się podstawowemu układowi widoku na komputer:

!!!

html

head

title robodeck

link(rel='stylesheet', href='/stylesheets/style.css')

link(rel='stylesheet', href='./deck.js/core/deck.core.css')

link(rel='stylesheet',

href='./deck.js/extensions/goto/deck.goto.css')

link(rel='stylesheet',

href='./deck.js/extensions/hash/deck.hash.css')

link(rel='stylesheet',

href='./deck.js/extensions/menu/deck.menu.css')

link(rel='stylesheet',

href='./deck.js/extensions/navigation/deck.navigation.css')

link(rel='stylesheet',

href='./deck.js/extensions/scale/deck.scale.css')

link(rel='stylesheet',

href='./deck.js/extensions/status/deck.status.css')

// Style theme. More available in /themes/style/ or create your own.

link(rel='stylesheet', href='../deck.js/themes/style/neon.css')

// Transition theme.

// More available in /themes/transition/ or create your own.

link(rel='stylesheet',

href='../deck.js/themes/transition/horizontal-slide.css')

script(src='../modernizr.custom.js')

script(src='../socket.io/socket.io.js')

body.deck-container

!= body

Możesz zobaczyć dołączenie CSS Deck.js i zależności JavaScript Socket.IO zadeklarowanych w znaczniku. Teraz zajmiemy się widokiem pulpitu, który zostanie uwzględniony w podstawowym układzie. W tym miejscu zaczniemy używać deklaratywnych znaczników potrzebnych do tworzenia naszych slajdów dla Deck.js. Zobaczysz, że każdy slajd jest oznaczony za pomocą tagu sekcji HTML5 z atrybutem klasy CSS slajdu.

// Utwórz dowolną liczbę elementów ze slajdami klas w kontenerze

section.slide

h1 robodeck

h2 Interactive Demo - Connecting and GeoLocation

ol

li Connect to http://robodeck.herokuapp.com

li Your Browser will detect Web Socket Support

li If necessary, your browser will fallback to Comet techniques

li Once connection is established,

your browser will pass GeoLocation to server

li Server pushes GeoLocation data to all clients

section.slide#remote

h1 HTML5 Rocks

h2 Interactive Demo - Remote Control Pub/Sub

ol

li We connect to the same application with a Sencha 2.O

HTML5 Mobile Web app

li We advance the deck by publishing NEXT or PREV command

messages to the server

li All connected clients are subscribing to the commands

li Client JavaScript then controls the deck

section.slide#thanks

h1 Thank You!

ol Interactive Demo - the end.

Ten znacznik zawiera trzy slajdy. Przejdź do przeglądarki i użyj prawego klawisza strzałki, aby przejść do drugiego slajdu. Zwróć uwagę na znacznik skrótu w adresie URL: http: // localhost: 3000 / # remote odpowiada identyfikatorowi pilota wskazanemu na drugim slajdzie. Dołączyliśmy również sekcję, która zostanie zaktualizowana po pojawieniu się komunikatu gniazda sieciowego jest wysyłany z informacją o aktualnej łącznej liczbie podłączonych klientów i geolokalizacjach każdego z tych klientów (więcej na ten temat później).

footer

div#clients

p#viewers

div#locationsWrapper

p#locations

p#tweets

Zależności JavaScript Deck.js są zawarte w widoku basic.jade u dołu strony. Dzieje się tak ze względu na wydajność, aby upewnić się, że znaczniki są ładowane przed rozpoczęciem ładowania i wykonywania skryptów.

script(src='../jquery-1.7.min.js')

script(src='../deck.js/core/deck.core.js')

script(src='../deck.js/extensions/hash/deck.hash.js')

script(src='../deck.js/extensions/menu/deck.menu.js')

script(src='../deck.js/extensions/goto/deck.goto.js')

script(src='../deck.js/extensions/status/deck.status.js')

script(src='../deck.js/extensions/navigation/deck.navigation.js')

script(src='../deck.js/extensions/scale/deck.scale.js')

Na koniec będziemy musieli utworzyć instancję talii Deck.js, wywołując:

script $(function() {$.deck('.slide');});

Teraz w przypadku trasy mobilnej będziemy potrzebować układu mobilnego, który będzie zawierał zasoby wymagane do odpowiedzi na żądania z urządzeń mobilnych. Pamiętaj, że urządzenia mobilne powinny uzyskiwać dostęp do aplikacji z localhost: 3000 / x, aby otrzymać aplikację zoptymalizowaną pod kątem urządzeń mobilnych. W układzie mobilnym uwzględnimy następujący szkielet znaczników:

html

head

title robodeck mobile

// sencha 1.0

// link(rel='stylesheet',

href='../javascripts/sencha/resources/css/sencha-touch.css') // script(src='http://maps.google.com/maps/api/js?sensor=true')

// script(src='./javascripts/sencha/sencha-touch-debug.js')

// sencha 2.0

link(rel='stylesheet',

href='app/lib/touch/resources/css/sencha-touch.css',

title='senchatouch', id='senchatouch')

link(rel='stylesheet',

href='app/css/style.css', title='android', id='android')

script(src='app/lib/touch/sencha-touch.js')

script(src='app/app/app.js')

script(src='app/app/views/Viewport.js')

script(src='app/app/views/Home.js')

// other

script(src='./modernizr.custom.js')

// script(src='./socket.io/socket.io.js')

body.deck-container

!= body

Układ mobilny zawiera wszystkie zależności Sencha 2.0 potrzebne do stworzenia mobilnej aplikacji internetowej HTML5. Widok smartfona jest wstrzykiwany do kontenera mobilnego i jest przeznaczony dla klientów internetowych niebędących tabletami. Moglibyśmy również mieć widoki specyficzne dla tabletów lub może używać zapytań o media CSS3, aby odpowiadać na różne rzutnie, konfigurując nasze znaczniki za pomocą metadanych. Na razie zachowamy prostotę i zwrócimy ten sam widok wszystkim klientom uzyskującym dostęp do adresu URL localhost: 3000 / x. Ze względu na sposób działania Senchy 2.0 w widoku smartfona nic nie jest potrzebne. Cały kod niezbędny do wygenerowania aplikacji Sencha 2.0 jest zawarty w plikach JavaScript już zawartych w widoku układu mobilnego. To podejście różni się od innych podobnych frameworków, takich jak jQuery Mobile, ale ten projekt "JavaScript jako jądro" jest tym, co sprawia, że Sencha 2.0 jest wyjątkowa i popularna wśród niektórych programistów JavaScript. Bardziej szczegółowo omówimy Senchę w następnej sekcji. Na razie będziemy nadal obsługiwać pusty widok smartphone.jade na wypadek, gdybyśmy chcieli w przyszłości dołączyć inne skrypty, takie jak klient Socket.IO lub przejść na jQuery Mobile.

Pliki publiczne

Podobnie jak w przypadku hacków dotyczących gniazd sieciowych przedstawionych wcześniej w tym rozdziale, aby uzyskać komunikację za pośrednictwem gniazd sieciowych, należy utrzymywać kod zarówno na serwerze, jak i na kliencie. Powinieneś także mieć katalog / public w swojej aplikacji, który będzie zawierał wszystkie statyczne zasoby twojej aplikacji. Na początek przenieśmy nasze pliki JavaScript Deck.js do katalogu / public, abyśmy mogli uzyskać dostęp do naszej podstawowej prezentacji z przeglądarki.

Obsługa Polyfill WebSocket z Socket.IO

Socket.IO ma na celu umożliwienie aplikacji czasu rzeczywistego w każdej przeglądarce i na każdym urządzeniu mobilnym, zacierając różnice między różnymi mechanizmami transportu. Aby zapewnić łączność w czasie rzeczywistym w każdej przeglądarce, Socket.IO wybiera najbardziej wydajny transport w czasie wykonywania, spośród tych z poniższej listy, bez wpływu na API:

WebSocket

Gniazdo Adobe Flash

Długie odpytywanie Ajax

Strumieniowanie wieloczęściowe Ajax

Forever iFrame

Polling JSONP

Na szczęście społeczność Node.js sprawiła, że włączenie Socket.IO do Twojej aplikacji jest wyjątkowo proste i błyskawiczne rozpoczęcie pracy z serwerem gniazd internetowych. Na początku tego hacka zainstalowaliśmy już moduł socket.io, więc teraz będziemy go potrzebować i zaczniemy korzystać z API. Aby rozpocząć, musimy otworzyć nasz plik app.js i dodać następujące elementy przed wywołaniami app.config i app.routes:

// Clients is a list of users who have connected

var clients = [];

function send(message) {

clients.forEach(function(client) {

client.send(message);

});

}

Tutaj tworzymy tablicę klientów i specjalną funkcję wysyłania, która będzie iterować przez tablicę połączonych klientów i wysyłać im wiadomość. Teraz dodamy następującą deklarację app.listen (1511):

var sio = io.listen(app);

sio.sockets.on('connection', function(client) {

clients.push(client);

send(JSON.stringify({ "clients": clients.length }));

client.on('disconnect', function () {

var index = clients.indexOf(client.id);

clients.splice(index, 1);

});

});

Tutaj zaczynamy tworzyć naszą implementację Socket.IO na serwerze. Zwróć uwagę, że przekazujemy obiekt aplikacji Express do metody listen () aplikacji Socket.IO, aby obie nasłuchiwały na tym samym porcie. Następnie konfigurujemy procedurę obsługi zdarzeń do obsługi zdarzenia połączenia klienta. Następnie wypychamy unikalnego klienta do tablicy klientów i wysyłamy początkowy komunikat gniazda sieciowego, zawierający bieżącą liczbę podłączonych klientów. Należy również zauważyć, że klient zostanie usunięty z tablicy klientów po wywołaniu zdarzenia rozłączenia, dzięki czemu suma klienta pozostanie dokładna. To jest nasz pierwszy komunikat gniazda sieciowego, który jest używany w widoku pulpitu Deck.js do aktualizowania interfejsu użytkownika o liczbę klientów wyświetlających prezentację Deck.js. Aby ta aktualizacja interfejsu użytkownika faktycznie działała, wymagana jest jeszcze jedna zależność, a mianowicie dodanie biblioteki Socket.IO po stronie klienta.

Dodanie JavaScript klienta Socket.IO do naszych widoków

Wróćmy teraz do naszego katalogu / public, który zawiera wszystkie nasze statyczne zasoby. W tym miejscu będzie działał JavaScript po stronie klienta, który tworzy obiekt WebSocket, który wyśle początkowe żądanie do serwera z żądaniem uaktualnienia do protokołu WebSocket. Ten klient JavaScript ustanowi również system publikowania i subskrypcji, który będzie obsługiwał i wysyłał wiadomości przez sieć. Tutaj utworzymy /socket.io dla naszych plików socket.io razem z naszymi plikami deck.js. Pobierzmy pliki JavaScript klienta socket.io i umieśćmy je w katalogu /socket.io. Na koniec dodajmy trochę JavaScript do naszego widoku basic.jade, aby umożliwić Socket.IO rozpoczęcie nasłuchiwania wiadomości z serwera:

script

var socket = io.connect();

socket.on('message', function (data) {

var json = JSON.parse(data);

if (json.clients) {

// update the DOM

$('#viewers').text('viewers:' + json.clients);

}

});

Po pobraniu wiadomości z właściwością client uzyskujemy dostęp do DOM za pomocą jQuery i aktualizujemy sekcję Viewers aktualną wartością. Możesz przetestować tę funkcję, otwierając kilka kart w przeglądarce lub nawet oddzielne instancje przeglądarki w dowolnym miejscu w sieci, które mają dostęp do localhost: 3000 i dostęp do prezentacji pod głównym adresem URL http: // localhost: 3000. Każda instancja otrzyma komunikat gniazda sieciowego i zaktualizuje wszystkich podłączonych klientów o całkowitą liczbę klientów.

Dodanie interfejsów API geolokalizacji i odwrotnego geokodowania za pomocą modułu googlemaps

Teraz, gdy mamy aplikację, która pobiera wiadomości z gniazda sieciowego, dodajmy dodatkowe dane (długość i szerokość geograficzną klientów), wykorzystując interfejsy API geolokalizacji dostępne w Twojej przeglądarce oraz moduł googlemaps Node.js, aby odwrócić geokodowanie lokalizacji klienta na podstawie na danych dwuczęściowych. Następnie wyślemy nową lokalizację zwróconą z usługi wyszukiwania Google z powrotem do klienta w celu dynamicznej aktualizacji w przeglądarce. Najpierw dodajmy niezbędny kod do klienta. W naszym pliku basic.jade użyjemy interfejsów API geolokalizacji dostępnych w przeglądarce, aby poprosić użytkownika o dostęp do jego lokalizacji. Wszystkie przeglądarki mają nieco inne monity.

Konfiguracja dla urządzeń mobilnych i instalacja Sencha 2.0

Sencha Touch 2 to wysokowydajny framework aplikacji mobilnych HTML5, który umożliwia programistom tworzenie szybkich i imponujących aplikacji, które działają we wszystkich mobilnych przeglądarkach internetowych. Na potrzeby tego hacka wybraliśmy ramy właśnie z tego powodu. Tak jak wybraliśmy Socket.IO ze względu na jego wieloplatformową obsługę komunikacji w czasie rzeczywistym, tak samo chcemy, aby nasza mobilna aplikacja do zdalnego sterowania mogła działać na jak największej liczbie urządzeń.

Komunikacja z pilota

Jako ostatnia część naszej funkcjonalności, potrzebujemy możliwości rozwijania naszej prezentacji przez wysyłanie jednokierunkowych żądań w tle przez XMLHttpRequest z naszych aplikacji klienckich połączonych pod adresem http: // localhost: 3000 / x. W tym celu chcemy wykorzystać platformę mobilną aplikacji internetowych Sencha 2.0 HTML5. W sekcji widoków zobaczyłeś, że zawarliśmy wszystkie zależności niezbędne do zbudowania aplikacji Sencha 2.0. Aby te linki miały dostęp do właściwych plików, musimy pobrać Sencha 2.0 i zainstalować pliki w katalogu / public. Teraz będziemy potrzebować tylko zaktualizować dwa pliki z katalogu aplikacji Senchy, aby używać deklaratywnej składni Senchy do tworzenia aplikacji. Najpierw otworzymy plik app.js znajdujący się w katalogu app / app i utworzymy nową aplikację Ext.regApplication. Teraz zdefiniujemy przestrzeń nazw we właściwości name i użyjemy metody uruchamiania, aby zadeklarować punkt wejścia do aplikacji.

Ext.regApplication({

name: 'robodeck',

launch: function() {

this.views.Viewport = new this.views.Viewport();

}

});

Otwórzmy plik Viewport.js znajdujący się w katalogu app / app / views i skonfigurujmy widok. Wszystko to powinno być dość proste; szczegółowe informacje znajdują się w dokumentacji Sencha. Kluczową deklaracją, na którą należy zwrócić uwagę, jest utworzenie Ext.TabPanel o nazwie Viewport:

robodeck.views.Viewport = Ext.extend(Ext.TabPanel, {

fullscreen: true,

layout: 'card',

tabBar: new Ext.TabBar({

dock: 'bottom',

ui: 'dark',

layout: {

pack: 'center'

}

}),

initComponent: function() {

//put instances of cards into app.views namespace

Ext.apply(robodeck.views, {

Home: new robodeck.views.Home(),

});

//put instances of cards into viewport

Ext.apply(this, {

items: [

robodeck.views.Home

]

});

robodeck.views.Viewport.superclass.initComponent.apply

(this, arguments);

}

});

Na koniec w katalogu app / app / views otworzymy Home.js, w którym zdefiniujemy naszą własną niestandardową funkcję xhr () i wywołamy ją z modułu obsługi kliknięć naszych dwóch niestandardowych przycisków, Forward i Back.

var server = 'http://' + document.location.host;

function xhr(url) {

var request = new window.XMLHttpRequest();

request.open('GET', url, true);

request.send();

}

robodeck.views.Home = Ext.extend(Ext.Panel, {



items: [

{

xtype: 'button',

text: 'Forward',

handler: function(){

console.log('pressed -- Next');

xhr(server + '/next');

}

},

{

xtype: 'button',

text: 'Back',

handler: function(){

console.log('pressed -- Back');

xhr(server + '/back');

}

}

],



});

I to naprawdę wszystko, czego potrzebujemy, aby wysłać żądania XMLHttpRequests za pomocą jednego kliknięcia w naszej aplikacji mobilnej Sencha 2. Na koniec zapamiętaj trasy, które ustawiliśmy na początku tego hacka, aby akceptować żądania do adresów URL http: // localhost: 3000 / next i http: // localhost: 3000 / back? Teraz możemy z nich skorzystać. Po odebraniu żądania wyzwalamy metodę send () gniazda sieciowego:

app.get('/next', function(req, res) {

send(JSON.stringify({ "cmd": 'next' }));

});

app.get('/back', function(req, res) {

send(JSON.stringify({ "cmd": 'prev' }));

});

W naszych połączonych klientach nasłuchujemy wiadomości i jeśli ma właściwość cmd, dynamicznie wywołujemy to polecenie z obiektu $ .deck:

socket.on('message', function (data) {

var json = JSON.parse(data);



if (json.cmd) {

console.log('cmd: ' + json.cmd);

console.log("CMD MESSAGE");

// call deck.js api

$.deck(json.cmd)

}



});

Wszyscy podłączeni klienci powinni zobaczyć ruch do przodu i do tyłu, biorąc pod uwagę kliknięcia przycisku mobilnej aplikacji zdalnej

Hack No.78

Sprawdź połączenie Socket.IO, aby określić, czy jest natywne, czy emulowane

Narzędzia dla programistów Chrome mogą pomóc w debugowaniu ruchu sieciowego. Możesz również wyłączyć gniazda sieciowe, aby zademonstrować moc wypełniaczy Socket.IO. W tym hacku zbadamy możliwości Socket.IO. Wcześniej wyjaśniłem, że Socket.IO zapewnia możliwość wypełniania gniazd dla przeglądarek, które nie mają jeszcze włączonej implementacji gniazd. W poprzednich hackach używaliśmy narzędzi programistycznych Chrome do sprawdzania informacji WebSocket. Jednym ze sposobów sprawdzenia zdolności Socket.IO do powrotu do innego mechanizmu jest przejście do konsoli Chrome Dev Tools i wpisanie WebSocket = undefined. Następnie, jeśli klikniesz kartę Sieć, zauważysz, że przeglądarka zamknęła połączenie WebSocket i powróciła do odpytywania XHR. Zobaczysz, że żądania HTTP są wysyłane w regularnych odstępach czasu w celu sprawdzenia aktualizacji

Hack No.79

Zbuduj prosty serwer SPDY za pomocą node-spdy

SPDY to protokół stworzony i używany przez Google w celu skrócenia czasu ładowania strony internetowej. Nie zastępuje HTTP; raczej można go użyć do kompresowania i upraszczania żądań HTTP. Zacznijmy ten hack od zastrzeżenia: ten hack będzie krótki i prosty. Badanie SPDY jest po prostu zbyt duże. To powiedziawszy, SPDY jest zbyt ważny dla przyszłości technologii internetowych i warstwy łączności HTML5, aby o tym nie wspominać. Tak więc celem tego hackowania będzie przygotowanie Cię do pracy z podstawami SDPY i dodanie kolejnego narzędzia do paska narzędzi.

node-spdy

Aby obsłużyć naszą prostą implementację SPDY, będziemy domyślnie korzystać z Node.js, jak często mamy w tym tekście. Skorzystamy z node-spdy Fiodora Indutnego. Możesz pobrać przykład hello_world z GitHub. Przejdźmy dalej i sklonujmy cały katalog, a następnie przejdź do przykładu hello_world:

$ cd katalog-twojego-kodu

$ git clone git: //github.com/indutny/node-spdy.git

Teraz przejdziemy do katalogu i uruchomimy instalację npm:

$ cd node-spdy

$ npm install

Teraz możemy uruchomić nasz serwer SPDY:

$ node app.js

Działamy na porcie 3232. Przejdźmy do https: // localhost: 3232 / i powinniśmy zobaczyć "Hello World". Pamiętaj, aby w adresie URL umieścić "https: //", w przeciwnym razie Twoja prośba nie zostanie zaakceptowana. Zrobiliśmy proste żądanie, a serwer odpowiedział "Hello World". Nie zobaczysz znaczącej poprawy swojej odpowiedzi, ponieważ jest po prostu zbyt prosta. SPDY świeci, gdy używasz go z aplikacjami, które nawiązują dużą liczbę połączeń i przesyłają znaczne ilości danych. Ciąg "Hello World" prawie nie mieści się w tej kategorii aplikacji.

Hack No.80

Uwagi dotyczące projektowania aplikacji HTML5

Oprócz przedstawiania hacków, które uczą podstaw korzystania z tych nowatorskich narzędzi, omówimy również niektóre z najczęstszych współczesnych wyzwań związanych z projektowaniem aplikacji HTML5, które są obecnie przedstawiane programistom, w tym zagadnienia dotyczące sieci mobilnej, optymalizacji wydajności po stronie klienta, kompatybilność z różnymi przeglądarkami i DRYing (Don′t Repeat Yourself) w bazie kodu.

Dlaczego Node.js?

Po pierwsze, zacznijmy od jednej z najbardziej popularnych technologii w ostatnim czasie, Node.js. Nie wybraliśmy tego serwera ze względu na otaczający go szum. Wybraliśmy go z następujących kluczowych powodów:

• Node.js udostępnia serwer HTTP, więc możemy obsługiwać pliki.

• Node.js udostępnia również JavaScript API, więc możemy używać tego samego języka programowania na serwerze i kliencie do tworzenia aplikacji.

• Istnieje wiele narzędzi, które sprawiają, że wdrażanie aplikacji Node.js jest proste i szybkie. Ponieważ naszym celem jest tworzenie hacków, wykorzystamy jak najwięcej z tych narzędzi.

Instalacja

Nie będę szczegółowo omawiać sposobu instalacji Node.js. Aby przeprowadzić konfigurację lokalną, przejdź do http://nodejs.org/#download, aby pobrać instalator. Jeśli chcesz zainstalować za pomocą menedżera pakietów, przejdź do:

https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager.

Oficjalna dokumentacja dotycząca instalacji, biorąc pod uwagę specyfikę lokalnego środowiska, jest dostępna pod adresem https://github.com/joyent/node/wiki/Installation.

Po zainstalowaniu Node.js otwórz terminal i sprawdź swoją instalację.

Instalowanie w systemie Mac OS X przez Homebrew

Jeśli używasz komputera Mac, prawdopodobnie najłatwiejszym sposobem na szybkie rozpoczęcie pracy jest instalacja przez Homebrew. Gdy masz już Homebrew, instalacja jest tak prosta, jak uruchomienie następującego:

zbrew install node

user$ node -v

v0.4.7

Hack No.81

Dostarcz "Hello Html5" do przeglądarki

Dzięki temu klasycznemu hackowi "Hello World" będziesz mógł używać Node.js do obsługi żądań z przeglądarek i odpowiadania treścią. Zajmijmy się naszym pierwszym żądaniem HTTP i odpowiedzią w Node.js. Zaczniemy tak prosto, jak to tylko możliwe: użyjemy przykładowej aplikacji "Hello World" z nodejs.org. Aby rozpocząć, musimy tylko przejść do pustego katalogu w naszym systemie plików i utworzyć pusty plik, taki jak server.js. Dodamy do pliku:

var http = require('http');

http.createServer(function (req, res) {

res.writeHead(200, {'Content-Type': 'text/plain'});

res.end('Hello Html5\n');

}).listen(1337, "127.0.0.1");

console.log('Server running at http://127.0.0.1:1337/');

Teraz możemy wykonać kod z plikiem, uruchamiając następujące polecenie z wiersza poleceń:

$ node server.js

Zobaczmy, co się tutaj dzieje. W górnej części pliku znajduje się metoda wymagania, która jest częścią systemu zarządzania modułami zależności Node.js, który jest zgodny ze specyfikacją CommonJS. Po dołączeniu modułu HTTP możemy wywołać http.createServer () i przekazać mu funkcję, która przyjmuje żądanie i obiekt odpowiedzi jako parametry. Każdy obiekt ma własny zestaw metod i właściwości. Później przyjrzymy się im nieco dokładniej, rejestrując żądanie i odpowiedź w konsoli.

http.createServer(function (req, res) {

res.writeHead(200, {'Content-Type': 'text/plain'});

res.end('Hello Html5\n');

}).listen(1337, "127.0.0.1");

Tutaj widać, że metoda writeHead () służy do zwracania kodu odpowiedzi i zapisywania zawartości do bufora wraz z obiektem, który zawiera nagłówki odpowiedzi. Następnie metoda end() akceptuje ciąg. Ostatnią rzeczą, na którą należy zwrócić uwagę, jest metoda Listen() połączona łańcuchem na końcu createServer(). Gdy createServer () zakończy się i zwróci. Następną metodą na stosie jest Listen (), która jest przekazywana do numeru portu nasłuchiwania. Metoda createServer () zwraca obiekt, który dziedziczy prototyp http.Server. To są podstawy konfigurowania i uruchamiania serwera Node.js. Zanim wydrukujemy nasze żądanie, wyłączmy rejestrowanie dodatkowego żądania dotyczącego favicon.ico, aby konsola nie drukowała dwóch żądań wysłanych z przeglądarki. Dzięki temu nasze dzienniki będą nieco bardziej czytelne.

var http = require('http');

http.createServer(function (req, res) {

// control for favicon

if (req.url === '/favicon.ico') {

res.writeHead(200, {'Content-Type': 'image/x-icon'} );

res.end();

console.log('favicon requested');

return;

}

else {

console.log('REQUEST: ' + req);

console.log(req);

res.writeHead(200, {'Content-Type': 'text/plain'});

res.end('Hello Html5\n');

}

}).listen(1337, "127.0.0.1");

console.log('Server running at http://127.0.0.1:1337/');

W tym hacku jesteśmy zainteresowani zrozumieniem podstaw protokołu HTTP i tego, jak Node.js pomaga nam szybko i łatwo tworzyć aplikacje internetowe. Zaczniemy od zbadania obiektów żądań i odpowiedzi HTTP. Aby uprościć, potraktuj żądanie jako dane wejściowe, a odpowiedź jako dane wyjściowe. Serwer i moduł HTTP będą przetwarzać przychodzące żądanie i zwracać odpowiedź na podstawie wszystkich parametrów i informacji zawartych w przychodzącym żądaniu. Oto obiekt żądania:

{ socket:

{ fd: 8

, type: 'tcp4'

, secure: false

, _readWatcher: { callback: [Function], socket: [Object] }

, readable: true

, _writeQueue: []

, _writeQueueEncoding: []

, _writeQueueFD: []

, _writeWatcher: { socket: [Circular],

callback: [Function: _doFlush] }

, writable: true

, _writeImpl: [Function]

, _readImpl: [Function]

, _shutdownImpl: [Function]

, remoteAddress: '127.0.0.1'

, remotePort: 50722

, server:

{ connections: 2

, paused: false

, pauseTimeout: 1000

, watcher: [Object]

, _events: [Object]

, type: 'tcp4'

, fd: 6

}

, _outgoing: [ [Object] ]

, __destroyOnDrain: false

, ondrain: [Function]

, _idleTimeout: 120000

, _idleNext:

{ fd: 9

, type: 'tcp4'

, secure: false

, _readWatcher: [Object]

, readable: true

, _writeQueue: []

, _writeQueueEncoding: []

, _writeQueueFD: []

, _writeWatcher: [Object]

, writable: true

, _writeImpl: [Function]

, _readImpl: [Function]

, _shutdownImpl: [Function]

, remoteAddress: '127.0.0.1'

, remotePort: 50726

, server: [Circular]

, _outgoing: []

, __destroyOnDrain: false

, ondrain: [Function]

, _idleTimeout: 120000

, _idleNext: [Object]

, _idlePrev: [Circular]

, _idleStart: Fri, 10 Feb 2012 05:46:34 GMT

, _events: [Object]

, ondata: [Function]

, onend: [Function]

, _onOutgoingSent: [Function]

}

, _idlePrev: [Circular]

, _idleStart: Fri, 10 Feb 2012 05:46:34 GMT

, _events:

{ timeout: [Function]

, error: [Function]

, close: [Function]

}

, ondata: [Function]

, onend: [Function]

, _onOutgoingSent: [Function]

}

, connection: [Circular]

, httpVersion: '1.1'

, headers:

{ host: 'localhost:1337'

, connection: 'keep-alive'

, 'cache-control': 'max-age=0'

, 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X ...'

, accept: 'text/html,application/xhtml+xml,...'

, 'accept-encoding': 'gzip,deflate,sdch'

, 'accept-language': 'en-US,en;q=0.8'

, 'accept-charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.3'

}

, url: '/'

, method: 'GET'

, statusCode: null

, client: [Circular]

, httpVersionMajor: 1

, httpVersionMinor: 1

, upgrade: false

}

Trochę informacji o http

Gdy moduł serwera HTTP Node.js przyjmuje żądanie od klienta, takiego jak przeglądarka, tworzy nowy obiekt do przechowywania wszystkich informacji. Jeśli spojrzysz na obiekt, zobaczysz szereg ważnych właściwości: adres URL, metody i nagłówki. Zrozumienie tych trzech właściwości pomoże Ci zrozumieć podstawy HTTP i serwerów WWW.

URL

Uniform Resource Locator (URL) to odniesienie do zasobu dostępnego w Internecie. Możesz o nim myśleć jak o adresie do dokumentów i innych zasobów. Pierwsza część adresu URL (lub tekst przed: //) to identyfikator protokołu. Informuje klienta (najczęściej przeglądarkę internetową), z którym protokołem ma się połączyć. W tym konkretnym przypadku protokołem jest HTTP (http: //). Następna część to nazwa zasobu (lub tekst po: //), który zawiera adres IP lub nazwę domeny. Adres URL to jeden z typów jednolitego identyfikatora zasobów (URI); jest bardziej ogólny termin związany z wieloma typami adresów, które odnoszą się do obiektów w Internecie.

Metody

HTTP definiuje metody (czasami określane jako czasowniki) wskazujące żądaną akcję, która ma zostać wykonana na zidentyfikowanym zasobie. To, co reprezentuje ten zasób, czy to istniejące dane, czy dane generowane dynamicznie, zależy od implementacji serwera. W tym przykładzie łącząca się przeglądarka używa metody GET do żądania ciągu tekstu zawierającego "Hello Html5".

Nagłówki

Nagłówki HTTP tworzą parametry przekazywane jako komunikat w transakcji HTTP. Wiadomość składa się z grupy pól, które są sformatowane w tablicy asocjacyjnej oddzielonej dwukropkami. Format danych to ciąg, który ułatwia przeglądanie i debugowanie danych. Inną właściwością, która jest interesująca dla HTML5, jest właściwość uaktualnienia. Jak wspomniano w [Hack # 86], zobaczysz, że wykonujemy żądania przy użyciu metody Upgrade. W tym przykładzie właściwość upgrade ma wartość false. Jak wspomniałem wcześniej, twórcy HTTP myśleli wystarczająco przyszłościowo, aby zbudować w taki sposób, aby przeglądarka zażądała aktualizacji do innego protokołu. To jest mechanizm uzgadniania dla WebSocket. Jeśli chcesz dokładniej zbadać obiekt, przeczytaj oficjalną dokumentację API obiektu żądania. A teraz wydrukujmy nasz obiekt odpowiedzi na konsolę:

var http = require('http');

http.createServer(function (req, res) {

console.log('RESPONSE: ' + res);

console.log(res);

}).listen(1337, "127.0.0.1");

console.log('Server running at http://127.0.0.1:1337/');

Gdy serwer przetworzy wszystkie informacje zawarte w żądaniu i przejdzie przez logikę zawartą w strukturze sieciowej, tworzy obiekt odpowiedzi. Oto obiekt odpowiedzi:

{ socket:

{ fd: 9

, type: 'tcp4'

, secure: false

, _readWatcher: { socket: [Object], callback: [Function] }

, readable: true

, _writeQueue: []

, _writeQueueEncoding: []

, _writeQueueFD: []

, _writeWatcher: { callback: [Function: _doFlush],

socket: [Circular] }

, writable: true

, _writeImpl: [Function]

, _readImpl: [Function]

, _shutdownImpl: [Function]

, remoteAddress: '127.0.0.1'

, remotePort: 50807

, server:

{ connections: 2

, paused: false

, pauseTimeout: 1000

, watcher: [Object]

, _events: [Object]

, type: 'tcp4'

, fd: 6

}

, _outgoing: [ [Circular] ]

, __destroyOnDrain: false

, ondrain: [Function]

, _idleTimeout: 120000

, _idleNext:

{ fd: 8

, type: 'tcp4'

, secure: false

, _readWatcher: [Object]

, readable: false

, _writeQueue: []

, _writeQueueEncoding: []

, _writeQueueFD: []

, _writeWatcher: [Object]

, writable: true

, _writeImpl: [Function]

, _readImpl: [Function]

, _shutdownImpl: [Function]

, remoteAddress: '127.0.0.1'

, remotePort: 50805

, server: [Circular]

, _outgoing: [Object]

, __destroyOnDrain: false

, ondrain: [Function]

, _idleTimeout: 120000

, _idleNext: [Object]

, _idlePrev: [Circular]

, _idleStart: Fri, 10 Feb 2012 06:07:08 GMT

, _events: [Object]

, ondata: [Function]

, onend: [Function]

, _onOutgoingSent: [Function]

}

, _idlePrev: [Circular]

, _idleStart: Fri, 10 Feb 2012 06:07:35 GMT

, _events:

{ timeout: [Function]

, error: [Function]

, close: [Function]

}

, ondata: [Function]

, onend: [Function]

, _onOutgoingSent: [Function]

}

, connection: [Circular]

, output: []

, outputEncodings: []

, _last: false

, chunkedEncoding: false

, shouldKeepAlive: true

, useChunkedEncodingByDefault: true

, _hasBody: true

, finished: false

}

Jeśli chcesz dokładniej zbadać obiekt, przeczytaj oficjalną dokumentację API obiektu odpowiedzi pod adresem. Żądanie i odpowiedź są przechowywane w pamięci jako obiekty JavaScript

Hack No.82

Wykryj ciąg agenta użytkownika w obiekcie żądania

Możesz przeszukiwać obiekt żądania HTTP, aby znaleźć cenne informacje o klienckim kliencie użytkownika. Jak widać, w obiektach żądań i odpowiedzi Node.js zawartych jest sporo właściwości. W [Hack # 90] zbadaliśmy kilka z nich. Tutaj skupimy się na nagłówkach żądań i wyodrębnimy ciąg agenta użytkownika, aby określić typ urządzenia klienckiego, z którego pochodzi żądanie. Sniffowanie klientów użytkownika jest powszechną praktyką, zwłaszcza w związku z niedawną eksplozją korzystania z mobilnej sieci WWW i fragmentacją mobilnych przeglądarek internetowych. Jest to więc bardzo istotny trik przy tworzeniu aplikacji internetowych HTML5. Czasami twórcy aplikacji muszą znać typ urządzenia klienckiego, które wysyła żądanie danych. Jeśli dane są dostępne po przeanalizowaniu nagłówków klienta użytkownika, aplikacja może następnie wysłać zapytanie do usługi sieci Web wyszukiwania, która zawiera szczegółowe właściwości dotyczące określonego urządzenia. Właściwości takie jak rozmiar ekranu, dostęp do kamery i akceleracja sprzętowa, żeby wymienić tylko kilka, mogą być następnie wykorzystane do zbudowania logiki warunkowej, wokół której zasoby i znaczniki są zwracane do klienta. Od dawna uważano za złą praktykę podsłuchiwanie agentów użytkownika z poziomu JavaScript przeglądarki, dlatego ta decyzja projektowa jest kontrowersyjna wśród programistów. Chociaż wykrywanie urządzeń po stronie serwera lub podsłuchiwanie agentów użytkownika jest dość powszechną praktyką, wielu programistów frontendu uważa to za niepotrzebną praktykę. Ciągła dojrzałość struktur JavaScript po stronie klienta i natywnych interfejsów API przeglądarek szybko zmienia projekt wielu aplikacji internetowych przystosowanych do urządzeń mobilnych. Pojęcia takie jak responsywne projektowanie witryn internetowych (wykorzystujące zapytania o media CSS3 i wykrywanie obiektów) umożliwiły zmianę treści za pomocą JavaScript i CSS3 w przeglądarce bez konieczności zdawania przez serwer typu klienta, który wysłał żądanie. W większości przypadków najbardziej optymalne jest zrównoważone podejście, które pozwala zarówno serwerowi, jak i klientowi zapewnić najlepszą funkcjonalność. Na razie zaczniemy od prostego zhakowania ciągu agenta użytkownika z obiektu żądania węzła, a następnie pokażemy, że możemy podejmować logiczne decyzje na podstawie prostych danych urządzenia w naszym obiekcie odpowiedzi. W naszym głównym pliku server.js dodamy bardzo prosty skrypt analizujący agenta użytkownika. Z poziomu bloku kodu, w którym konsolujemy.logujemy obiekt żądania, możemy wyciągnąć właściwość agenta użytkownika z nagłówków żądania. Utwórzmy również globalny obiekt o nazwie DeviceData, aby określić przestrzeń nazw naszych danych.

var ua = req.headers['user-agent'],

DeviceData = {};

Teraz, gdy mamy ciąg agenta użytkownika, możemy użyć wyrażeń regularnych, aby przeanalizować go w poszukiwaniu informacji związanych z naszym celem. Najpierw możemy przetestować mobile w ciągu znaków i ustawić właściwość mobile DeviceData na true:

// Mobile?

if (/mobile/i.test(ua))

DeviceData.mobile = true;

While we are at it, let′s test for Apple products:

// Apple device?

if (/like Mac OS X/.test(ua)) {

DeviceData.iOS = /CPU( iPhone)? OS ([0-9\._]+) like Mac OS

X/.exec(ua)[2].replace(/_/g, '.');

DeviceData.iPhone = /iPhone/.test(ua);

DeviceData.iPad = /iPad/.test(ua);

}

Mamy teraz podstawy wykrywania urządzeń. Oczywiście jest to uproszczony przykład systemu gotowego do produkcji. W systemie produkcyjnym aplikacja najprawdopodobniej przekazałaby ciąg agenta użytkownika do innej aplikacji, która jest frontonem bazy danych, lub do usługi internetowej innej firmy. Aplikacja lub usługa internetowa może następnie wyszukiwać informacje, rejestrować błędy, zbierać dane analityczne lub aktualizować aktualne dane.

Hack No.83

Użyj obiektu Response Node.js, aby odpowiedzieć klientowi za pomocą danych specyficznych dla urządzenia

Możesz użyć obiektu response w Node.js, aby wyświetlić specyficzne dla klienta informacje w przeglądarce. Teraz, gdy zhakowaliśmy nasz obiekt request, zajmijmy się odpowiedzią. Możemy zacząć od wywołania metody writeHead() i przekazania kodu odpowiedzi sukcesu wraz z ustawieniem Content-Type na text / plain. To dość standardowa odpowiedź. Następnie wywołamy metodę end() i przekażemy jej określony ciąg znaków w oparciu o właściwości, które dodaliśmy do naszego globalnego obiektu DeviceData.

res.writeHead(200, {'Content-Type': 'text/plain'});

if (DeviceData.mobile) {

res.end('Hello Html5\n Request from a Mobile Device');

}

if (DeviceData.iOS || DeviceData.iPhone || DeviceData.iPad) {

res.end('Hello Html5\n Request from an Apple Device');

}

else {

res.end('Hello Html5\n Request from some other Device');

}

Więc nasz ostateczny plik server.js wygląda następująco:

var http = require('http');

http.createServer(function (req, res) {

// control for favicon

if (req.url === '/favicon.ico') {

res.writeHead(200, {'Content-Type': 'image/x-icon'} );

res.end();

// console.log('favicon requested');

return;

}

else {

// console.log('REQUEST: ' + req);

console.log(req.headers);

var ua = req.headers['user-agent'],

DeviceData = {};

// Mobile?

if (/mobile/i.test(ua))

DeviceData.mobile = true;

// Apple device?

if (/like Mac OS X/.test(ua)) {

DeviceData.iOS = /CPU( iPhone)? OS ([0-9\._]+) like Mac OS

X/.exec(ua)[2].replace(/_/g, '.');

DeviceData.iPhone = /iPhone/.test(ua);

DeviceData.iPad = /iPad/.test(ua);

}

res.writeHead(200, {'Content-Type': 'text/plain'});

if (DeviceData.mobile) {

res.end('Hello Html5\n Request from a Mobile Device');

}

if (DeviceData.iOS || DeviceData.iPhone || DeviceData.iPad) {

res.end('Hello Html5\n Request from an Apple Device');

}

else {

res.end('Hello Html5\n Request from some other Device');

}

};

}).listen(1337, "127.0.0.1");

console.log('Server running at http://127.0.0.1:1337/');

Hack No.84

Użyj Menedżera pakietów węzłów, aby dodać strukturę aplikacji sieci Web jako moduł innej firmy

Dodawanie modułów do aplikacji Node.js jest proste dzięki Node Package Manager (NPM). Dodanie frameworku aplikacji sieci Web Express do aplikacji jest tak proste, jak dodanie go do manifestu aplikacji i zainstalowanie za pośrednictwem NPM. Najpierw musimy zrozumieć, jak Node.js obsługuje moduły innych firm. NPM to system zarządzania pakietami napisany dla Node.js. Zainspirowany systemami zarządzania pakietami Linux, pomaga poprzez automatyzację procesu instalowania, uaktualniania, konfigurowania i usuwania modułów innych firm z komputera i aplikacji Node.js. NPM utrzymuje zdalne repozytorium informacji o wersji i zależnościach, z których twórcy aplikacji mogą wysyłać zapytania i pobierać moduły w celu dołączenia ich do swoich aplikacji. Od wersji 0.6.3 (stabilnej) Node.js jest teraz dostarczany z NPM. Sprawdźmy jeszcze raz:

user$ npm -help

Dzięki NPM otrzymujemy proste narzędzie do zarządzania pakietami modułów. Zobaczmy podstawy. Najpierw zaktualizujemy plik manifestu package.json, aby uwzględnić naszą pierwszą zależność od innej firmy:

{

"name": "html5hacks-node"

, "version": "0.0.1"

, "private": true

, "dependencies": {

"express": "latest"

}

}

Teraz użyjemy parametru d, aby zainstalować zależności z naszego manifestu package.json:

user$ npm install -d

Teraz dodaliśmy nasz pierwszy moduł do naszej bazy kodu i powinniśmy mieć dostęp do najnowszej wersji frameworka sieciowego Express w celu ulepszenia naszej aplikacji.

Hack No.85

Użyj generatora aplikacji Express, aby uruchomić swoją aplikację

Plik wykonywalny generatora aplikacji Express pomaga w tworzeniu szkieletu aplikacji z poziomu wiersza poleceń.

Dlaczego warto korzystać z usługi Express?

We wcześniejszych hackach operowaliśmy na obiektach żądań i odpowiedzi, używając tylko modułu HTTP. Node.js jest dostarczany z innym "podstawowym" modułem o nazwie Connect, który zapewnia dodatkową warstwę funkcjonalności oprócz HTTP. Metoda createServer modułu HTTP zwraca obiekt, którego można użyć do odpowiadania na żądania HTTP. Ten obiekt dziedziczy prototyp http.Server. Connect oferuje również metodę createServer, która zwraca obiekt, który dziedziczy rozszerzoną wersję http.Server. Rozszerzenia Connect służą głównie do łatwego podłączania oprogramowania pośredniego. Dlatego Connect określa się jako "platforma oprogramowania pośredniego". Express łączy to, co Connect robi z modułem HTTP: oferuje metodę createServer, która rozszerza prototyp serwera Connect. Tak więc dostępna jest cała funkcjonalność Connect, a także renderowanie widoku i poręczne łącze DSL do opisywania tras. Ruby Sinatra to dobra analogia. Najszybszym sposobem rozpoczęcia pracy z Expressem jest użycie pliku wykonywalnego Express do wygenerowania aplikacji. Najpierw utworzymy aplikację:

$ npm install -g express

$ express /mydir && cd /mydir

OR

$ npm install -g express

$ mkdir mydir

$ cd mydir

$ express

Spowoduje to wygenerowanie następującego szkieletu aplikacji w wyznaczonym katalogu:

create : .

create : ./package.json

create : ./app.js

create : ./public

create : ./public/javascripts

create : ./public/images

create : ./public/stylesheets

create : ./public/stylesheets/style.css

create : ./routes

create : ./routes/index.js

create : ./views

create : ./views/layout.jade

create : ./views/index.jade

Przyjrzyjmy się bliżej plikowi app.js. Oto, co otrzymujesz domyślnie:

/**

* Module dependencies.

*/

var express = require('express')

, routes = require('./routes');

var app = module.exports = express.createServer();

// Configuration

app.configure(function(){

app.set('views', __dirname + '/views');

app.set('view engine', 'jade');

app.use(express.bodyParser());

app.use(express.methodOverride());

app.use(app.router);

app.use(express.static(__dirname + '/public'));

});

app.configure('development', function(){

app.use(express.errorHandler({

dumpExceptions: true,

showStack: true }));

});

app.configure('production', function(){

app.use(express.errorHandler());

});

// Routes

app.get('/', routes.index);

app.listen(3000);

console.log("Express server listening on port %d in %s mode",

app.address().port, app.settings.env);

Ale podzielmy to na podstawowe rzeczy, aby zacząć biegać:

// 1. Zadeklaruj nasze zależności modułów

var express = require('express')

, routes = require('./routes');

// 2. Utwórz wystąpienie Express Server

var app = module.exports = express.createServer();

// 3. Skonfiguruj aplikację

app.configure(function(){

app.set('views', __dirname + '/views');

app.set('view engine', 'jade');

app.use(express.static(__dirname + '/public'));

});

// 4. Ustaw trasy

app.get('/', routes.index);

// 5. Nasłuchuj na porcie 3000

app.listen(3000);

console.log("Express server listening on port %d in %s mode",

app.address().port, app.settings.env);

Teraz możemy zainstalować zależności:

$ npm install -d

and start the server:

$ node app.js

Hack No.86

Zbuduj niestandardowy moduł do obsługi routingu

Modularyzuj obsługę żądań kierowanych do Twojej aplikacji w ramach własnego modułu niestandardowego. W naszym głównym pliku app.js wprowadzimy kilka zmian. Po pierwsze, nasz generator już utworzył nowy moduł, który będzie izolował całą naszą logikę routingu. Dla celów demonstracyjnych i na wypadek, gdybyś musiał użyć statycznych plików HTML, wyłączymy silnik układu i skonfigurujemy Express do obsługi statycznych plików HTML z katalogu / public. Wyłączymy również obsługę układu. Oto plik app.js:

var express = require('express')

, routes = require('./routes');

var app = module.exports = express.createServer();

app.configure(function(){

// disable layout

app.set("view options", {layout: false});

app.use(express.static(__dirname + '/public'));

// make a custom html template

app.register('.html', {

compile: function(str, options){

return function(locals){

return str;

};

}

});

});

// Routes

app.get('/', function(req, res){

res.render("index.html");

});

app.listen(process.env.PORT || 3000);

console.log("Express server listening on port %d in %s mode",

app.address().port, app.settings.env);

Moduł tras zawiera plik index.js, który zawiera naszą logikę routingu:

routing logic:

exports.index = function(req, res){

res.render('index', { title: 'Index' })

};

A oto podstawowy plik index.html zawierający znaczniki:

< !DOCTYPE html >

< html lang="en" >

< head >

< meta charset="utf-8" >

< title >HTML5 Hacks< /title >

< /head >

< body >

< p >HTML5 Hacks< /p >

< /body >

< /html >

Teraz stworzyliśmy nasz pierwszy moduł i używamy tras do obsługi znaczników w przeglądarce. Wraz z rozwojem aplikacji będziesz musiał nadal modularyzować jej funkcjonalność, aby plik app.js nie stał się trudny w utrzymaniu.

Hack No.87

Skonfiguruj Express do korzystania z aparatu widoku

Skonfigurowanie Express do renderowania widoków z silnikiem widoku zapewnia elastyczność i prostotę naszego podejścia do tworzenia szablonów HTML. Wcześniej wyłączyliśmy silnik układu i skonfigurowaliśmy Express do obsługi statycznych plików HTML. Następnym logicznym krokiem jest rozpoczęcie upraszczania i optymalizacji naszej strategii widoków. Powszechną sprawdzoną metodą kodowania jest zachowanie SUCHEGO kodu (nie powtarzaj się). Zhakujemy to razem tutaj. Skoro już o tym mowa, krótko przedstawimy również metajęzyk zwany Jade i zademonstrujemy, że możemy skonfigurować Express do używania go jako alternatywy dla HTML. Będzie więcej w naszym następnym hacku o Jade i Stylusie. Następnie zaczniemy obserwować wzrost wydajności zapewniany przez te narzędzia. Na razie spójrzmy na nasze nowe zmiany. Oto plik app.js:

var express = require('express')

, routes = require('./routes');

var app = module.exports = express.createServer();

app.configure(function(){

app.set('views', __dirname + '/views');

app.set('view engine', 'jade');

app.use(express.static(__dirname + '/public'));

});

// Routes

app.get('/', function(req, res){

res.render("index.html");

});

app.listen(process.env.PORT || 3000);

console.log("Express server listening on port %d in %s mode",

app.address().port, app.settings.env);

Moduł tras zawierający plik index.js nadal zawiera tę samą logikę routingu:

exports.index = function(req, res){

res.render('index', { title: 'Index' })

};

Ale teraz mamy plik index.jade zawierający znaczniki w mniej szczegółowej, prostszej składni:

!!! 5

html(lang='en')

head

meta(charset='utf-8')

title HTML5 Hacks

body

p HTML5 Hacks

W następnych kilku hackach przyjrzymy się bliżej Jade i jej siostrzanemu językowi, Stylus.

Hack No.88

Użyj Jade Layouts, z DRY dla widoku aplikacji

DRY (Don't Repeat Yourself) to popularny język projektowania oprogramowania. Ten hack zademonstruje te sprawdzone metody w Twoich widokach. Aby zarządzać naszymi znacznikami i CSS w naszej aplikacji Express, będziemy używać dwóch fantastycznych narzędzi: Jade i Stylus. Jade to dynamiczny metajęzyk, który kompiluje się do HTML. Stylus to dynamiczny metajęzyk, który kompiluje się do CSS. Podobnie jak w przypadku innych narzędzi, nie będziemy wchodzić w szczegóły, ponieważ nie jest to podręcznik dotyczący interfejsu API. Nasze uzasadnienie wyboru tych języków "skrótów" jest proste. Chcemy mieć pewność, że nasze środowisko hakerskie pozostanie zorganizowane i tak się nie stanie nieporęczny. Jeśli już piszesz HTML i CSS, krzywa uczenia się dla tych technologii nie będzie stroma. Czas, który zaoszczędzisz w dłuższej perspektywie, sprawi, że ich wykorzystanie będzie warte czasu i energii, które poświęcisz na naukę obsługi narzędzi. Istnieją inne podobne metajęzyki, takie jak HAML, Less i Sass, które być może już znasz. Te narzędzia są nieco inne, ale koncepcje są takie same. Zanim przejdziemy do naszych hacków, będziemy musieli nieco zapoznać się ze składnią Jade. Miałeś posmak uproszczonej składni w poprzednich hackach, ale jeśli chcesz zagłębić się trochę głębiej, zobacz oficjalną stronę Jade i oficjalną dokumentację. W [Hack # 96] skonfigurowaliśmy podstawową aplikację wykorzystującą Jade. Wprowadziliśmy koncepcję DRY (nie powtarzaj się). Teraz zhakujemy razem kilka dodatkowych widoków, podstawowy układ i kilka częściowych, aby zademonstrować tę ważną koncepcję. Zarówno układy, jak i częściowe to techniki dynamicznego "dołączania" znaczników w celu zagregowania wielu plików .jade w jedno wyjście. Najpierw potrzebujemy innej trasy. W naszym głównym pliku app.js dodajmy:

app.get('/', routes.index);

app.get('/example1', routes.example1);

In our index.js file, let's add a new example route:

exports.index = function(req, res){

res.render('index', { title: 'Index' })

};

exports.example1 = function(req, res){

res.render('example1', { title: 'Example1' })

};

I wreszcie w naszym katalogu views możemy dodać nowy plik basic.jade. Teraz, gdy nasze żądanie dotyczy http: // localhost / example1, zawartość zostanie zwrócona z tego konkretnego pliku example1.jade. Jeśli będziemy nadal dodawać więcej tras i odpowiadających im widoków, nasze nowe pliki bez wątpienia będą zawierać zduplikowane znaczniki, zduplikowane zasoby i tak dalej. Cóż, to nie jest bardzo SUCHE. Tutaj do gry wchodzą układy i częściowe. Pomyśl o układzie jako o współużytkowanym kontenerze, a fragmenty jako o zduplikowanej zawartości, która powinna zostać uwzględniona we wnętrzach widoku. Stwórzmy więc plik layout.jade, który będzie zawierał wszystkie nasze udostępnione informacje o kontenerze, takie jak nasz nagłówek, nasz atrybut tytułu oraz wszystkie nasze JavaScript i CSS zależności:

!!! 5

html(lang='en')

head

meta(charset='utf-8')

title HTML5 Hacks

meta(name='description', content='')

meta(name='author', content='')

// Styles

link(href='assets/css/bootstrap.css', rel='stylesheet')

script(src='assets/js/application.js')

body

!= body

Teraz znaczniki zwrócone z widoku zostaną wstrzyknięte do treści dokumentu za pośrednictwem wywołania! = Body. Oto plik index.jade:

P HTML5 Hacks

Teraz wyświetl swoją aplikację pod adresem http: // localhost: 3000, a zobaczysz swoje dwa pliki, layout.jade i index.jade, zagregowane razem i wyprowadzone do przeglądarki. Układ jest domyślnie włączony, ale jeśli kiedykolwiek będziesz miał taką potrzebę, możesz go wyłączyć w następującej konfiguracji:

// disable layout

app.set("view options", {layout: false});

Hack No.89

Użyj części Jade, aby utworzyć wspólny pasek nawigacji w swoich widokach

Teraz możesz ponownie wykorzystywać treści w swoich widokach, używając podrzędnych. Aby zademonstrować tę możliwość, wstrzyknijmy część udostępnianej zawartości, na przykład pasek nawigacji, tworząc plik o nazwie nav.jade i umieszczając go w naszym katalogu views / Parts:

ul.nav

li.active

a(href='/example1') Example1

li

a(href='/example2') Example2

li

a(href='/example3') Example3

Będziemy również musieli zaktualizować nasz główny plik app.js o nowe trasy:

app.get('/', routes.index);

app.get('/example1', routes.example1);

app.get('/example2', routes.example2);

app.get('/example3', routes.example3);

Teraz musimy zbudować trzy nowe pliki przykładowe. Zaczniemy od example1.jade:

p Example 1

potem kolejny prosty plik, example2.jade:

p Example 2

i wreszcie ostatni, przykład3.jade:

p Example 3

Oto układ z wywołaniem podszablonu / nav:

!!! 5

html(lang='en')

head

meta(charset='utf-8')

title HTML5 Hacks

meta(name='description', content='')

meta(name='author', content='')

body

!= partial('partials/nav')

!= body

Kiedy poruszamy się po aplikacji, klikając linki na naszym pasku nawigacyjnym, zobaczymy, że udostępniamy teraz układ i część nawigacji z każdą przykładową stroną. Ten hack jest bardzo prostym pokazem, jak utrzymać porządek w naszych zasobach, ponieważ możesz sobie wyobrazić, że wraz ze wzrostem rozmiaru i złożoności hacków zastosowanie tego podejścia powinno ci dobrze służyć. Zanim przejdziemy do eksploracji Jade, zakończmy jeszcze jednym hackiem, który zademonstruje moc naszego nowego silnika szablonów.

Hack No.90

Użyj Jade Mixins, aby wypełnić swoje widoki danymi

Jade Mixis to kolejne narzędzie w Twoim zestawie narzędzi, którego możesz użyć w swoich widokach, aby zmniejszyć nadmiar kodu. Możesz użyć elementów mieszanych, aby ułatwić iterację danych z usługi internetowej, bazy danych lub pamięci. Powiedzmy, że mamy złożony obiekt przechowywany w pamięci lub utrwalony w bazie danych. Chcielibyśmy wydrukować kilka list, aby wyświetlić dane przechowywane w tym obiekcie. Aby to zrobić, użyjemy mixinów. Moc kryjąca się za tą funkcją polega na tym, że możemy teraz przekazywać obiekty do naszych widoków, przeglądać dane i dynamicznie wyświetlać ich właściwości na ekranie. Dzięki elementom mieszanym możemy przekazywać części danych jako obiekty, zagnieżdżać elementy mieszane w elementach mieszanych i utrzymywać nasz kod w porządku, zwięzły i SUCHY. Najpierw przyjrzyjmy się przekazywaniu złożonego obiektu z naszej trasy do naszego widoku.

Możesz się zastanawiać, dlaczego pracujemy ze złożonym obiektem. Zazwyczaj obiekty zwracane przez większość interfejsów API usług sieci Web mają wiele poziomów zagnieżdżenia. Jest to również typowe dla obiektu blob danych, do którego możemy uzyskać dostęp z bazy danych NoSQL, takiej jak MongoDB, CouchDB lub Redis. W naszym hacku będziemy emulować dane zwrócone z aplikacji Frozen Yogurt Shop. Na razie zakodujmy obiekt na stałe i przekażmy go jako drugi argument do metody renderowania naszego obiektu odpowiedzi.

exports.example3 = function(req, res){

res.render('example3', {

"name": "Yogurt Shop Daily Data",

"toppings":

[

{ "id": "5001", "type": "Walnuts" },

{ "id": "5002", "type": "Jelly Beans" },

{ "id": "5005", "type": "Cherries" },

{ "id": "5007", "type": "Powdered Sugar" },

{ "id": "5006", "type": "Chocolate Sprinkles" },

{ "id": "5003", "type": "Chocolate Syrup" },

{ "id": "5004", "type": "Cocunut" }

],

"yogurts":

[

{ "id": "5001", "type": "Tart", "flavors":

[

{ "id": "5001", "type": "Green Tea" },

{ "id": "5002", "type": "Euro" },

{ "id": "5005", "type": "Orange" }

]

},

{ "id": "5002", "type": "Sweet", "flavors":

[

{ "id": "5001", "type": "Vanilla" },

{ "id": "5002", "type": "Chocolate" },

{ "id": "5005", "type": "Mexican Bean" }

]

},

{ "id": "5005", "type": "Cake", "flavors":

[

{ "id": "5001", "type": "Cherry Cheesecake" },

{ "id": "5002", "type": "Apple Fritter" },

{ "id": "5005", "type": "Carrot Cake" }

]

}

]

})

};

Teraz powinniśmy mieć dostęp do danych w naszym widoku za pośrednictwem obiektu locals. Aby iterować te dane i wyświetlić je w naszym widoku, Jade zapewnia kilka bardzo przydatnych funkcji. Jednym z nich są mixy. Nasz plik example3.jade może teraz korzystać z tej funkcji:

h2 Yogurts and Toppings

mixin toppings(data)

ul.data

- each item in data

li= item.type

h2= name

h3 Toppings

mixin toppings(toppings)

Teraz możemy dodać więcej logiki do naszego pliku example3.jade. Możemy również zagnieżdżać mieszankę w innej mieszance.

h2 Yogurts and Toppings

mixin toppings(data)

ul.data

- each item in data

li= item.type

mixin yogurts(data)

ul.data

- each item in data

li= item.type

mixin flavors(data)

ul.data

- each item in data

li= item.type

mixin yogurts_nest(data)

ul.data

- each item in data

li= item.type

mixin flavors(item.flavors)

h2= name

h3 Toppings

mixin toppings(toppings)

h3 Yogurts

mixin yogurts(yogurts)

h3 Yogurts with Flavors

mixin yogurts_nest(yogurts)

Dzięki mocy metajęzyka Jade i niektórym z jego solidnych funkcji, jesteśmy w stanie stworzyć jasne i zwięzłe znaczniki do zarządzania naszymi widokami

Hack No.91

Skonfiguruj ekspresyjny, dynamiczny, solidny CSS za pomocą Stylus

Stylus ułatwia pisanie i konserwację CSS dla aplikacji Express. Jak wspomniano wcześniej, Stylus to dynamiczny metajęzyk, który kompiluje się do CSS. Zestaw funkcji rysika jest wyczerpujący, więc będziemy musieli wybrać i wybrać najlepsze dla zakresu poniższych hacków. To nie jest CSS babci; Stylus zapewnia mnóstwo wsparcia w żmudnym zadaniu pisania selektorów CSS. Ponownie, metajęzyk, taki jak Rysik, zawsze będzie opcjonalny, ale korzyści będą widoczne po zapoznaniu się. Najpierw dodajmy moduł Rysika do naszej obecnej aplikacji. W tym hacku po prostu włączymy Stylus i skompilujemy plik .styl do .css.

var express = require('express')

, routes = require('./routes')

, stylus = require('stylus');

var app = module.exports = express.createServer();

app.use(stylus.middleware({ debug: true,

src: __dirname + '/public',

dest: __dirname + '/public',

compile: compileMethod

}));

function compileMethod(str) {

return stylus(str)

.set('compress', true);

};

// Configuration

app.configure(function(){

app.set('views', __dirname + '/views');

app.set('view engine', 'jade');

app.use(express.bodyParser());

app.use(express.methodOverride());

app.use(app.router);

app.use(express.static(__dirname + '/public'));

});

// Routes

app.get('/', routes.index);

app.listen(3000);

console.log("Express server listening on port %d in %s mode",

app.address().port, app.settings.env);

Oto nowy arkusz stylów style.styl, w którym wykorzystamy naszą nową składnię:

body

font 12px Helvetica, Arial, sans-serif

background #D6396E

text-align left

ul

list-style none

Jeśli wrócimy do naszego style.css, zauważymy, że nasz arkusz stylów jest kompilowany w czasie wykonywania:

body{font:12px Helvetica,Arial,sans-serif;background:#D6396E;

text-align:left}

ul{list-style: none}

Mamy teraz uruchomioną aplikację na Node.js z Express, Jade i Stylus współpracującą w celu renderowania danych wyjściowych do przeglądarki internetowej

Hack No.92

Dołącz szablon HTML5 jako domyślny szablon startowy

Użyj HTML5 Boilerplate, profesjonalnego, frontendowego, opartego na programistach Szablon HTML / CSS / JavaScript dla szybkiej, solidnej i bezpiecznej w przyszłości witryny. HTML5 Boilerplate wyewoluował do de facto standardu opracowywania podstaw do rozwiązywania najczęstszych problemów i rozważań potrzebnych do dzisiejszego tworzenia profesjonalnych aplikacji internetowych. Przyjrzymy się dokładniej niektórym elementom z wyczerpującej listy powodów, dla których powinniśmy używać szablonu do uruchamiania dowolnej aplikacji hakerskiej HTML5 lub aplikacji gotowej do produkcji. Przejdź do trybu online, aby zobaczyć pełną listę. Zaangażowani programiści przeprowadzili wyczerpujące badania w każdym z tych obszarów, a ten szablon wyewoluował w fantastyczny zbiór najlepszych praktyk. Podejście, które podejmiesz, aby włączyć HTML5 Boilerplate do projektu, różni się w zależności od środowiska. Zacznijmy od najprostszego sposobu, a następnie możemy zintegrować go z naszymi aplikacjami Node.js za pomocą Jade i Stylusa. Rozpocznij od pobrania pliku archiwum. Teraz otwórz plik index.html, a zobaczysz następujące standardowe znaczniki HTML:

< !doctype html >

< !-

paulirish.com/2008/conditional-stylesheets-vs-css-hacks-answer-neither/

-->

< !--[if lt IE 7] >

< html class="no-js lt-ie9 lt-ie8 lt-ie7" lang="en" >

< ![endif]-- >

< !--[if IE 7]>

< ![endif]-- >

< !--[if IE 8]> < ![endif]-- >

< !--[if gt IE 8]> < !--

< head >

< meta charset="utf-8" >

< title >HTML5 Hacks< /title >

< meta name="description" content="" >

< !-- Mobile viewport optimized: h5bp.com/viewport -- >

< meta name="viewport" content="width=device-width" >

< !-- Place favicon.ico and apple-touch-icon.png in the root

directory: mathiasbynens.be/notes/touch-icons -- >

< link rel="stylesheet" href="css/style.css" >

< !-- More ideas for your < head > here: h5bp.com/d/head-Tips -- >

< !-- All JavaScript at the bottom, except this Modernizr build.

Modernizr umożliwia wykrywanie elementów i funkcji HTML5 w celu uzyskania optymalnej wydajności. Utwórz własną niestandardową kompilację Modernizr:

www.modernizr.com/download/ -- >

< script src="js/libs/modernizr-2.5.3.min.js" >< /script >

< /head >

< body >

< !-- Prompt IE 6 users to install Chrome Frame. Remove this

if you support IE 6.

chromium.org/developers/how-tos/chrome-frame-getting-started -- >

< !--[if lt IE 7] >< p class=chromeframe>Your browser is

< em >ancient!< /em > < a href=http://browsehappy.com/ >Upgrade to a different browser< /a > or < a href=http://www.google.com/chromeframe/?redirect=true >install Google Chrome Frame to experience this site.< /p >

< ![endif]-- >

< header >

< /header >

< div role="main" >

< p >Welcome to HTML5< /p >

< /div >

< footer >

< /footer >

< !-- JavaScript at the bottom for fast page loading -- >

< !-- Grab Google CDN′s jQuery, with a protocol relative URL;

fall back to local if offline -- >

< script

src="//ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" >

< /script >

< script >

window.jQuery || document.write('

< script src="js/libs/jquery-1.7.1.min.js" >< \/script >')

< /script >

< !-- scripts concatenated and minified via build script -- >

< script src="js/plugins.js" >< /script >

< script src="js/script.js" >< /script >

< !-- end scripts -- >

< /body >

< /html >

Ze względu na ten hack usunąłem niektóre mniej znaczące kody i komentarze, ponieważ są poza zakresem. Można by napisać całą książkę o kwestiach i kwestiach poruszanych w tym szablonie. Skoncentrujemy się na elementach, które są najbardziej związane z HTML5; to z pewnością wystarczy, aby zacząć. Otwórz plik index.html w przeglądarce internetowej, a będziesz miał punkt wyjścia Twoich hacków HTML5. Przyjrzyjmy się bliżej siedmiu najważniejszym aspektom tego standardowego schematu:

< doctype >

Jak wspomniano, dołączona jest nasza deklaracja HTML5 .

Arkusze stylów warunkowych

Arkusze stylów warunkowych zostały opracowane w celu wyeliminowania błędów renderowania CSS w wielu powszechnie stosowanych przeglądarkach. Ponieważ nie wszystkie przeglądarki poprawnie implementują specyfikacje CSS wydane przez W3C, pisanie CSS w różnych przeglądarkach może być skomplikowane. Podejście warunkowe z arkuszami stylów pomaga zapewnić spójne renderowanie witryny w tak wielu popularnych przeglądarkach, jak to możliwe, i łagodzi niektóre problemy inżynierów frontendu. Koncepcja wywodzi się z metody warunkowego komentowania w programie Internet Explorer i od tego czasu ewoluowała przy wsparciu innych inżynierów frontendu. Możesz śledzić ewolucję na paulirish.com. Rozwiązanie to jest starsze niż HTML5 Boilerplate, ale stało się ważnym narzędziem do użycia przy rozpoczynaniu tworzenia aplikacji HTML5, stąd powód jego integracji z szablonem.

Modernizr

Modernizr stanowi punkt wyjścia do tworzenia najlepszych witryn i aplikacji, które działają dokładnie bez względu na przeglądarkę lub urządzenie używane przez odwiedzających. W pakiecie Modernizr znajdują się testy zapytań o media i wbudowana mikro biblioteka YepNope.js jako Modernizr.load ().

Chrome Frame

Google Chrome Frame to wtyczka typu open source, która integruje Google Chrome z Internet Explorerem. Wraz z wtyczką Google Chrome Frame otrzymujesz interpreter JavaScript V8 przeglądarki Chrome, obsługę tagów kanwy HTML5 i inne otwarte technologie internetowe niedostępne w Internet Explorerze.

Elementy nagłówka i stopki

Wspomniany wcześniej, nasz szablon domyślnie zawiera elementy nagłówka i stopki.

JQuery Google CDN

jQuery to powszechnie używana biblioteka narzędziowa, która zasadniczo rozszerza możliwości przeglądarki. Jest testowany w różnych przeglądarkach pod kątem obsługi dowolnej różnicy między przeglądarkami. CDN to sieć dostarczania treści, której celem jest rozwiązanie problemu opóźnień w sieci dla zasobów statycznych, takich jak arkusze stylów, obrazy i JavaScript.

Zoptymalizowany JavaScript

Nasz JavaScript powinien być konkatenowany i zminimalizowany za pomocą skryptów kompilacji, a także dołączony na dole strony. Oto trzy podstawowe zasady wysoce zoptymalizowanych stron internetowych, które spopularyzował Steve Souders. Aby uzyskać więcej informacji, odwiedź stronę stevesouders.com.

Integracja z aplikacją Node.js / Express

Niestety, zapewnia to korzyść tylko w przypadku hakowania funkcji HTML5, które nie są zależne od komunikacji z serwerem HTTP lub WebSocket. Aby mieć w pełni funkcjonalną aplikację, którą można wdrożyć na zdalnym serwerze, musimy zintegrować ten schemat z naszą podstawową aplikacją internetową, którą zbudowaliśmy na początku. Teraz, gdy aplikacja jest już uruchomiona, zaktualizujmy pliki index.jade i layout.jade, aby odzwierciedlić to, czego nauczyliśmy się z HTML5 Boilerplate. Oto nasze oryginalne pliki .jade - layout.jade:

!!! 5

html(lang='en')

head

meta(charset='utf-8')

title HTML5 Hacks

meta(name='description', content='')

meta(name='author', content='')

// Styles

link(href='assets/css/bootstrap.css', rel='stylesheet')

script(src='assets/js/application.js')

body

!= partial('partials/nav')

!= body

and index.jade:

P HTML5 Hacks

Po dodaniu dodatkowych znaczników i deklaracji skryptu z naszego standardowego schematu otrzymujemy:

!!! 5

//if lt IE 7

html(class="no-js ie6 oldie", lang="en")

//if IE 7

html(class="no-js ie7 oldie", lang="en")

//if IE 8

html(class="no-js ie8 oldie", lang="en")

// [if gt IE 8]
html(class="no-js", lang="en")

// < ![endif]

head

meta(charset='utf-8')

title HTML5 Hacks

meta(name='description', content='')

meta(name='author', content='')

meta(name='description', content='')

// Styles

link(href='css/style.css', rel='stylesheet')

script(src='js/libs/modernizr-2.5.3.min.js')

body

// [if lt IE 7] >

Your browser is

< em >ancient!< /em > < a href=http://browsehappy.com/ >

// Upgrade to a different browser< /a > or < a href=http://www.google.com/chromeframe/?redirect=true >install

// Google Chrome Frame to experience this site.< /p >

< ![endif]

header

!= partial('partials/nav')

div(role="main")

!= body

footer

// JavaScript at the bottom for fast page loading

// Grab Google CDN's jQuery, with a protocol relative URL;

fall back to local if offline

script(

src='http//ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js')

script

window.jQuery || document.write('

< script src="js/libs/jquery-1.7.1.min.js" > < \/script >')

script(src='js/plugins.js')

script(src='js/script.js)

Jak widać, posiadanie naszych znaczników, stylów i deklaracji skryptów w plikach .jade sprawia, że zarządzanie tym szablonem jest mniej skomplikowane.



Odwiedzin: 905
Dzisiaj: 1
On-line: 1
Strona istnieje: 1379 dni
Ładowanie: 0.434 sek


[ 392 ]