IMPORTANT: Per accedir als fitxer de subversion: http://acacha.org/svn (sense password). Poc a poc s'aniran migrant els enllaços. Encara però funciona el subversion de la farga però no se sap fins quan... (usuari: prova i la paraula de pas 123456)

http://singlepageappbook.com/

http://itsnat.sourceforge.net/php/spim/spi_manifesto_en.php
http://www.johnpapa.net/pageinspa/
Definition: https://streamacon.com/video/laracon-us/evan-you-vue-router-and-vuex

aka also known as single-page interface (SPI)

Introducció

Les Aplicacions d'una sola pàgina es distingeixen de les aplicacions web tradicionals per la seva habilitat de redibuixar qualsevol part de la Interfície d'usuari sense necessitat de tornar a fer una petició sencera nova al servidor. Això s'aconsegueix separant les dades de la presentació (vegeu el patró Separation Of Concerns a Design patterns) utilitzant una capa o captes transitòria/es entre les capes de vista (presentació) i una de dades (model). (Vegeu també MVC i altres models com MVVM)

IMPORTANT: Quin és l'objectiu de seguir un model SPA? la raó principal és que permet oferir una experiència d'usuari més "app-like" és a dir que l'usuari tingui la sensació de treballar amb una aplicació d'escriptori o una app nativa aplicacions que pel que fa a la interfície d'usuari solen tenir una UX més fluida i amb més interacció amb l'usuari

El concepte SPA se sol aplicar només als entorns web, és a dir al que coneixem com a web apps (o també a aplicacions híbrides, vegeu Desenvolupament multiplataforma). La base per a realitzar aplicacions SPA es troba en els estàndards web o el que coneixem com la triada web: HTML5, CSS3 i Javascript on cada protocol té el seu rol:

1409729756css_three-layers.png

El comportament possiblement és la part més complexa i la que sol necessitar de més suport i aquesta és la raó per la que han aparegut tants Javascript Frameworks per ajudar al desenvolupament de aplicacions SPA. És important però també destacar també l'aparició de CSS frameworks i eines CSS com Less/Sass/PostCSS per ajudar al desenvolupament tant de pàgines web com d'aplicacions web SPA (o no).

Que vol dir Page? I single?

Interpretació incorrecte:

Una possible definició de pàgina és:

“totality of whatever occupies the four walls of the application UI

Per tant si el contingut canvia a una tenim una nova pàgina. Per tant ja no tenim una sola pàgina i tenim dos per tant ja no és SPA? El problema és que la definició d pàgina no és aquesta en SPA...

Exemple web app amb canvi de pàgines : http://cc-ng-z.azurewebsites.net/#/speakers

En SPA la pàgina single és la pàgina web que el servidor envia el primer cop que ens connectem a l'aplicació i l'aplicació s'inicia. A partir d'aquí ja no hi ha més recarregues de pàgines des del servidor (compte carregues totals: si poden haver comunicacions o carregues parcials amb el servidor via Ajax quan cal canviar quelcom al backend o també pot haver comunicació push del servidor al frontend via notificacions push o també amb websockets). Un cop la pàgina és carrega tota la lògica de presentació i d'estat de l'aplicació s'executa al client.

Per tant el que defineix una aplicació SPA és el rol que pren el servidor i el client:

  • El servidor no pren part del control de l'aplicació. El servidor no té lògica de la user Interface de l'aplicació ni manté l'estat de l'aplicació. El server si que pot proveir de recursos a l'aplicació com per exemple la pàgina inicial o dades posteriors de forma asíncrona (fragments HTML, Jsons obtinguts per peticions Ajax, etc.)
  • El client (navegador) és qui determina que fa l'aplicació. És el client qui composa les pàgines web (presentació) sovint utilitzant HTML templates (o HTML fragments) i dades que s'obtenen del servidor o a la carrega inicial o de forma asíncrona utilitzant AJAX

Resources:

Parts d'una aplicació web moderna

Vegem els apartats en els que es divideix una modern web app:

  • Architecture: en quines parts (conceptuals) consisteix l'aplicació? Com es comuniquen les diferents parts entre elles? Com depenen entre elles?
  • Asset packaging: com s'estructura l'aplicació en fitxers i en mòduls lògics? Com es construeixen els mòduls i com es carreguen al navegador? Com es poden carregar els mòduls per a realitzar unit testing?
  • Run-time state: quan es carrega l'aplicació al navegador quines parts de la app estan a la memòria? Com es realitzen les transicions entre estats?

Vegem un exemple d'arquitectura:

overview.png

