Link Cerca Menu Expand Document

Disseny i implementació

Resultats d’aprenentatge:

  1. Genera interfícies gràfiques d’usuari mitjançant editors visuals fent servir les funcionalitats de l’editor i adaptant el codi generat.
  2. Genera interfícies gràfiques d’usuari basades en XML fent servir eines específiques i adaptant el document XML generat.
  3. Crea components visuals valorant i emprant eines específiques.

Aplicacions web

Una aplicació web és un programari que implica un client i un servidor que es comuniquen utilitzant la xarxa i el seu protocol HTTP.

  • El client és un navegador web, que sap com renderitzar documents basats en els estàndards web HTML (estructura), CSS (estil) i Javascript (lògica).
  • El servidor permet rebre peticions HTTP de diferents tipus (GET, POST, PUT, DELETE…) per a retornar, crear, modificar o esborrar continguts.

Hi ha diferents models d’aplicació web que es distingeixen en funció de qui, com i quan genera els documents o continguts que renderitzarà el navegador per a l’usuari.

Història

Inicialment, els continguts eren només estàtics. Hi ha servidors web que serveixen HTML i CSS amb HTTP GET. L’adreça del navegador és un HTTP GET cap el servidor, que serveix continguts estàtics. Aquests continguts els ha creat prèviament un dissenyador utilitzant eines de disseny.

L’aparició de PHP revoluciona la web. Es tracta d’un llenguatge que permet rebre peticions HTTP POST, accedir a bases de dades i generar documents HTML de forma dinàmica. Per tant, els documents els genera el servidor dinàmicament en funció de les peticions del navegador utilitzant motors de plantilles. Se’n diuen Multi-Page Applications (MPA), i van apareixen nous frameworks (Servlets, ASP.NET, Laravel, Django, Spring).

També el navegador evoluciona, amb Javascript, que permet manipular el DOM i fer interaccions dinàmiques, així com Ajax, que permet fer peticions al servidor sense recarregar la pàgina. Els navegadors són cada cop més compatibles, i es desenvolupa un motor Javascript en el servidor, Node.js. Podem comunicar-nos amb el servidor amb altres tipus de continguts, com per exemple JSON. També tenim JQuery, una llibreria que facilita la manipulació del DOM.

És el preàmbul de les Single-Page Applications (SPA).

Single-Page Application

Una SPA encapsula la majoria de la lògica de l’aplicació al client mitjançant Javascript, i sap com generar els documents HTML i CSS. El navegador obté aquesta lògica mitjançant una petició de documents estàtics (HTML amb Javascript), i esdevé una aplicació autònoma. De fet, intercepta el routing de la URL a l’adreça del navegador, que no produeix necessàriament comunicació amb el servidor.

Només accedirà a un servidor si necessita interactuar amb altres recursos externs dins d’una arquitectura centralitzada que comparteix amb altres usuaris o aplicacions D’aquesta arquitectura se’n diu full-stack web, i té dues aplicacions: la del client (front-end) i la del servidor (back-end), comunicades amb un protocol sobre HTTP i un format de dades, com JSON.

Tenim diverses solucions al client i al servidor, que poden ser intercanviables si es respecta el protocol:

  • Al client hi ha diverses llibreries o frameworks, com Angular, React o Vue.
  • Al servidor es poden utilitzar frameworks MPA o bé de nous, com Node.js (Express.js).

Javascript i APIs

Per a poder desenvolupar al front-end, cal tenir un coneixement de Javascript avançat basat en l’estàndard ECMAScript ES6 (2015) i que s’utilitza al front-end i a l’entorn de desenvolupament associat.

Veure la pàgina de Javascript modern per a entendre les funcionalitats base del llenguatge.

Al marge del llenguatge, tenim una sèrie d’APIs que permeten interactuar amb el navegador anomenades Web API:

  • Manipulació DOM (DOM API), que inclou, entre d’altres:
    • Accés i control d’elements HTML
    • Manipulació de dades de formularis
    • Accés a la història del navegador
    • Scripts en background (Web Workers API)
    • Websockets i Server-sent events
  • Obtenció de dades d’un servidor (Fetch API)
  • Manipulació de gràfics (Canvas i WebGL) i interacció amb àudio i vídeo
  • APIs del dispositiu, per exemple, geolocalització
  • Emmagatzematge de dades al dispositiu (Web Storage API)

Cal dir que Node.js, un runtime Javascript standalone, és principalment compatible amb l’especificació ES6 (veure node.green), però no amb les APIs del navegador.

Esdeveniments

Podem associar un esdeveniment del DOM a una crida d’una funció amb element.addEventListener(event, fn). Podem esborrar el listener amb element.removeEventListener(event, fn).

La funció listener pot tenir un paràmetre, l’esdeveniment. Aquest objecte té la propietat target, que fa referència a l’objecte que origina l’esdeveniment, i type, el tipus d’esdeveniment, entre d’altres.

Manipulació DOM

Alguns mètodes importants que permeten manipular el DOM:

  • document.querySelector(selector), document.querySelectorAll(selector). Versions clàssiques: getElementById(id), getElementsByTagName(name)
  • document.createElement(tag), element.appendChild(child), element.removeChild(child), element.remove().

