Naše JavaScript soubory rostou. Co zacalo jako pár radku jQuery pro validaci formuláře a AJAX volání, se změnilo v tisíce radku neprehledneho kodu. jQuery je skvělý pro manipulaci s DOM, ale neposkytuje žádnou strukturu pro organizaci aplikacni logiky. Backbone.js od Jeremyho Ashkenase řeší presne tento problem — prinasi modely, kolekce, pohledy a router do frontendoveho vývoje.
Proc potřebujeme strukturu na frontendu¶
Pred třemi lety byl typicky webový frontend jednoduchý — server vyrendrovál HTML, jQuery přidalo pár interaktivních efektu. Ale s rostoucími ocekavanimi uzivatelu se frontend stal slozitejsim. Gmail, Google Maps, Facebook — tyto aplikace běží převážně v prohlížeči a server slouží hlavně jako API. Kdyz mate tisíce radku JavaScriptu bez jasne struktury, každá zmena se stává rizikem.
Problem není v jQuery — jQuery dělá presne to, k čemu byl navržen. Problem je v absenci architektonických vzoru na frontendu. Na serveru máme MVC (Spring MVC, Rails, Django), dependency injection, service layer. Na frontendu jsme měli soubor plný $() selektoru a anonymnich funkci. Backbone.js je prvni široce adoptovany pokus o změnú tohoto stavu.
Modely (Models)¶
Backbone Model reprezentuje datovou entitu — uzivatele, produkt, objednávku. Model obsahuje atributy (data), validační logiku a metody pro komunikaci se serverem (save, fetch, destroy). Klíčová vlastnost je, ze model vyvolava udalosti pri zmene atributů — kdyz se zmeni jmeno uzivatele, vsechny pohledy, které toto jmeno zobrazují, se automaticky aktualizují.
Toto je zakladni princip data binding — Model je jediný zdroj pravdy a pohledy na něj reagují. V jQuery svete jsme typicky měli data ulozena v DOM (v atributech, v textu elementů) a JavaScript je odsud cet a tam zapisoval. S Backbone modely jsou data oddělená od prezentace. Chcete změnit jmeno uzivatele? Změníte atribut modelu a vsechny pohledy se aktualizují samy.
Model take abstrahují komunikaci se serverem. Backbone předpokládá RESTful JSON API. Volání model.save() pošle POST nebo PUT požadavek na server, model.fetch() načte data pres GET, model.destroy() pošle DELETE. URL se automaticky odvozuje z korenovahe URL kolekce a ID modelu. Tato konvence nad konfiguraci vyrazne snižuje boilerplate kod.
Kolekce (Collections)¶
Collection je usporadana množina modelu stejného typu. Kolekce uzivatelu, kolekce produktu, kolekce objednávek. Collection poskytuje metody pro přidávání, odebirani, filtrování a řazení modelu. Díky integraci s Underscore.js (sourozenec Backbone) mate k dispozici bohatou sadu funkcionalnich metod — map, filter, reduce, find, sortBy, groupBy.
Stejne jako modely, kolekce vyvolvavaji udalosti — add, remove, reset, sort. Pohled zobrazující seznam produktu muze naslouchat na tyto udalosti a automaticky se prekreslovat pri zmene dat. Přidání noveho produktu? Kolekce vyvolá událost add, pohled přidá nový řádek do seznamu. Smazání produktu? Událost remove, pohled odebere řádek. Žádná manuální manipulace s DOM.
Pohledy (Views)¶
Backbone View je zodpovědný za renderování HTML a zpracování uzivatelsych interakci. Kazdy pohled je spojen s DOM elementem a muze naslouchat na DOM udalosti (click, submit, keyup) v rámci svého elementů. Pohled typicky naslouchá na zmeny v modelu a pri zmene se prerenderuje.
Backbone nepredpisuje sablonovy system — můžete pouzit cokoliv od jednoduchých string templates pres Underscore templates az po Handlebars nebo Mustache. My pouzivame Handlebars, protože nabízí logické helpery a partials bez toho, aby umožňoval slozitou logiku v sablonach (separation of concerns). Pohled připraví data z modelu, předá je sablone a výsledné HTML vloží do DOM.
Důležitý detail — Backbone views nezajistuji automaticky efektivni prekreslovani. Kdyz zavoláte render(), celý obsah pohledu se nahradí novým HTML. Pro jednoduché pohledy to není problem, ale pro slozite seznamy s desítkami položek to muze byt pomalé. Optimalizace vyzaduje granularnejsi aktualizace — misto prerenderovani celého seznamu aktualizovat jen změněny řádek. To je oblast, kde Backbone nechává rozhodnutí na vývojáři.
Router¶
Backbone Router mapuje URL hash fragmenty na funkce. Kdyz uživatel přejde na #users/123, router zavolá funkci, která načte uzivatele s ID 123 a zobrazí příslušný pohled. To umožňuje vytvořit single-page aplikaci s plnou podporou historie prohlížeče a bookmarků. Uživatel muze používat tlačítka zpět a vpřed, sdílet URL a obnovit stránku bez ztráty stavu.
Router je jednodušší nez routery v serverových frameworcích — nemá middleware, nemá named routes, nemá nestovani. Pro složitější aplikace to muze byt omezeni a často vidíme developery, kteří si vytvářejí vlastní abstrakci nad Backbone routerem. Ale pro zakladni use case je vestavěný router dostačující.
Naše architektura s Backbone¶
Po několika projektech jsme si vyvinuli standardní architekturu pro Backbone aplikace. Na vrcholu je Application objekt, který inicializuje router a sdílené sluzby. Router deleguje na Controller objekty (Backbone je nemá nativně, přidali jsme si je), které koordinují načítání dat a vytváření pohledu. Pohledy jsou hierarchické — hlavní layout pohled obsahuje regionya kazdy region hostí specificky pod-pohled.
Pro komunikaci mezi nesouvisejicimi pohledy pouzivame event aggregator — globální event bus, na který mohou pohledy vyvolavat a naslouchat udalosti. To zamezuje primym zavislostem mezi pohledy a dělá kod flexibilnejsim. Například pohled košíku nevola přímo pohled produktového katalogu — místo toho vyvolá událost product:add-to-cart a košík naslouchá.
Tato architektura je inspirovaná frameworkem Marionette.js od Derricka Baileye, který přidává do Backbone koncepty jako regions, layouts a composite views. Zvažujeme jeho plnou adopci pro další projekt.
Testovani¶
Backbone kod je dobre testovatelný, protože modely a kolekce nejsou závislé na DOM. Můžete testovat validaci modelu, vypocitane atributy a interakci s API pomoci unit testu bez prohlížeče. Pro testovani pouzivame Jasmine — BDD framework pro JavaScript s čistou syntaxi (describe, it, expect). Pro pohledy testujeme, ze spravne renderují HTML a reagují na udalosti.
Mockování API volání zajistujeme pres Sinon.js FakeServer, který zachytává AJAX požadavky a vracía předpřipravené odpovedi. To umožňuje testovat celý flow — od načtení dat pres model az po zobrazení v pohledu — bez skutečného serveru.
Limity Backbone¶
Backbone je zámerne minimalistický. Jeremy Ashkenas ho navrhl jako „minimalni sadu nástrojů pro strukturovani JavaScriptu”, ne jako kompletni framework. To znamená, ze spoustu veci musíte resit sami — memory management (odhlasovani event listeneru pri ruseni pohledu), efektivni renderování seznamu, komunikace mezi pohledy, lazy loading modulu. Pro kazdy z těchto problemu existují řešení v komunitě, ale nejsou součástí core frameworku.
Take je důležité si uvedomit, ze Backbone není jediná možnost. Knockout.js nabízí deklarativni data binding. Ember.js je kompletnejsi framework s konvencemi. A na horizontu se rysuje AngularJS od Googlu, který slibuje radikálně jiný pristup. Ale dnes, na podzim 2011, je Backbone nejzralejší a nejsirseji adoptovaná volba pro strukturovani frontendoveho JavaScriptu.
Zaver¶
Backbone.js prinasi tolik potřebnou strukturu do frontendoveho vývoje. Models, Collections, Views a Router jsou zakladni stavební kameny, které umožňují organizovat rostouci JavaScript kódovou bázi. Není to stříbrná kulka — vyzaduje disciplínu a doplnkove knihovny — ale je to obrovský krok vpřed oproti jQuery spaghetti. Doporučujeme každému tymu, který staví netriviální webovou aplikaci.
Potřebujete pomoc s implementací?
Naši experti vám pomohou s návrhem, implementací i provozem. Od architektury po produkci.
Kontaktujte nás