WEBES ALKALMAZÁSFEJLESZTÉS 1. Horváth Győző Egyetemi adjunktus 1117 Budapest, Pázmány Péter sétány 1/C, 2.420 Tel: (1) 372-2500/1816
2 Nagy méretű webes alkalmazások
Kliensoldali webes alkalmazások 3 fejlődése 2000-es évek eleje: szerveroldali technológiák Megbízhatatlan kliensoldal, kiforratlan környezet, ismeretlen nyelv HTTP input HTML oldal generálása Üzleti logika Adatok perzisztálása Felhasználókezelés Kapcsolattartás szolgáltatásokkal
4 Kliensoldali webes alkalmazások fejlődése 2005-től: Ajax technológia Kényelmes, élményszerű webes alkalmazások Nyelv felfedezése Technológiák fejlődése Böngészők fejlődése Nagy kódbázis a kliensoldalon Szerepkörök egy része kliensoldalon HTML generálás Adatok feldolgozása Adatok tárolása
Egyoldalas alkalmazások 5 Új alkalmazáskategória Single Page Application (SPA) Jellegzetességei oldal egyszer töltődik be további szerverkommunikáció a háttérben (Ajax) főleg adatcsere Nagyon sok JavaScript kód
Kódszervezési problémák 6 Függvények, objektumok, modulok Sok modul hogyan szerveződik alkalmazássá? Az adat és a nézet hogyan különül el? Segíthet ebben a jquery? felelősségi körök elválasztása
7 Alkalmazás feladatai
8 Alkalmazás feladatai
Alkalmazás feladatai 9 modulok kezelése események kezelése DOM kezelése, elemek megjelenítése adatok és üzleti logika definiálása adatok tárolása, mentése (pl. AJAX) URL kezelése
10 Modulkezelés
Modulkezelés 11 Alkalmazásszintű modulkezelés egy olyan alapvető alkalmazásarchitektúra definiálását jelenti, amely elősegíti a modulok együttműködését és kommunikációját anélkül, hogy közben szorosan kapcsolódjanak egymáshoz. Modul = az alkalmazás egy független része Koncepciók Nicholas C. Zakas Addy Osmani
Modulkezelés 12 Modulkezelés funkciói névterezés (névtér minta) minden modul számára egy közös alapfunkcionalitás biztosítása (homokozó minta) modulok élettartamának kezelése (regisztrálás, indítás, megállítás) eseménykezelő rendszer biztosítása a modulok kommunikációjához alkalmazásszintű adatok kezelése bővítmények definiálása
13 Architektúrakoncepció
Modulok 14 Felhasználói felület egyes részei JavaScript kód + adatok + felületi elemek Szabványos modulinterfész, amit minden modulnak implementálnia kell var moduleinterface = { start: function () {}, stop: function () {} };
Homokozó objektum 15 Modulok nem tudnak a többiről Kommunikáció a homokozó objektumon keresztül Minden modul megkapja egy példányát Homlokzat minta (Facade pattern) = közös API Funkciók eseménykezelés DOM műveletek AJAX stb
Homokozó objektum 16 Homokozó prototípus var sandboxproto = extenddeep({ //... }, observable); Példa moduldefiníció var module1 = function (sandbox) { //A modul implementációja //sandbox használata //Publikus API meghatározása return extenddeep(object.create(moduleinterface), { //... }); };
var weathermodule = function (sandbox) { var state = 'sunny', changestate = function (newstate, hours) { state = newstate; sandbox.notify(newstate, hours); }; return extenddeep(object.create(moduleinterface), { changestate: changestate }); }; var smartphonemodule = function (sandbox) { var warnforrain = function (hours) { console.log('rain is coming in ', hours, 'hours!'); }, prepareforsun = function (hours) { console.log('the sun will shine in ', hours, 'hours!'); }, init = function () { sandbox.addobserver('rainy', warnforrain); sandbox.addobserver('sunny', prepareforsun); }; return extenddeep(object.create(moduleinterface), { init: init }); }; 17
Alkalmazásobjektum 18 Modulok kezelése regisztrálás indítás leállítás hibakezelés modulok közti kommunikáció
var application = (function () { var modules = {}; return { 19 Alkalmazásobjektum register: function (moduleid, module) { var moduleinstance = module(object.create(sandboxproto)); modules[moduleid] = moduleinstance; return moduleinstance; }, start: function (moduleid) { modules[moduleid].start(); }, stop: function (moduleid) { modules[moduleid].stop(); }, startall: function () { for (moduleid in modules) { if (modules.hasownproperty(moduleid)) { this.start(moduleid); } } } //... }; })(); application.register('weather', weathermodule); application.register('smartphone', smartphonemodule); application.startall();
Kiegészítők 20 Alkalmazás funkcionalitásának gazdagítása Beépülhet homokozó objektum interfészébe alkalmazásba
Megvalósítások 21 Terrific Aura scaleapp Kernel.js Hydra.js
22 Eseménykezelés
Eseményt kiváltó objektumok 23 Események kibocsátása + megfigyelése Megfigyelő minta Példa: böngésző és DOM objektumai Hátránya: referencia a megfigyelő objektumra szoros kapcsolat kapcsolat karbantartására fordított idő nő
Eseménygyűjtők 24 Központi eseményvezérlő minta Előnye a megfigyelőnek és a megfigyeltnek nem kell tudnia egymásról lazán kapcsolt rendszerek tipikusan a homokozó objektumon keresztül valósul meg Hátránya nincs biztosítva az esemény feldolgozása
Eseménybuszok 25 Jól kontrollált és adminisztrált környezet üzenetek kezelésére állapotmenedzsment logolás üzeneteknek kötött formátuma van
Eseménysorok 26 Speciális eseménybusz Eltérő feldolgozási sebesség Sorrend megtartása
Eseménykezelési stratégia kiválasztása 27 Esemény kibocsátása, megfigyelhetőség: aszinkron viselkedést nyújtó függvénykönyvtárak Eseménygyűjtő: alkalmazás egyes részei közötti kommunikáció Eseménybusz: rétegek közötti kommunikáció nagy méretű alkalmazásoknál
28 MN* architektúrák
MNV-minta (MVC) 29 Kiindulási pont: adat és nézet szétválasztása Architekturális tervezési minta Modell: adat + feldolgozási logika Nézet: felhasználói felület kezelésével kapcsolatos logika Vezérlő: a modell és a nézet irányítása
Eredeti MNV-minta 30 Smalltalk-80 (1979) Jellegzetességei: Modell független a felhasználói felülettől Nézet-vezérlő pár a megjelenítéshez Vezérlő a bemeneti adatok feldolgozásáért felel
MNP-minta (MVP) 31 Modell-Nézet-Prezenter Passzív nézet Prezenter: központi irányító Nézet: interfész
MNNM-minta (MVVM) 32 Modell-Nézet-Nézetmodell Deklaratív nézet Kétirányú adatkötés
MN* architektúrák 33 Az előző architektúrák alkalmazkodtak a kliensoldal jellegzetességeihez Maradt modell nézet Többi sokféle MN* architektúra
MN* architektúrák 34 Kijelöli az adat és megjelenítés helyét Lazább kapcsolat Nagyobb rugalmasság Nagy kódbázisnál érdemes használni Jól bevált megoldások egyéb dolgokra is
35 MN* architektúrák alkotóelemei
Megfigyelhető modellek 36 Modell változás esetén eseményt bocsát ki adatkötés Elemei modellt megfigyelhetővé tenni megfigyelő minta változáskor eseményt kibocsátani Utóbbira megoldások get(), set() metódusok natív getter és setter használata adattagok metódusokká alakítása Object.observe()
get() és set() metódusok 37 var book = { author: 'Stoyan Stefanov', title: 'JavaScript Patterns' }; var observablebook = makeobservable(book); observablebook.addobserver('change', function (book) { console.log('book changed', book); }); observablebook.addobserver('change:title', function (title) { console.log('title changed', title); }); observablebook.set('title', 'JavaScript Patterns rev.3.'); var title = observablebook.get('title');
get() és set() metódusok 38 var makeobservable = function(o) { return extenddeep({}, observable, { get: function (attr) { return this.attributes[attr]; }, set: function (attr, val) { this.attributes[attr] = val; this.notify('change:' + attr, val); this.notify('change', this.attributes); }, attributes: extenddeep({}, o) }) };
Natív getter és setter 39 var makeobservable = function(o) { var newo = extenddeep({}, observable); var attributes = {}; for (var i in o) { if (o.hasownproperty(i)) { attributes[i] = o[i]; Object.defineProperty(newO, i, { enumerable: true, get: function() { return attributes[i]; }, set: function(val) { attributes[i] = val; this.notify('change:' + i, val); this.notify('change', attributes); } }); } } return newo; }; observablebook.title = 'JavaScript Patterns rev.3.'; var title = observablebook.title;
Adattagok metódussá alakítása 40 var makeobservable = function(o) { var newo = extenddeep({}, observable); var attributes = {}; for (var i in o) { if (o.hasownproperty(i)) { attributes[i] = o[i]; newo[i] = function(val) { if (arguments.length > 0) { attributes[i] = arguments[0]; this.notify('change:' + i, val); this.notify('change', attributes); } else { return attributes[i]; } } } } return newo; }; observablebook.title('javascript Patterns rev.3.'); var title = observablebook.title();
Sablon-kezelés (template-ek) 41 Felhasználói felület kialakítása Szövegösszefűzés rossz gyakorlat feladatkörök szétválasztása sérül: logika + HTML elemek körülményes és olvashatatlan nagy számítási igény function createlistitem(book) { return '<li><a href="show/' + book.id + '">' + book.title + '</a></li>'; }
Sablon 42 Sablonnyelv + sablonfeldolgozó HTML struktúra + dinamikus elemeket jelölő részek változók ciklus (logika?) elágazás (logika?) Sablonnyelvek Jade, Mustache, Handlebars, Haml, EJS, Plates, Pure, Underscore microtemplates
Sablonnyelvek 43 Jade li a(href='show/'+id)= title Handlebars.js <li> <a href="show/{{id}}">{{title}}</a> </li> Underscore <li> <a href="show/<%= id %>"><%= title %></a> </li>
Sablon és sablonfeldolgozó 44 <script id="listitem" type="text/template"> <li> <a href="show/<%= id %>"><%= title %></a> </li> </script> //Sablonszöveg kiolvasása var templatestring = $('#listitem').html(); //Sablon fordítása var compiledtemplate = _.template(templatestring); //Adat megadása var book = { id: 12, title: 'JavaScript Patterns' }; //HTML-szöveg előállítása var htmlstring = compiledtemplate(book); //A HTML-szöveg beillesztése a dokumentumba $('#viewwrapper').html(htmlstring);
45 minimvc
Prekoncepciók 46 Hagyományos MVC-minta Függőségek jquery Underscore
47 Modell var minimvc = {}; minimvc.model = (function() { var proto = extenddeep({ get: function(attr) { return this.attributes[attr]; }, set: function(attr, val) { this.attributes[attr] = val; this.notify('change:' + attr, val); this.notify('change', this.attributes); }, tojson: function() { return _.clone(this.attributes); } }, observable); return { create: function(attrs) { return extendshallow(object.create(proto), { id: _.uniqueid('model'), attributes: attrs {} }); } }; })();
Modell használata 48 //"Hagyományos" modell var book = { author: 'Stoyan Stefanov', title: 'JavaScript Patterns' }; //minimvc modell var book = minimvc.model.create({ author: 'Stoyan Stefanov', title: 'JavaScript Patterns' }); book.addobserver('change:title', function (val) { console.log('a könyv címe megváltozott: ', val); }); book.set('title', 'Pro JavaScript Patterns');
Nézet 49 Üres objektumliterál minimvc.view = (function() { var proto = {}; return { create: function(attrs) { return extendshallow(object.create(proto), { id: _.uniqueid('view') }, attrs {}); } }; })();
Vezérlő 50 minimvc.controller = (function() { var proto = {}; return { create: function(attrs) { var obj = extendshallow(object.create(proto), { id: _.uniqueid('controller') }, attrs {}); if (attrs.events) { _.each(attrs.events, function(methodname, domeventname) { var parts = domeventname.split('.'); var selector = parts[0]; var eventtype = parts[1]; $(document).on(eventtype, selector, function(e) { obj[methodname](e); }); }); } } }; })(); return obj;
51 Használata
52 HTML oldal <!doctype html> <html> <head> <title>book</title> </head> <body> <div id="bookform"></div> <div id="bookpanel"></div> <script type="text/template" id="bookform-template"> Title: <input type="text" id="txttitle" value="<%= title %>"> <br> Author: <input type="text" id="txtauthor" value="<%= author %>"> </script> <script type="text/template" id="bookpanel-template"> Title: <%= title %> <br> Author: <%= author %> </script> <script type="text/javascript" src="jquery.js"></script> <script type="text/javascript" src="underscore.js"></script> <script type="text/javascript" src="extenddeep.js"></script> <script type="text/javascript" src="extendshallow.js"></script> <script type="text/javascript" src="observable.js"></script> <script type="text/javascript" src="minimvc.js"></script> <script type="text/javascript" src="book.js"></script> </body>
Modell 53 var book = minimvc.model.create({ author: 'Stoyan Stefanov', title: 'JavaScript Patterns' });
Űrlap nézet 54 var formview = minimvc.view.create({ el: '#bookform', model: book, template: _.template($('#bookform-template').html()), initialize: function() { this.model.addobserver('change', this.updateview.bind(this)); this.render(this.model.tojson()); }, render: function(data) { $(this.el).html(this.template(data)); }, updateview: function(data) { $(this.el).find('#txttitle').val(data.title); $(this.el).find('#txtauthor').val(data.author); } });
Panel nézet 55 var panelview = minimvc.view.create({ el: '#bookpanel', model: book, template: _.template($('#bookpanel-template').html()), initialize: function() { this.model.addobserver('change', this.render.bind(this)); this.render(this.model.tojson()); }, render: function(data) { $(this.el).html(this.template(data)); } });
Vezérlő 56 var bookcontroller = minimvc.controller.create({ model: book, formview: formview, panelview: panelview, events: { '#txttitle.keyup': 'writing', '#txtauthor.keyup': 'writing' }, initialize: function() { this.formview.initialize(); this.panelview.initialize(); }, writing: function(e) { this.model.set('title', $('#txttitle').val()); this.model.set('author', $('#txtauthor').val()); } }); bookcontroller.initialize();
57 MN* keretrendszerek
Keretrendszerek 58 Backbone (MVC vagy MVR) Knockout (MVVM) Ember Angular (MVW)
59 Backbone