Web&Co

2016

 

Gonzalo Ruiz de Villa

@gruizdevilla

@gft_es

Experiencias de GFT

BBVA Cells

Bankia AAD

Mapfre GAIA

Banco Sabadell

Angular 

Actualmente estamos creando definiendo las nuevas arquitecturas basadas en Angular en tres clientes (tres en España y uno en Brasil) 

GFT & the community

Agenda

  • HTTP 2.0
  • Progressive Web Apps
  • Physical Web
  • Frameworks
    • Polymer
    • Angular
  • Material Design
  • RAIL

HTTP/2

 

a.k.a. h2

Hay que cambiar ya

HTTP 0.9 - 1991

(GET, POST, HEAD)

HTTP 1.0 - 1996

(PUT, DELETE, LINK, UNLINK)

HTTP 1.1  - 1997

(OPTIONS, HOST Header, Keep-Alive)

 

¿Optimizaciones?

 

Minimizar, contatenar, gzip, sprites, vulcanizar, inline, ...

¿O más bien hacks?

HOL Blocking

(Head Of Line Blocking)

un recurso cada vez

 

https://http2.golang.org/gophertiles?latency=30

 

Metadatos:

Repetición de cabeceras

GET /index.html
User-Agent: Mozilla/5.0 ...
Cookie: __atuvc=4%7C49; ...
Referer: http://www.3pi ...


GET /script.js
User-Agent: Mozilla/5.0 ...
Cookie: __atuvc=4%7C49; ...
Referer: http://www.3pi ...

GET /styles.css
User-Agent: Mozilla/5.0 ...
Cookie: __atuvc=4%7C49; ...
Referer: http://www.3pi ...

HPACK

Compresión de cabeceras para http

SPDY - 2012

http/2 - 2015

Técnicas que ya NO aplican:

  • inlining

  • concatenación

  • sprites

  • vulcanización

Técnicas que ya NO gracias a HPACK:

  • SHARDING

  • CDN

Técnicas que ya NO aplican:

  • SHARDING

  • CDN

Aún hay más

que puedes tener en cuenta al construir una aplicación

PUSH

Sigue siendo necesario:

 

  • GZIP/Deflate
  • First Render
  • DNS Lookup
  • Cache-Control

CANIUSE

¿AHORA? SI

¿POR QUÉ? #PERFMATTERS

¿CÓMO?

PROGRESSIVE
WEB APPS

Offline web apps

Y otros intentos:

  • WinJS
  • WebOS
  • ReactNative
  • Chrome Package Apps

Fricción

 

Tradeoff:

  • cambia el deployment y el modelo de uso
  • no funcionan con un enlace

Si no puedes enlazar algo no forma parte de la web

- 25 apps usadas mensualmente por un usuario

 

- 100+ sitios webs visitados por un usuario medio con Chrome sobre Android

In a consumer mobile app, every step you make a user perform before they get value out of your app will cost you 20% of users.

http://blog.gaborcselle.com/2012/10/every-step-costs-you-20-of-users.html

¿que es lo que echamos de menos? Fundamentalmente:

 

  • funcionar offline
  • aparecer en la pantalla de inicio
  • mensajes push

¿Es lo mismo que un bookmark?

 

example.com/

vs

example.com/date/title

Service worker

Manifest start_url debe funcionar siempre

TLS

El sitio debe tener un origen seguro

Engament

Dos visitas de 5+ minutos

Manifiesto

Con las propiedades adecuadas

Las progressive web apps:

 

Son parte de la web real

 

Forman parte de una iniciativa de estandarización

 

Y son muy adecuadas para móviles económicos, típicos de mercados emergentes 

Web Manifest

 

Incluye metadatos para una Progressive Web App, como:

  • iconos
  • descripción
  • colores
  • ...

El manifiesto se referencia en <head>


    <link rel="manifest" href="/manifest.json">

Media querys


    @media (display-mode: standalone) {
       h3:after {content: "¡En modo standalone!"} 
    }

    {
       "name": "Progressive Web App Demo",
       "short_name": "Demo",
       ...
       "start_url": "/simple-demo/?home=true"
    }

Manifest generator

Manifest validator

A considerar:

 

Refresh, atrás y adelante

 

Compartir la UI, copiando un enlace

 

onbeforeinstall Event

 

Deep linking

Instant Loading

App Shell

+

Service Workers

Los Service Workers permiten  eliminar la red de la ecuación

 

(en visitas posteriores)

1s de retraso de la carga de una pagina

11% páginas vistas menos

16% de reducción en satisfacción del cliente

http://www.radware.com/Products/FastView/

Proporcionar una experiencia de carga instantánea y consistente

¿Qué es la App Shell?

