Krypton Solid

Plantillas del lado del cliente – Revista Smashing

Plantillas del lado del cliente – Revista Smashing

El uso de plantillas en el navegador se está generalizando cada vez más. Traslado de la lógica de la aplicación del servidor al cliente y el uso cada vez mayor de plantillas inspiradas en patrones similares a MVC (modelo-vista-controlador) para adoptar el navegador.

El uso de plantillas en el navegador se está generalizando cada vez más. Traslado de la lógica de la aplicación del servidor al cliente y el uso cada vez mayor de plantillas inspiradas en patrones similares a MVC (modelo-vista-controlador) para adoptar el navegador. Esto solía ser un asunto solo del lado del servidor, pero las plantillas son en realidad muy poderosas y expresivas también en el desarrollo del lado del cliente.


Credito de imagen: Viktor Hertz

¿Por qué lo usarías?

En general, aprovechar las plantillas es una excelente manera de separar el marcado y la lógica en las vistas, y de maximizar la reutilización y el mantenimiento del código. Con una sintaxis cercana a la salida deseada (es decir, HTML), tiene una forma clara y rápida de hacer las cosas. Aunque las plantillas se pueden usar para generar cualquier tipo de texto, en este artículo proporcionamos ejemplos usando HTML, ya que eso es lo que queremos en el desarrollo del lado del cliente.

Otras lecturas en SmashingMag:

En las aplicaciones dinámicas actuales, el cliente necesita actualizar con frecuencia la interfaz de usuario (UI). Esto se puede hacer obteniendo un fragmento HTML del servidor que se puede insertar fácilmente en el documento. Sin embargo, esto requiere que el servidor admita la entrega de dichos fragmentos (en lugar de páginas completas). Además, como desarrollador del lado del cliente responsable del marcado, quieres tener un control total sobre tus plantillas. No es necesario saber nada sobre Smarty, Velocity, ASP, alguna otra sintaxis oscura del lado del servidor o incluso peor: lidiar con código espagueti como HTML que contiene esos infames <? o <% etiquetas por todo el lugar.

Así que echemos un vistazo a una alternativa viable: las plantillas del lado del cliente.

Primeras impresiones

Para empezar, me gustaría dar una definición del término «plantilla». Aquí hay un buena definicion de foldoc:

«Un documento que contiene parámetros, identificados por alguna sintaxis especial, que son reemplazados por argumentos reales por el sistema de procesamiento de plantillas».

Observemos un ejemplo y veamos cómo se vería una plantilla básica:

<h1>{{title}}</h1>
<ul>
    {{#names}}
        <li>{{name}}</li>
    {{/names}}
</ul>

Esto probablemente le resulte bastante familiar si conoce HTML. Contiene etiquetas HTML con algunos marcadores de posición. Los reemplazaremos con algunos datos reales. Por ejemplo, con este simple objeto:

var data = {
    "title": "Story",
    "names": [
        {"name": "Tarzan"},
        {"name": "Jane"}
    ]
}

La combinación de la plantilla y los datos debería dar como resultado el siguiente HTML:

<h1>Story</h1>
<ul>
    <li>Tarzan</li>
    <li>Jane</ul>
</ul>

Con la plantilla y los datos separados, resulta fácil mantener el HTML. Por ejemplo, cambiar etiquetas o agregar clases solo necesitará cambios en la plantilla. Además, agregar un atributo a elementos repetidos como el <li> El elemento solo necesita hacerse una vez.

Motor de plantillas

La sintaxis de la plantilla (es decir, el formato de los marcadores de posición, como {{title}}) depende de motor de plantilla desea utilizar. Este motor se encarga de analizar las plantillas y reemplazar los marcadores de posición (variables, funciones, bucles, etc.) con los datos reales que se proporcionan.

Algunos motores de plantilla son sin lógica. Esto no significa que solo pueda tener marcadores de posición simples en una plantilla, pero las características están bastante limitadas a algunas etiquetas inteligentes (es decir, iteración de matriz, representación condicional, etc.). Otros motores tienen más funciones y son más extensibles. Sin entrar en detalles aquí, una pregunta que debe hacerse es si permite y cuánta lógica en sus plantillas.

Aunque cada motor de plantilla tiene su propia API, normalmente encontrará métodos como render() y compile(). La hacer proceso es la creación del resultado final poniendo los datos reales en la plantilla. En otras palabras, los marcadores de posición se reemplazan con los datos reales. Y si hay alguna lógica de plantillas, se ejecuta. A compilar una plantilla significa analizarla y traducirla a una función de JavaScript. Cualquier lógica de creación de plantillas se traduce a JavaScript simple, y los datos se pueden enviar a la función, que concatena todos los bits y piezas de una manera optimizada.

Un ejemplo de bigote

La producción del ejemplo anterior se puede realizar utilizando un motor de plantillas, p. Ej. moustache.js. Esto usa el popular Bigote sintaxis de plantillas. Más sobre ellos y alternativas más adelante. Echemos un vistazo a un poco de JavaScript para producir algunos resultados:

var template="<h1>{{title}}</h1><ul>{{#names}}<li>{{name}}</li>{{/names}}</ul>";
var data = {"title": "Story", "names": [{"name": "Tarzan"}, {"name": "Jane"}]};

var result = Mustache.render(template, data);

Ahora queremos mostrar esto en la página. En JavaScript simple, esto se podría hacer así:

document.body.innerHTML = result;

¡Eso es todo! Puede probar lo anterior en su navegador colocando el script Moustache antes de su propio código:

<script src="https://raw.github.com/janl/mustache.js/master/mustache.js"></script>

O puede probar este ejemplo en jsFiddle.

Organizar plantillas

Si eres como yo, probablemente no te guste tener el HTML en una cadena larga. Esto es difícil de leer y de mantener. Idealmente, podemos colocar nuestras plantillas en archivos separados para que aún tengamos todos los beneficios del resaltado de sintaxis y la capacidad de sangrar correctamente las líneas de HTML para facilitar la lectura.

Pero esto lleva a otro problema. Si nuestro proyecto contiene muchas plantillas, no queremos cargar todos esos archivos por separado, ya que esto genera muchas solicitudes (Ajax). Esto sería malo para el rendimiento.

Escenario 1: etiquetas de secuencia de comandos

Una solución que se ve a menudo es poner todas las plantillas dentro <script> etiquetas con una alternativa type atributo, por ejemplo type=“text/template” (que es ignorado para renderizar o analizar por el navegador):

<script id="myTemplate" type="text/x-handlebars-template">
    <h1>{{title}}</h1>
    <ul>
        {{#names}}
            <li>{{name}}</li>
        {{/names}}
    </ul>
</script>

De esta manera, puede poner todas sus plantillas en el documento HTML y evitar todas las solicitudes Ajax adicionales a esas plantillas.

El contenido de dicha etiqueta de secuencia de comandos se puede utilizar más adelante en su JavaScript como plantilla. El siguiente ejemplo de código, esta vez usando el motor de plantillas de Handlebars y un poco de jQuery, usa el anterior <script> etiqueta:

var template = $('#myTemplate').html();
var compiledTemplate = Handlebars.compile(template);
var result = compiledTemplate(data);

También puede probar este ejemplo en jsFiddle.

El resultado aquí es el mismo que en nuestro ejemplo de Moustache. Los manillares también pueden usar plantillas de Moustache, por lo que usamos la misma plantilla aquí. Sin embargo, hay una diferencia (importante), que es que Handlebars utiliza un paso intermedio para obtener el resultado HTML. Primero compila la plantilla en una función de JavaScript (la llamamos compiledTemplate aquí). Esta función luego se ejecuta usando los datos como su único argumento, devolviendo el resultado final.

Escenario 2: Plantillas precompiladas

Si bien solo una función para realizar el renderizado de la plantilla puede parecer conveniente, existen importantes ventajas al dividir el proceso de compilación y renderizado. Lo más importante es que esto permite que la parte de compilación se lleve a cabo en el lado del servidor. Podemos ejecutar JavaScript en el servidor (por ejemplo, usando Node), y algunos de los motores de plantillas admiten esta compilación previa de plantillas.

Poniéndolo todo junto, podemos organizar y entregar un solo archivo JavaScript (digamos, compiled.js) que contiene varias plantillas precompiladas. Esto podría verse más o menos así:

var myTemplates = {
    templateA: function() { ….},
    templateB: function() { ….};
    templateC: function() { ….};
};

Luego, en el código de la aplicación solo necesitamos completar la plantilla precompilada con datos:

var result = myTemplates.templateB(data);

Este es generalmente un enfoque de mucho mejor rendimiento que colocar plantillas dentro <script> etiquetas como se discutió anteriormente, ya que el cliente puede omitir la parte de compilación. Dependiendo de su pila de aplicaciones, este enfoque no es necesariamente más difícil de lograr, como veremos a continuación.

Ejemplo de Node.js

Cualquier secuencia de comandos de precompilación de plantillas debe hacer al menos lo siguiente:

  • leer los archivos de la plantilla,
  • compilar las plantillas,
  • combinar las funciones de JavaScript resultantes en uno o más archivos.

El siguiente script básico de Node.js hace todo eso (usando el motor de plantillas Hogan.js):

var fs = require('fs'),
    hogan = require('hogan.js');

var templateDir="./templates/",
    template,
    templateKey,
    result="var myTemplates = {};";

fs.readdirSync(templateDir).forEach(function(templateFile) {

    template = fs.readFileSync(templateDir + templateFile, 'utf8');
    templateKey = templateFile.substr(0, templateFile.lastIndexOf('.'));

    result += 'myTemplates["'+templateKey+'"] = ';
    result += 'new Hogan.Template(' + hogan.compile(template, {asString: true}) + ');'

});

fs.writeFile('compiled.js', result, 'utf8');

Esto lee todos los archivos en el templates/ carpeta, compila las plantillas y las escribe en compiled.js.

Tenga en cuenta que este es un código altamente no optimizado y no incluye ningún manejo de errores. Aún así, hace el trabajo y muestra que no requiere mucho código para precompilar las plantillas.

Escenario 3: AMD y RequireJS

La definición de módulo asincrónico (AMD) está ganando cada vez más tracción. Los módulos desacoplados suelen ser una excelente manera de organizar una aplicación. Uno de los cargadores de módulos más populares es RequireJS. En una definición de módulo, se pueden especificar dependencias, que se resolverán y estarán disponibles para el módulo real (fábrica).

En el contexto de las plantillas, RequireJS tiene un complemento de «texto» que le permite especificar dependencias basadas en texto. Las dependencias de AMD se tratan como JavaScript de forma predeterminada, pero las plantillas son solo texto (por ejemplo, HTML), por lo que usamos el complemento para eso. Por ejemplo:

define(['handlebars', 'text!templates/myTemplate.html'], function(Handlebars, template) {

    var myModule = {

        render: function() {

            var data = {"title": "Story", "names": [{"name": "Tarzan"}, {"name": "Jane"}]};
            var compiledTemplate = Handlebars.compile(template);
            return compiledTemplate(data);

        }
    };

    return myModule;
});

De esta manera, la ventaja radica (solo) en la capacidad de organizar las plantillas en archivos separados. Esto es bueno, pero necesita una solicitud Ajax adicional para obtener la plantilla, y aún necesita compilar la plantilla del lado del cliente. Sin embargo, la solicitud adicional se puede eliminar utilizando el r.js optimizador que viene con RequireJS. Esto resuelve las dependencias y «alineará» las plantillas (o cualquier dependencia) en esta definición de módulo, reduciendo enormemente el número de solicitudes.

La ausencia de un paso de precompilación se puede solucionar de varias formas. Puede que se le ocurra que el optimizador también precompile las plantillas (por ejemplo, podríamos escribir un complemento para r.js). Pero eso también requeriría un cambio en la definición del módulo, ya que estaríamos usando una plantilla cuerda antes de la optimización y una plantilla función después. Sin embargo, esto no sería muy difícil de manejar, ya sea verificando este tipo de variable o abstrayendo esta lógica (en el complemento o en la aplicación).

Visualización de plantillas

En ambos escenarios # 2 y # 3, podemos hacerlo aún mejor al tratar nuestras plantillas como archivos fuente no compilados. Al igual que los archivos CoffeeScript, Less o SCSS. Podemos tener nuestros archivos de plantilla vigilados para detectar cambios durante el desarrollo, y recompilarlos automáticamente cuando se cambia un archivo, es decir, al igual que compilaría CoffeeScript en JavaScript. De esta manera, siempre estamos tratando con plantillas precompiladas en nuestro código, y el optimizador integra sin esfuerzo las plantillas precompiladas en el proceso de compilación.

define(['templates/myTemplate.js'], function(compiledTemplate) {

    var myModule = {

        render: function() {

            var data = {"title": "Story", "names": [{"name": "Tarzan"}, {"name": "Jane"}]};
            return compiledTemplate(data);

        };
    };

    return myModule;
}

Consideraciones de rendimiento

Representación Actualizaciones de UI El uso de plantillas del lado del cliente suele ser el camino a seguir. Aún así, el mejor rendimiento para la inicial página completa La carga se logra sirviendo esa página como un todo. Esto permite que el navegador represente el HTML tal cual sin necesidad de análisis de JavaScript ni solicitudes adicionales de datos. Esto podría ser un desafío, especialmente para las páginas que son dinámicas y requieren los mejores tiempos de carga inicial posibles. Entonces, idealmente, las plantillas se están desarrollando y reutilizando en el cliente y el servidor para soportar el mejor rendimiento y seguir siendo mantenibles.

Dos preguntas a considerar aquí son:

  • ¿Qué parte de mi aplicación es principalmente dinámica y qué parte requiere los mejores tiempos de carga inicial posibles?
  • ¿Desea transferir el procesamiento al cliente o el servidor debe hacer el trabajo pesado?

La respuesta solo se puede dar midiendo realmente diferentes enfoques. Sin embargo, al usar plantillas precompiladas, el cliente generalmente no tiene muchas dificultades para renderizarlas sobre la marcha. Y en caso de que desee reutilizar plantillas en el cliente y el servidor, encontrará que una sintaxis de plantilla sin lógica es la más versátil.

Conclusión

Hemos visto muchas fortalezas de las plantillas del lado del cliente, que incluyen:

  • Los servidores de aplicaciones y las API son mejores para servir solo los datos (es decir, JSON); Las plantillas del lado del cliente encajan perfectamente.
  • HTML y JavaScript coinciden naturalmente con las habilidades de los desarrolladores del lado del cliente.
  • El uso de plantillas refuerza una buena práctica de separar presentación y lógica.
  • Las plantillas se pueden precompilar y almacenar en caché por completo, esto deja solo los datos reales para actualizar desde el servidor.
  • Pasar la fase de renderizado del servidor al cliente puede afectar positivamente el rendimiento.

Hemos analizado bastantes aspectos de la creación de plantillas (del lado del cliente). Es de esperar que a estas alturas ya comprenda mejor el concepto y por qué lo usaría.

Deja un comentario