Alguns comentaris:

  • Write-only DOM. No s'ha de llegir dades mai del DOM, el DOM només ha de ser escrit i no ha de ser mai l'origen de dades o estat. Una aplicació mostra com a sortida un contingut HTML formatat amb CSS és a dir escriu un DOM però no ha de llegir mai dades directament del DOM. Emmagatzemar l'estat de l'aplicació al DOM crear codi difícil de mantenir. És molt millor tenir un lloc específic per a les dades i fer un render de la Interficie d'usuari a partir de les dades, especialment si les mateixes dades s'han de mostrar a múltiples llocs de la interfície d'usuari.
  • Models as the single source of truth: com a resultat de lo anterior, en comptes de guardar les dades al DOM o a altres objectes s'han de crear uns models que siguin la representació en memòria de l'estat i les dades de l'aplicació.
  • Views observe model changes: Volem que les vistes reflecteixen en temps real l'estat dels models. Quan múltiples vistes depenen d'un mateix model, no volem tenir que manualment fer un seguiments dels canvis i re-escriure les vistes. En comptes de fer un seguiment manual dels canvis, necessitem un sistema d'esdeveniments que permeti a les vistes canviar automàticament quan hi ha canvis al model. Les vistes han de rebre notificacions/esdeveniments dels models per tal de que elles mateixes siguin les responsables de redibuixar-se.
  • Decoupled modules that expose small external surfaces: En comptes de fer coses globalment, hem de mirar de crear petits subsistemes que no siguin interdependents. Les dependencies fan que el codi sigui complicar de testejar. Small external surfaces fan que la refactorizació interna sigui més senzilla ja que la majoria de coses es poden canviar sempre i quan la interfície externa es mantingui igual.
  • Minimizing DOM dependent-code: Qualsevol codi que depengui del DOM necessitat ser testejat per a cada navegador (cross-browser compatibility). TODO: By writing code in a way that isolates those nasty parts, a much more limited surface area needs to be tested for cross-browser compatibility. Cross-browser incompatibilities are a lot more manageable this way. Incompatibilities are in the DOM implementations, not in the Javascript implementations, so it makes sense to minimize and isolate DOM -dependent code.

Arquitectura. Controllers Must Die

Controllers MUST DIE:

Si us fixeu al diagrama superior no s'utilitza la paraula Controller i això és degut a que simplement és un placeholder (un nom sense contingut) provinent del desenvolupament server-side i el desenvolupament amb MVC i tot que la majoria de frameworks encara utilitzant el terme el seu significat en aplicacions SPA va molt més enllà del seu significat en server-side.

Amb server side la paraula controller simplement implica:

put glue code here

és a dir que el controlador és un codi que adjunta o comunica el codi de vista amb el codi de model.

Les aplicacions de client-side i/o aplicacions SPA són més complexes i necessiten d'una definició o paraula diferent ja en client-side tenim molts més canvis de model/estat:

  • hi ha esdeveniment de DOM que provoquen petits canviar a les vistes.
  • hi ha esdeveniments de model quan les dades es modifiquen al model.
  • hi ha canvis a l'estat de l'aplicació que fan que canviïn les vistes (el que anomenem router i que amb server side fem mitjançant navegació per links)views to be swapped
  • hi ha canvis globals d'estat com per exemple quedar-se sense connexió (offline mode) en una aplicació en temps real.
  • hi ha esdeveniments que succeeixin amb retard (delay) al fer peticions AJAX al servidor que realitzen operacions de backend.

Com podeu veure hi ha moltes més coses que necessiten ser "controlades" i es complicat mantenir tot relacionat i connectat (glue). Tenim clar que necessitem un model per a les dades i una vista per a la presentació però la capa de controlador típica en MVC és ara molt més complexa i es dividirà sovint en múltiples (sub)capes. Utilitzar la paraula controlador per fer referència a tantes responsabilitats no segueix el Single Responsability Principle i es d'agrair utilitzar paraules més específiques que determinin que fa el "controlador" o realment els diferents controladors. És a dir la pregunta és: controlador de que?

Parlarem doncs de:

Asset packaging