HTML:             /shell

                      +

JavaScript:     /app.js

                      +

Estilos:             /styles.css

¿Qué es un Service Worker?

Una buena analogía es la de un controlador aéreo

Web page

Service Workers

Network

Cache

Ciclo de vida de un Service Worker

 

  1. No hay Service Worker
  2. Evento  install
    
    <head>
        <style>
            /* estilos en linea */
        </style>
    </head>

    <body>
        <!-- punto de inserción de la plantilla -->
        <script src="app.js">
    </body>

/shell

Ciclo de vida de un Service Worker

 

  1. No hay Service Worker
  2. Evento  "install"

  3. Evento "activate" (limpieza de cache)

  4. Espera

  5. Evento "fetch"

  6. Volver a 4

  7. Stop

Dos librerías para facilitar la vida

 

  • sw-precache
  • sw-toolbox

 

https://developers.google.com/web/showcase/case-study/service-workers-iowa

Dos librerías para facilitar la vida

 

  • sw-precache (precache de recursos todavía no pedidos)
  • sw-toolbox (para facilitar todo lo demás)

Notificaciones PUSH

 

Alcance y engagement

Las experiencias actuales consiguen aumentar el tráfico un 50%

¿Cómo funciona?

Service Workers

Service Worker

Web server

Service Worker

Web Server

Push Service


  navigator.serviceWorker.ready.then(function(serviceWorkerRegistration) {  
    serviceWorkerRegistration.pushManager.subscribe()  
      .then(function(subscription) {  
        // The subscription was successful  
       
        return sendSubscriptionToServer(subscription);  
      })  
      .catch(function(e) {  
        //Permission denied or an error occurred
      });  
  }); 

Service Worker

Web Server

Push Service



    self.addEventListener('push', function(event) {
        event.waitUntil(
            fetch('/notification.json').then(function(response) {
                return response.json();
            }).then(function(data) {
                return self.registration.showNotification(
                    data.title,
                    {
                        body: data.body,
                        icon: data.iconUrl,
                        tag: data.tag
                    }
                );
            });
        );
    });

Importante

Urgente

"Usa mi aplicación"

Comienza una emisión online

Ha llegado una domiciliación

Los valores caen por debajo de un límite

Foco en ventanas existentes


    clients.matchAll({
        type: 'window'
    })
    .then(function(clients) {
        if (clients.length > 0) {
            clients[0].postMessate({naviegateTo:url})
        } else {
            return clients.openWindow(url);
        }
    });

WEB OF THINGS

Physical Web

Web Bluetooth

Device

       Service

              Characteristic

navigator.bluetooth.requestDevice({
    filters: [{
        services:['battery_service']
    }]
})
navigator.bluetooth.requestDevice({
    filters: [{
        services:['battery_service']
    }]
})
.then(device => {
    log('Connection to ' + device.name);
    return devie.connectGATT();
})
.then(gattServer => gattServer.getPrimaryService('battery_service'))
.then(service => service.getCharacteristic('battery_level'))
.then(characteristic => characteristic.readValue())
.then(buffer => {
    let batteryLevel = buffer.getUint8(0);
    log('> Battery Level is' + batteryLevel + '%');
})
characteristic.startNotifications().then(() => {
    characteristic.addEventListener(
        'characteristicvaluechanged',
        handleNotifications
    );
});
function handleNotifications(event) {
    let buffer = event.target.value;
    //...
}

Notificaciones

bluetoothDevice.request()
    .then(() => bodySensorLocation.read());

Polymer element:

platinum-bluetooth

<platinum-bluetooth-device services-filter='["battery_service"]'>
  <platinum-bluetooth-characteristic
      service='battery_service'
      characteristic='battery_level'>
  </platinum-bluetooth-characteristic>
</platinum-bluetooth-device>

<span>{{bodySensorLocation}}</span>
bluetoothDevice.request()
    .then(() => bodySensorLocation.read());

Polymer element:

platinum-bluetooth

<platinum-bluetooth-device services-filter='["battery_service"]'>
  <platinum-bluetooth-characteristic
      service='battery_service'
      characteristic='battery_level'>
  </platinum-bluetooth-characteristic>
</platinum-bluetooth-device>

<span>{{bodySensorLocation}}</span>

BLE Peripheral Simulator

Se necesita un servicio de descubrimiento sobre la barra de direcciones

1. Descubrir

2. Recuperar y ordenar

https://paradas.bus/p123

 

https://paradas.bus/p123

 

https://paradas.bus/p123

 

https://paradas.bus/p123

El puente entre la web y los dispositivos físicos

Es solo una web

Una web por dispositivo

Sin interrupciones

¿Son códigos QR?

¿SPAM?

Eddystone