Fetch API

El mètode fetch(resource, options) és la forma estàndard d’obtenir dades de forma asíncrona. Retorna una promesa.

Paradigma declaratiu

En la programació imperativa, el canvi està associat al flux d’execució i l’operador assignació. És una definició operacional.

La programació declarativa es basa en la definició conceptual, es defineixen relacions atemporals.

La programació reactiva es basa en el paradigma declaratiu: les nostres sortides es descriuen declarativament en funció de les nostres entrades. Això les permetrà reaccionar als canvis que es produiran a les entrades al llarg del temps.

Si utilitzem programació basada en esdeveniments, podem associar un listener als canvis (esdeveniments) produïts a les entrades. Llavors, es poden modificar les sortides. Reactivament, canviem el concepte per una relació “el seu valor depèn de” de les sortides cap a les entrades. Conceptualment, podem imaginar que una sortida és listener de totes les entrades de què depèn, i que actualitza el seu valor cada cop que es crida.

El funcionament reactiu executa un programa es produeix en dues fases:

  1. Inicialització: construir un graf dirigit de dependències
  2. Execució: els valors entren i produeixen canvis a les sortides És similar a com funciona una GUI: primer es construeixen els widgets i després el bucle d’esdeveniment els processa.

Gestió de l’estat

La UI (vista) és el resultat de dibuixar una representació de les dades. La gestió de l’estat inclou com representar aquestes dades i gestionar els seus canvis. La relació entre UI i estat és:

UI = fn(estat)

Per explicar com es produeixen els canvis que un usuari produeix sobre l’estat, cal introduir el concepte d’acció:

En aquest esquema es diu que l’estat és un observable, i que l’UI és un observador dels canvis que es produeixen en l’estat. Els observadors es subscriuen als observables, i seran notificats per ells quan hi hagi canvis.

Per tant, l’UI no canvia directament l’estat, li cal passar un missatge (acció) que encapsula el canvi d’estat. Un canvi d’estat fa que tots els seus observadors siguin notificats. L’UI és el principal subscriptor, i quan se li notifica d’un canvi d’estat (observables) torna a renderitzar-se per a reflectir-ho.

Efectes secundaris

Algunes operacions no tenen un efecte directe sobre la UI. Per exemple: obtenir dades d’un servidor, guardar-les, executar alguna tasca periòdica, etc. Aquestes són operacions externes no associades a l’UI, i les diem efectes secundaris o side effects.

Web reactiva

Les interfícies (o sortides) reaccionen als canvis que es produeixen a les seves dependències (o entrades). Són observadors. El paradigma reactiu ens permet oblidar-nos de com i quan es tornarà a redibuixar la UI. D’això s’encarrega la llibreria, que detecta els canvis a realitzar al DOM i els aplica.

La relació entre entrada i sortida és declarativa, i s’implementa de diferents formes segons la llibreria o framework. Habitualment tenim un cert estat (entrades) i un markup (sortides) que el referencia.

Les operacions bàsiques essencials d’un framework reactiu són:

  • Definició d’estats (observables).
  • Definició d’UI (observador) a partir dels estats (observables).
  • Definició de side-effect handlers (observador).
  • Modificació d’un estat a partir d’un esdeveniment UI o un side-effect.
  • Creació d’un observable computat a partir d’altres observables (optimització).

Toolchain

L’entorn de desenvolupament dels frameworks Javascript requereix d’un conjunt d’eines (toolchain) per a realitzar diferents funcions:

  • Gestor de paquets: permeten instal.lar i utilitzar paquets de tercers tant en temps de desenvolupament com en temps d’execució. Exemples: npm, yarn.
  • Bundler: permet desenvolupar modularment el codi i després optimitzar-lo per a carregar ràpidament en temps d’execució. Exemples: webpack, parcel, vite.
  • Compiladors: permeten transformar el codi font (transpile/polyfill) des de Javascript modern (cada cop menys necessari), Typescript o altres DSL (syntax sugar) a Javascript que pot executar-se als navegadors. Exemples: babel.
  • Linters: analitzador estàtic de codi que permet detectar errors. Exemple: eslint.
  • Prettiers: formatadors de codi. Exemple: prettier.

Totes aquestes eines són integrables des de diferents editors de codi, com per exemple VSCode o Webstorm.

Fonaments React

React és un framework basat en la programació declarativa de components. Cada component declara com s’ha de renderitzar en funció de les propietats d’entrada i el seu estat:

(props, state) => view

La vista només es torna a renderitzar si canvien les propietats o l’estat.

Els components pare es comuniquen amb els fills passant propietats, i els fills poden generar esdeveniments cap als pares mitjançant callbacks.

El procés de renderitzar components es realitza sobre un DOM virtual, detectant quins canvis s’han produït a l’arbre de nodes respecte el DOM anterior. Només els canvis s’enviaran al DAM real, en un procés anomenat reconciliació.

