Durante el pasaje a producción de una single-page application (SPA) es muy importante el proceso de bundling, que consiste en combinar los archivos y módulos de la aplicación para su puesta en marcha.

En el panel de Mango utilizamos Browserify para estructurar el Front-end.
Browserify nos proporciona muchas ventajas y utilidades como contamos en este post.
El proceso de bundling más simple utilizando browserify resulta en un único archivo de JavaScript que contiene toda la lógica de la aplicación.

El problema del bundle único

A medida que se agregan nuevas features el bundle resultante incrementa en peso acarreando dos inconvenientes:


– El tiempo de descarga es mayor.
– El tiempo de interpretado del código es mayor.

El panel, como la mayoría de las SPA, necesita descargar, parsear e interpretar el código antes de empezar a dibujar. Esto se traduce en tiempos más largos de espera antes de poder utilizar la aplicación, afectando a la experiencia del usuario.

El primer problema, si bien es real, no se manifiesta de modo lineal a medida que crece la cantidad de código resultante. Como apunta TJ VanToll la compresión gzip es muy fuerte y funciona mejor para archivos más grandes (mientras más grande el archivo hay más repeticiones por encontrar).

El inconveniente en materia de performance más grande está relacionado con el tiempo de parsing e interpretado del código inicial.

Filament group hizo un análisis interesante sobre la penalización en la utilización de ciertos frameworks MVC populares en materia de carga inicial que da un buen panorama del problema.

factor-bundle al rescate

factor-bundle funciona como un plugin de browserify que recibe múltiples archivos como puntos de entrada (en nuestro caso están mapeados a las secciones de nuestra aplicación), analiza sus contenidos y genera por un lado un archivo de salida por cada archivo de entrada sumado a un archivo extra que contiene las dependencias en común entre todos ellos.

Gracias a esta ‘factorización’ de dependencias, cuando un usuario entra a la app solo descarga el JavaScript asociado a la sección que visita, sumado a las dependencias compartidas. El resto de las secciones se descargan de modo asincrónico y sin repetir la descarga de dependencias, mientras el usuario navega por el panel.

Una pequeña optimización que hacemos es producir, por cada archivo de salida, otro que surge de concatenarle a este el archivo de dependencias comunes. Esto previene la descarga de un script adicional (-common-.js).

El output luego del procesado se visualiza de este modo:

.
|-- config-0.3.6.js
|-- config-common-0.3.6.js
|-- login-0.3.6.js
|-- login-common-0.3.6.js
|-- store-single-0.3.6.js
|-- store-single-common-0.3.6.js
|-- stores-0.3.6.js
|-- stores-common-0.3.6.js
|-- user-0.3.6.js
`-- user-common-0.3.6.js

Existen optimizaciones para aplazar la carga de módulos no usados. Por ejemplo detectar el acercamiento del mouse a links de otras secciones. Decidimos dejar esas optimizaciones para más adelante (y para otros posts).

Conclusiones

La técnica de separación de bundles resultó en una mejora de aproximadamente un 30% de ahorro en el tiempo de espera promedio de los usuarios a la hora de ingresar a nuestro Panel de administración, dado por la baja en el peso del script inicial como la menor cantidad de código a interpetar. Además, el agregado de nuevas secciones a la aplicación no resulta en una reducción de la performance en la carga inicial del sistema.

Hay otras técnicas para mejorar la carga inicial de SPAs. Una de las que estamos investigando para integrar es la implementación de rendering compartido entre el cliente y el servidor. Una ventaja interesante de la factorización de bundles es que es un cambio casi exclusivamente del proceso de compilación de la aplicación. La lógica de negocio permaneció intacta.

Cualquier duda/comentario o experiencias que quieran compartir, me pueden contactar vía Twitter a @dzajdband.

6to5-2

Next Saturday night, we’re sending you back to the future!

– Dr. Emmett L. Brown, Back to the Future (1985)

Like the nerds we are, we constantly check new technologies and the evolution of the ones we’re already interested in. That’s why the discussion of the implementation of different aspects of the EcmaScript standard keeps us interested and gives us the motivation to experiment new things.

Backwards compatibility is always important on the client-side JavaScript development process. That’s one reason why Can I use is one of the tools we use almost everyday. Looking forward is always exciting.
The problem with this is knowing which tools are going to be really useful from the ones that are just a fad.

After a JSConfAR filled with talks about ES6, I decided to investigate the possibility of adding a ES6 to ES5 transpiler and its potential benefits. Our aim included that Mango’s Dashboard browser compatibility included IE8+ and modern browsers.

6to5

The search was successful and led me to 6to5. The slogan says it all: «Next generation JavaScript, today». The idea is simple and powerful, a lot of the features of the new standards can be converted with the help of transformers, to a ES5 compatible syntax, making possible to run your code in older versions of Internet Explorer, for example. The 6to5 website has a REPL that allows you to experiment and easily become a fan of it.

The implementation of 6to5 to our build was done literally with 2 lines. The 6to5 also maintains 6to5ify, a Browserify plugin (something we talked about in a previous post), that analyses the ES6 compatible code and turns it into ES5. Something important to point out is that the implementation of 6to5 didn’t involve modifying not even one line of code from our product. The code doesn’t need to be modified at all.

Using the Browserify CLI, you can run 6to5ify in the following way:

$ browserify script.js -t 6to5ify --outfile bundle.js

In order to support some features on the browser, you should include polyfills already present in 6to5. In order to do that in our development environment, we only needed to add this: `require(‘6to5/polyfill’)`.

Better than ever

During the first week of using 6to5 we used a lot of ES6 features in our code daily.

Without going into detail, here are some of the ones we’re using right now:

Arrow functions

This construction is fundamental for a language and a platform (the web) that depends so much on callbacks. The arrow functions are not only a shortcut to define functions, but also have a very useful feature such as lexical binding. Thanks to arrow functions we can almost forget about the classic `self = this`. Look to the following example, of the first commit that uses ES6 in our code:

Before:

Tabs.prototype.show = function() {
  var self = this;
  // ...
  this.$triggers.on('click', function(eve) {
    self.select(target.getAttribute('data-tabs-trigger'));
    });
  }

After:

  Tabs.prototype.show = function() {
    // ...
    this.$triggers.on('click', (eve) => this.select(target.getAttribute('data-tabs-trigger'));
  }

 

Classes

In our modules, it’s very common to find the following pattern:

function Input(options) { // code }
inherit(Input, Emitter); // Input prototype inherits from Emitter
Input.prototype.val = function() {} // Extend prototype

The classes syntax in ES6 manifest this patter much more clearly:

class Input extends Emitter {
  constructor() { //code }
  val() { //... }
}

 

let + const

As its said `let` is the new `var`, but with Block scoping. It’s interesting that in exception of the cases where the block scope became relevant the transpiler converts `let` statements into var’s.

let a = 3; // written code
var a = 3; // transpiled code (in most contexts)

// written code
let x = 2;
{
  let x = 3; // new var different than above
}

// Transpiled code
var x = 2;
{
  var _x = 3;
}

Regarding adding constants using the keyword `const`, I remember a colleague that worked with Java who used to ask me if I didn’t like to declare constants or if there was a problem with the language design. This new keyword implies for 6to5 making checks at compilation time. If the static code analysis detects a double assign, it simply stops the compilation process and prompts the error. One of the uses that we’re assigning to `const` besides declaring new constants, is using it for the `require`. We don’t want to modify the objects that export other modules directly. Using `const`, we make sure we didn’t make a mistake.

const Input = require('input');

 

Parameter destructuring

Another pattern in our code is a constructor that receives a series of options via a parameter that is an object in the following way:

function Form(options) {
  options = options || {};
  this.size = options.size;
  this.legend = options.legend;
}

using ES6 we could write:

function Form({size, legend}={}) {
  this.size = size;
  this.legend = legend;
}

 

Template Strings

Anyone who hasn’t created a URL in the following way, throw the first stone.

function fetchData(id) {
  request('/api/controller/' + id + '/action/');
}

Template strings make this more friendly:

// Written code
function fetchData(id) {
  request('/api/controller/${id}/action/');
}

// Transpiled code
function fetchData(id) {
  request('/api/controller/' + id + '/action/');
}

 

Conclusions

The use of the features above and some others, helps us to be more productive and also to write more friendly code and less error-prone. Making the decision to code this way is not just about the comfort of the developers, but also impacts on productivity for projects where there’s need to maintain code already in production.

We hope to continue discovering interesting paths using ES6 and letting you know about how it can help us to enhance the quality of the web development.

 

ES7: Looking beyond the horizon

Once ES6 was completed, the discussion about ES7 started. Just like 6to5 allows us to use ES6 features before it’s available in some environments, the same happens with ES7.

That’s why we are in conditions to make use of such keywords as `async` and `await`that allows async programming a lot like sync, or event use generator comprehensions. The latter is available as an `experimental feature` for 6to5.

More resources:

ES6 Rocks
Understanding ES6
Axel Rauschmayer Blog
6to5 Tour
ES6 Overview

6to5-2

«El futuro llegó hace rato todo un palo, ya lo ves.»

Carlos Solari – Todo un palo

Como buenos nerds estamos constantemente detrás del desarrollo de nuevas tecnologías y de la evolución de aquellas que nos interesan. Es por eso que la discusión e implementación de aspectos del estándar de EcmaScript nos encuentra siempre atentos y con ganas de experimentar.

Por lo general en la vida del JavaScript en el cliente se suele mirar todo el tiempo la compatibilidad para atras. Así es como Can I use es frecuentado diariamente por nosotros. Mirar para adelante es motivo de exaltación.
El problema principal está en separar las nuevas herramientas que resultan útiles de aquellas que se nos aparecen simplemente por una cuestión de moda o marketing.

Luego de una JSConfAR plagada de charlas sobre ES6 decidí investigar la viabilidad técnica y los potenciales beneficios de agregar un transpiler de ES6 a ES5 (el compromiso de compatibilidad del panel de administración de Mango es de IE8+ y navegadores modernos).

6to5

La búsqueda fue fructífera y me llevó a 6to5. El slogan es claro: «Next generation JavaScript, today». La idea es simple y poderosa, muchas de las características de los nuevos estándares pueden convertirse con la ayuda de transformaciones a una sintaxis compatible con ES5, posibilitando correr el código en por ejemplo versiones antiguas de Internet Explorer. El sitio de 6to5 cuenta con un REPL que permite jugar y fanatizarse rápidamente.

La inclusión de 6to5 a nuestro build system fue literalmente de 2 lineas. El equipo de 6to5 también mantiene 6to5ify, un plugin de Browserify, del cual hablamos en un post anterior, que analiza el código ES6 compatible y lo convierte a ES5. Un punto a destacar es que la inclusión de 6to5 no involucró la modificación de ni una linea de código del producto. El código escrito no necesita ser modificado (no hay excusas).

Usando el CLI de Browserify, se puede correr 6to5ify del siguiente modo:

$ browserify script.js -t 6to5ify --outfile bundle.js

Para soportar algunas características en el navegador se necesitan incluir polyfills que vienen incluidos en 6to5. La inclusión de los polyfills estuvo a un `require(‘6to5/polyfill’)` de nuestros ambiente de desarrollo.

Ya lo estamos usando y está más lindo que nunca

Durante la primera semana de uso de 6to5 en el día a día usamos muchas features de ES6 en nuestro código.

Sin explicar sus funciones detalladamente, paso a detallar el uso que le estamos dando a algunas de ellas:

Arrow functions

Esta construcción es fundamental para un lenguaje y una plataforma (la web) que delega tanto peso en los callbacks. Las arrow functions no son solo un shortcut para definir funciones sino que tienen una característica super útil como es el lexical binding. Gracias a las arrow functions nos olvidamos casi por completo del clásico `self = this`. Este ejemplo es del primer commit que usa ES6 en nuestro código.

Antes:

Tabs.prototype.show = function() {
  var self = this;
  // ...
  this.$triggers.on('click', function(eve) {
    self.select(target.getAttribute('data-tabs-trigger'));
    });
  }

Después:

  Tabs.prototype.show = function() {
    // ...
    this.$triggers.on('click', (eve) => this.select(target.getAttribute('data-tabs-trigger'));
  }

 

Classes

En nuestros módulos es muy común encontrar el siguiente patrón

function Input(options) { // code }
inherit(Input, Emitter); // Input prototype inherits from Emitter
Input.prototype.val = function() {} // Extend prototype

La sintaxis de clases de ES6 manifiesta este patrón de un modo claro

class Input extends Emitter {
  constructor() { //code }
  val() { //... }
}

 

let + const

Como se suele decir `let` es el nuevo `var`, pero con Block scoping. Es interesante que salvo los casos donde el scope de bloque cobra valor, el transpiler pasa las declaraciones de `let` directamente a var.

let a = 3; // written code
var a = 3; // transpiled code (in most contexts)

// written code
let x = 2;
{
  let x = 3; // new var different than above
}

// Transpiled code
var x = 2;
{
  var _x = 3;
}

Respecto al agregado de constantes mediante la keyword `const` recuerdo a un compañero de trabajo que venía de Java y me preguntaba si no me gustaba declarar constantes o si era una falla de diseño del lenguaje. Esta nueva keyword implica para 6to5 chequeos en tiempo de compilación. Si el análisis de código estático detecta una doble asignación, simplemente frena la compilación y nos advierte del error. Un uso que le estamos dando a `const` más allá del de declarar nuevas constantes es usarlo para los `require`. No queremos modificar los objetos que exportan otros módulos directamente. Usando `const` nos aseguramos de no equivocarnos.

const Input = require('input');

 

Parameter destructuring

Otro patrón común en nuestro código es un constructor que recibe una serie de opciones mediante un parámetro que es un objeto del siguiente modo:

function Form(options) {
  options = options || {};
  this.size = options.size;
  this.legend = options.legend;
}

usando ES6 podemos escribir:

function Form({size, legend}={}) {
  this.size = size;
  this.legend = legend;
}

 

Template Strings

El que nunca armó una url de esta manera que tire la primer piedra:

function fetchData(id) {
  request('/api/controller/' + id + '/action/');
}

Los template strings lo hacen amigable:

// Written code
function fetchData(id) {
  request('/api/controller/${id}/action/');
}

// Transpiled code
function fetchData(id) {
  request('/api/controller/' + id + '/action/');
}

 

Conclusiones

El uso de las features detalladas y algunas otras nos ayudan a ser más productivos y a la vez escribir código más entendible y menos propenso a errores. Decisiones de este tipo no solo hacen a la comodidad del programador sino que se traducen en mejoras de productividad para proyectos y empresas tanto a la hora de generar nuevas oportunidades como de mantener código previamente puesto en producción.

Esperamos seguir descubriendo caminos interesantes usando ES6 y contar como nos puede ayudar a mejorar la calidad del desarrollo web.

 

ES7: Mirando un poco más allá del horizonte

Una vez completado ES6 se empezó a debatir ES7. Así como 6to5 nos permite usar funcionalidades de ES6 antes de estar disponible en ciertos ambientes, lo mismo ocurre con ES7.

Es por eso que estamos en condiciones de hacer uso de las keywords async/await que hacen sentir la programación asíncrona como si estuviéramos escribiendo código sync o generator comprehensions que justamente permiten construir generadores por comprensión. Esta última se encuentra en modo experimental para 6to5.

Algunos recursos interesantes:

ES6 Rocks
Understanding ES6
Blog de Axel Rauschmayer
Tour de 6to5
ES6 Overview

iphone-device

Hace unos días jugando con formularios (en el equipo de front-end jugamos mucho con formularios) nos encontramos con una feature más que interesante para Safari en IOS8: la posibilidad de escanear tarjetas de crédito.

Luego de buscar un poco en Google, vimos que con el lanzamiento de IOS8, Apple agregó una nueva feature en Safari, no documentada o escondida, llamada «Scan Credit Card».

Esto nos permite escanear tarjetas de crédito desde el navegador utilizando la cámara del dispositivo y luego autocompletar los campos de un formulario con los datos de la tarjeta.

Si tienen un iPod/iPhone/iPad con IOS8, los invito a que lo prueben en la demo que armamos.

demo

Cuando entran a la demo y presionan sobre cualquiera de los inputs del formulario, Safari les va a mostrar el teclado y la opción «Scan Credit Card» (o «Escanear tarjeta de crédito»). Eligen dicha opción y los va a llevar a una pantalla aparte para escanear su tarjeta. Una vez escaneada, el navegador autocompleta el formulario con los siguientes datos:

  • el número de la tarjeta;
  • el nombre que figura en la tarjeta;
  • y la fechas de expiración de la tarjeta (mes y año, por separado).

Lo quiero implementar!

Lo más importante para activar esta feature es que la página en la que se encuentre nuestro formulario tenga un certificado SSL. Lo cual no sorprende ya que si vamos a aceptar pagos online, es algo que todos tenemos que tener.

Por otra parte, es importante entender que no es necesario utilizar JavaScript para activarlo. Al parecer es algo que Safari hace automáticamente y no tenemos forma de controlarlo.

Luego de realizar varias pruebas y gritar un poco, pudimos descubrir que Safari le «presta mucha atención» (para no decir que usa una regex que no sabemos cuál es) a los textos de los labels y a los atributos placeholder de los inputs.

A continuación, tienen el ejemplo del formulario de la demo, el cual funciona correctamente:

<div class="box">
  <header class="header">
    <img class="logo" src="assets/logo.png" alt="Logo Crucero">
    <h1>Crucero</h1>
  </header>

  <form class="form" action="/" method="POST">
    <fieldset>
      <div class="fieldLine">
        <label for="holdername">Name on card</label>
        
      </div>

      <div class="fieldLine">
        <label for="card">Credit Card Number</label>
        
      </div>

      <div class="fieldLine">
        <label for="month">Expiration Date</label>
        
        
      </div>
    </fieldset>

    <div class="form-action">
      <button type="submit" class="btn">Continue</button>
    </div>
  </form>
</div>

Pueden revisar el código completo de la demo en el repo de Github demo-scan-credit-card.

Trucos y secretos

Les tengo que confesar que hay un par de trucos y secretos por los cuales este formulario funciona:

  1. El primero es que todos los textos están en inglés.
  2. El segundo es que estamos usando un texto «mágico» en uno de los labels: «Name on card».

Cuando mencionaba que Safari «presta mucha atención» a los textos, es cierto, pero solamente a los textos que están en inglés, independientemente del lenguaje de nuestro dispositivo o del atributo lang del tag HTML.

¿Qué pasa si mi formulario está en español o portugués? Es acá cuando nos ponemos tristes y pensamos que no podemos usar esta feature, pero momento… es hora de hackear!

Lo que podemos hacer es agregar un segundo label que tenga el texto en inglés y luego ocultarlo. En este caso, vamos a usar el atributo hidden en dicho label.

<div class="fieldLine">
  <label for="holdername" hidden>Name on card</label>
  <label for="holdername">Nombre que figura en la tarjeta</label>
</div>

De esta forma, logramos que todos nuestros usuarios que tengan IOS8 no se pierdan de esta feature y puedan escanear su tarjeta de crédito mejorando su experiencia y velocidad de compra.

Cualquier duda/sugerencia o si encuentran algo nuevo me pueden contactar vía Twitter a @pazguille.

¡Hasta la próxima!