UID

TLM

URL

Escenarios

  1. Web Bluetooth
  2. Classic Web
  3. Cloud Passthrough

Frameworks

POLYMER

WEBCOMPONENTS

  1. Custom Elements
  2. Templates
  3. HTML Imports
  4. Shadow DOM

WEBCOMPONENTS


    <my-tabstrip>
      <my-tab>
        Home
      </my-tab>
      <my-tab>
        Services
      </my-tab>
      <my-tab>
        Contact Us
      </my-tab>
    <my-tabstrip>

¿QUÉ ES POLYMER?

 

Una librería "opinionated" para construir nuevos webcomponents

<link rel="import"
      href="bower_components/polymer/polymer.html">

<script>
  // register a new element called proto-element
  Polymer({
    is: "proto-element",
    // add a callback to the element's prototype
    ready: function() {
      this.textContent = "I'm a proto-element. Check out my prototype!"
    }
  });
</script>
<!DOCTYPE html>
<html>
  <head>
    <script src="bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
    <link rel="import" href="proto-element.html">
  </head>
  <body>
    <proto-element></proto-element>
  </body>
</html>

<link rel="import"
      href="bower_components/polymer/polymer.html">

<dom-module id="dom-element">

  <template>
    <p>I'm a DOM element. This is my local DOM!</p>
  </template>

  <script>
    Polymer({
      is: "dom-element"
    });
  </script>

</dom-module>
<link rel="import"
      href="bower_components/polymer/polymer.html">

<dom-module id="picture-frame">

  <template>
    <!-- scoped CSS for this element -->
    <style>
      div {
        display: inline-block;
        background-color: #ccc;
        border-radius: 8px;
        padding: 4px;
      }
    </style>
    <div>
      <!-- any children are rendered here -->
      <content></content>
    </div>
  </template>

  <script>
    Polymer({
      is: "picture-frame",
    });
  </script>

</dom-module>
<!DOCTYPE html>
<html>
  <head>
    <script src="bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
    <link rel="import" href="picture-frame.html">
  </head>
  <body>
    <picture-frame>
      <img src="images/p-logo.svg">
    </picture-frame>
  </body>
</html>
<link rel="import"
      href="bower_components/polymer/polymer.html">

<dom-module id="name-tag">

  <template>
    <!-- bind to the "owner" property -->
    This is <b>{{owner}}</b>'s name-tag element.
  </template>

  <script>
  Polymer({
    is: "name-tag",
    ready: function() {
      // set this element's owner property
      this.owner = "Daniel";
    }
  });
  </script>
  
</dom-module>
<link rel="import"
      href="bower_components/polymer/polymer.html">
<!-- import the iron-input custom element -->
<link rel="import"
      href="bower_components/iron-input/iron-input.html">

<dom-module id="editable-name-tag">

  <template>
    <p>
    This is a <strong>{{owner}}</strong>'s editable-name-tag.
    </p>
    <!-- iron-input exposes a two-way bindable input value -->
    <input is="iron-input" bind-value="{{owner}}"
      placeholder="Your name here...">
  </template>

  <script>
    Polymer({
      is: "editable-name-tag",
      properties: {
        owner: {
          type: String,
          value: "Daniel"
        }
      }
    });
  </script>

</dom-module>

POLYMER CATALOG

 

  • Fe: Iron Elements: Polymer core elements
  • Md: Paper elements: Material Design elements
  • Go: Google Web Components: For Google Api's and services
  • Au: Gold Elements: e-commerce elements
  • Ne: Neon elements: Animation and special effects 
  • Pt: Platinum elements: Offline, Push and more
  • Mo: Molecules: wrappers de librerías terceras

Angular

AngularJS

 

 

 

Angular 2

Web

Mobile Web

Android

iOS

Windows

Mac