Hi ha dues formes d’escriure de components:

  • basats en classes (tradicional): s’utilitzen classes Javascript que estenen React.Component amb mètodes del cicle de vida. Cal, almenys, definir el mètode render(), una funció pura que retorna el contingut a renderitzar.
  • basats en funcions (modern): són funcions Javascript amb un paràmetre, les propietats, que retornen el contingut a renderitzar. Utilitzen el concepte de hook, unes crides al framework que permeten gestionar l’estat o els efectes secundaris, per exemple.

Els components responen a tres esdeveniments:

  1. Muntatge (mount): quan el component es crea i s’afegeix al DOM.
  2. Actualització (update): quan el component s’actualitza en modificar-se una propietat o estat.
  3. Desmuntatge (unmount): quan el component s’esborrar del DOM.

El flux de React és el següent:

  • Fase de renderitzat
    • Es construeixen els components a muntar (1)
    • Es renderitzen a VDOM els nous components i aquells que calgui actualitzar (1, 2)
  • Fase de commit
    • S’actualitza el DOM (real)
    • El navegador pinta el DOM actualitzat
    • Els components actualitzats i desmuntats (2, 3) fan el cleanup d’efectes
    • Els components nous i actualitzats criden els efectes (1, 2)

JSX i components

React utilitza una sintaxi estesa de JavaScript anomenada JSX. Un compilador del toolchain (Babel) s’encarrega de convertir-ho a JavaScript. Està basada en HTML, però permet codi JavaScript dins de claus {}.

Permet definir elements React, que són objectes creats amb React.createElement(). Els elements poden ser de tipus HTML o bé definits pel desenvolupador.

Conceptualment, els components són funcions de JavaScript. Accepten entrades arbitràries (anomenades props) i retornen elements de React que descriuen el que hauria d’aparèixer a la pantalla. Aquestes propietats són de només lectura, i poden incloure callbacks per a gestionar esdeveniments.

Flux de dades

Les dades de React només viatgen des del pare cap els fills (one way data flow), i ho fan mitjançant les props. Les props són un objecte immutable, mentre els estats són mutables. Però com les props són immutables, els canvis de l’estat als fills no afecten els pares.

function Child({propName}) {
  return (<h1>Hello {propName}!</h1>);
}
function Parent() {
  return (<Child propName={"React"} />);
}

Com que un fill no pot modificar l’estat d’un pare el que fem és aixecar l’estat (lifting state up). Això permet compartir-lo i accedir a aquells components que el necessiten.

El flux invers és possible utilitzant esdeveniments enviats des dels fills als pares.

function Child({ message, callBack }) {
  return (
    <button onClick={() => callBack("message from child!")}
    >{message}</button>);
}
function Parent() {
  const [message, setMessage] = useState('no message');
  return (
    <Child message={message}
      callBack={propValue => setMessage(propValue)} />);
}

Components amb funcions

React modern utilitza els components basats en funcions. Un component és una funció amb props d’entrada i el contingut a mostrar de sortida, i utilitza els hooks per a definir el seu comportament.

Els hooks permeten implementar les operacions de React:

  • Definició d’estats amb useState.
  • Definició d’UI a partir dels estats amb el JSX retornat.
  • Definició de side-effect handlers amb useEffect.
  • Modificació d’un estat amb setState.
  • Creació d’un observable computat a partir d’altres observables amb useMemo.

Disseny React

Imaginem que tenim una maqueta de la interfície i l’API JSON que utilitzarem per a comunicar-nos. Els passos habituals per a dissenyar una aplicació React serien:

  1. Descompondre la interfície en una jerarquia de components.
  2. Construir una versió estàtica en React.
  3. Trobar la mínima representació possible de l’estat.
  4. Identificar on ha de situar-se l’estat dins de la jerarquia.
  5. Afegir flux invers de dades.

Descomposició

Identifica les responsabilitats dins del disseny i separa-les en components. De vegades té sentit estructurar-ho en funció de l’estructura de les dades JSON.

Versió estàtica

Començar creant una versió no interactiva. Cal decidir quines props passen de dalt cap a baix de la jerarquia, però sense utilitzar estat. Es diu flux d’un sol sentit.

Estat mínim i suficient

La interfície d’usuari es fa interactiva gràcies a la possibilitat de modificar les dades del nostre model. Ho podem fer utilitzant estat: el mínim conjunt de dades que la nostra aplicació ha de recordar.

Identifica les dades que són estat:

  • canvien al llarg del temps,
  • no arriben per les props,
  • no són computables a partir d’un altre estat o les props.

Localització de l’estat

Cal identificar quin component modifica l’estat i per tant li pertany. Es pot fer en tres passos:

  1. Identifica els components que utilitzen l’estat
  2. Trobar el pare comú més proper de tots dins la jerarquia
  3. Decideix on ha de localitzar-se. Habitualment és al pare comú, tot i que podria ser qualsevol ancestre d’aquest pare.

Flux invers de dades

Si volem que un component fill pugui modificar l’estat d’un component pare, cal que el pare passi un callback mitjançant les props que el fill pugui cridar per a realitzar el flux invers de dades.

Referències