Asset packaging o de forma més descriptiva packaging code per al navegador, fa referència a com organitzem el codi Javascript i creem un o més fitxers/mòduls. Sovint cada fitxer és un paquet ( al estil de NodeJS però també es poden fer organitzacions per carpetes, l'important és organitzar el codi per paquets/mòduls). Al final els scripts es carreguen al navegaodr mitjançant etiquetes script d'HTML (es poden utilitzar eines com Webpack o Browserify, Gulp/Grunt etc per empaquetar múltiples fitxers en un, hi ha multitud opcions).

Asset packaging no és només millorar el temps de carrega (que de fet pot ser un requeriment opcional) sinó que està més relacionat amb fer la nostra aplicació modular i assegurar-nos que no acaba sent un untestable mess. No hi ha cap altre cosa que influencií tant en si el codi serà testable o no. Comparem les dos formes de fer:

Messy and random (no modules)

  • Cada peça de codi és global per defecte
  • Els noms són globals
  • Fully traversable namespaces (TODO)
  • El ordre en que es carrega el codi és important per què qualsevol cosa pot ser sobreescrita pel que ve després pel fet d'utilitzar variables globals.Implicit dependencies on anything global
  • Fitxers i mòduls no tenen cap relació
  • Només es pot executar el codi al navegador per que les dependències no s'han especificat clarament

Packages and modules (modular)

  • Cada paquet exposa una sola interfície pública (aka API)
  • Els noms són locals al paquet.
  • Els detalls de la implementació no són accessibles des de fora el paquet (Encapsulation)
  • L'ordre de carrega dels paquets no és important
  • Les dependencies és declaren explícitament
  • Un fitxer <-> un mòdul
  • El codi és executable des de la línia de comandes sense necessitat d'un navegador. (facilita els testos)

En Javascript en comportament habitual de tenir un espai de noms global i esperar que tot funcioni correctament és un desastre ja que fa que els Unit Tests siguin molt complicats i per extensió la refactorització.

NOTA: És una bona pràctica "forçar" als desenvolupadors usuaris d'un mòdul a utilitzar bones pràctiques i per tant a no dependrà dels detalls interns d'implementació d'un mòdul sinó només d'un interfície pública o API. Això s'aconsegueix amb una bona encapsulació i amb una definició explícita de les dependencies

Run-time state

Run time state fa referència al estat de l'aplicació durant la seva execució és a dir en com la app es mostra quan s'està executant al navegador. Coses com quines variables conté la app i quina informació i quins passos estan relacionats amb el flux de l'aplicació d'una activitat ("page") a un altre.

Cal destacar tres relacions importants:

  • URL < - > state: Single page applications com les aplicacions de servidor tenen una relació molt important amb les URL. El problema és que les URL es queden sovint petites per a reflectir tots els estats possibles d'una aplicació web SPA rica. Per un altre banda no podem prescindir de les URL ja que són una de les bases de la web i ens permeten accedir a un estat (amb un bookmark) de forma que puguem passar fàcilment d'una activitat a un altre utilitzant les URL. Això fa que sovint una URL permeti accedir directament (bookmark) a una activitat de l'aplicació però molt possiblement sacrificant l'estat de subparts/subcomponents/subativitats de la pàgina. Un exemple: un chat dins d'una aplicació web de correu electrònic es possible que perdi el seu estat al navegar directament a un bookmark.
  • Definition < - > initialization: És important no barrejar els dos conceptes. Els components reutilitzables (Reusable components) s'han de definir sense inicialitzar-los ni activar-los per permetre la reutilització i el testeig. Però un cop feta la separació com realitzem la initialization/instantiation dels components per a diversos estats de l'aplicació? Hi ha tres possibles formes:
  • Have a small function for each module that takes some inputs (e.g. IDs) and instantiates the appropriate views and objects. I like the first one; the second one is mostly seen in apps that have organically grown to a point where things start being entangled; the third one is seen in some frameworks, particularly with regards to the view layer.The reason I like the first one is that I consider state (e.g. instances of objects and variables) to be disgusting and worth isolating in one file (per subsystem - state should be local, not global, but more on that later). Pure data is simple, so are definitions. It is when we have a lot interdependent and/or hard-to-see state that things become complicated; hard to reason about and generally unpleasant.

The other benefit of the first approach is that it doesn't require loading the full application on each page reload. Since each activity is initializable on its own, you can test a single part of the app without loading the full app. Similarly, you have more flexibility in preloading the rest of the app after the initial view is active (vs. at the beginning); this also means that the initial loading time won't increase proportionately to the number of modules your app has.

  • Have a global bootstrap file followed by a router that loads the correct state from among the global states.
  • Wrap everything in sugar that makes instantiation order invisible.
  • HTML elements < - > view objects and HTML events < - > view changes: Finally, there is the question of how much visibility we can gain into the run time state of the framework we are using. I haven't seen frameworks address this explicitly (though of course there are tricks): when I am running my application, how can I tell what's going on by selecting a particular HTML element? And when I look at a particular HTML element, how can I tell what will happen when I click it or perform some other action?

Server side vs client side

view-approaches.png

Conceptes

Requirements

Motivation

Why do we want to write single page apps? The main reason is that they allow us to offer a more-native-app-like experience to the user.

Benefits & downsides

Benefits:

  • Speed, no page reloads
  • Mobile optimization
  • Real-time pushes
  • Notifications
  • Feak'n cool.

Downsides:

  • Some healthy JavaScript talent to maintain
  • SEO issues (but take in account the differences beetwenn web apps and web sites)
  • General deviation from the linked, document driven web we are used to.

Single Page vs Multipage

http://studio.uxpin.com/blog/single-page-vs-multi-page-ui-design-pros-cons/

How it works?

TODO:

SPAs.png

TODO:

Capture.JPG

Resources:

Router

Vegeu també Web routing

Angular

Vegeu Angular i ngRoute

Vue

Vegeu vue-router

Examples

Cordova App with vue-router

See Also

External links