Linux

  • pensado para construir aplicaciones del tamaño que sea
  • mobile first
  • rendimiento brutal
  • sencillo y expresivo
  • inmutabilidad
  • inyección de dependencias jerárquica
  • integración con webcomponents
  • i18n
  • server side rendering
  • web workers
  • aplicaciones nativas, de escritorio
  • routers jerárquicos y paralelos
  • PWA
  • Angular CLI

    import {Component, bootstrap} from 'angular2/angular2';
    import {TodoList} from './todoList.ts';
    @Component({
      selector: 'app',
      directives: [TodoList],
      template: `
        <h1>Todo App</h1>
        <todo-list></todo-list>
      `
    })
    class AppComponent {
      constructor() {
      }
    }
    bootstrap(AppComponent);

    import {Component, CORE_DIRECTIVES} from 'angular2/angular2';
    import {TodoItem} from './todoItem.ts';
    @Component({
      selector: 'todo-list',
      template: `
        <ul>
          <todo-item
            *ng-for="#todo of todos"
            [todo]="todo"></todo-item>
        </ul>
      `,
      directives: [CORE_DIRECTIVES, TodoItem]
    })
    export class TodoList {
      public todos: Array<any>;
      constructor() {
        this.todos = TODOS;
      }
    }
    var TODOS = [
      {
        content: 'Discuss new feature',
        isCompleted: false
      },
      {
        content: 'Fix issue',
        isCompleted: false
      }
    ]

    import {Component} from 'angular2/angular2';
    @Component({
      selector: 'todo-item',
      properties: ['todo'],
      template: `
        <li class="todo-item">
          {{todo.content}}
        </li>
      `
    })
    export class TodoItem {
    }
    export class TodoService {
      private _todos;
      constructor() {
        this._todos = todos;
      }
      getTodos() {
        return this._todos;
      }
      completed(todo) {
        todo.isCompleted = todo.isCompleted ? false : true;
      }
      delete(todo) {
        var index = this._todos.indexOf(todo);
        this._todos.splice(index, 1);
      }
      add(content: string) {
        var todo = {
          content: content,
          createdAt: new Date(),
          isCompleted: false
        }
        this._todos.push(todo);
      }
    }
    var todos = [
      {
        content: 'Write Angular 2 Blog',
        createdAt: new Date(),
        isCompleted: false
      },...
    ]
 
   ...
    import {TodoService} from './todoService.ts';
     
    ...
    export class TodoList {
      constructor(private todoService: TodoService) {
     
      }
    }
     
 
import {Component, CORE_DIRECTIVES} from 'angular2/angular2';
import {TodoService} from './todoService.ts';
@Component({
  selector: 'todo-item',
  properties: ['todo'],
  directives: [CORE_DIRECTIVES],
  styles: [
    `
    .todo-item.completed{
      text-decoration: line-through;
    }
    `
  ],
  template: `
  <li [ng-class]="getTodoItemClass(todo)">
    {{todo.content}}
    -
    <a (click)="todoService.completed(todo)">Completed</a>
    -
    <a (click)="todoService.delete(todo)">Delete</a>
  </li>
  `
})
export class TodoItem {
  constructor(private todoService: TodoService) {
  }
  getTodoItemClass(todo) {
    return {
      completed: todo.isCompleted === true,
      "todo-item": true
    }
  }
}
     
 

@Component({
  selector: 'modal',
 ...
  template: `
  <div>
    <h1 #inicio tabindex="-1">Título modal</h1>

    ...
    <div (focus)="inicio.focus()" tabindex="0"></div>
  </div>
  `
})
export class Modal {
  //...
}
     

Ventana modal accesible con control de foco de teclado

  • Native UX - no compromises
  • Native Performance
  • Native UI components
  • 100% Access to Native Platform API

Material Design

 

 

Material Design by Google

Un mundo 3D

El entorno material tiene en cuenta el 3D, con lo que tenemos que considerar el eje Z perpendicular a la superficie


Movimientos auténticos

Al percibir la naturaleza tangible de un objeto ayuda a entender como manipularlo. El movimiento de un objeto transmite esta naturaleza: es ligero o pesado, flexible o rígido, grande o pequeño.

 

El movimiento no es solo algo bonito, proporciona información de relaciones espaciales, funcionalidad e intencionalidad del sistema.

Interacciones responsivas

Interacciones responsivas

Acción radial

Transiciones significativas

Jerarquía temporal

Detalles y más detalles

EJEMPLOS

Compromisos en web

Cada vez menos

RAIL

Delay User reaction
0-100ms Instantáneo
100-300ms Retraso vagamente perceptible
300ms-1s Retraso perceptible, mantiene foco
1s+ Cambio de contexto mental
10s+ Ya vol

60 fps

Reponse

Animation

Idle

Load

Response

100ms percepción de respuesta instantánea

Animación

16 ms para realizar el trabajo antes de cada frame

8~10 ms en la práctica

Idle

50ms para responder a la interacción de un usuario

Load

1s para que el usuario no se despiste

Response Animation Idle Load
Tap to paint
menos de 100ms
Cada frame se pinta en menos de 16ms Usar el tiempo ocioso para programar trabajo de forma proactiva Satisfacer los objetivos de "Response" en la carga
Drag to paint en menos de 16ms Cada bloque de trabajo en 50ms Conseguir pintar algo con sentido en menos de 1000ms

RAIL pone al usuario en el centro

¡GRACIAS!

Web&Co @ GFT

By Gonzalo Ruiz de Villa

Web&Co @ GFT

  • 1,750
Loading comments...

More from Gonzalo Ruiz de Villa