Handlebars ha ido ganando popularidad con su adopción en marcos como Meteor y Ember.js, pero lo que realmente está sucediendo detrás de las escenas de este emocionante motor de plantillas.?
En este artículo analizaremos en profundidad el proceso subyacente que utilizan los manillares para compilar sus plantillas..
Este artículo espera que hayas leído mi introducción anterior a Los manillares y como tales asumen que usted sabe lo básico para crear plantillas de manillar..
Al usar una plantilla de manillares, probablemente sepa que comienza compilando el origen de la plantilla en una función usando Handlebars.compile ()
y luego utiliza esa función para generar el HTML final, pasando valores para propiedades y marcadores de posición.
Pero esa aparentemente simple función de compilación en realidad está realizando bastantes pasos detrás de la escena, y de eso se tratará realmente este artículo; echemos un vistazo a un rápido desglose del proceso:
En este artículo crearemos una herramienta para analizar las plantillas de los manubrios en cada uno de estos pasos, de modo que para mostrar un poco mejor los resultados en la pantalla, usaré Resaltador de sintaxis prism.js creado por el único y único Lea Verou. Descargue la fuente minificada recordando revisar JavaScript en la sección de idiomas.
El siguiente paso es crear un archivo HTML en blanco y llenarlo con lo siguiente:
Manillares.js Fichas:
Operaciones:
Salida:
Función:
Es solo un código repetitivo que incluye manillares y prisma y luego configura algunos divs para los diferentes pasos. En la parte inferior, puede ver dos bloques de script: el primero es para la plantilla y el segundo para nuestro código JS.
También escribí un poco de CSS para organizar todo un poco mejor, que puedes agregar:
cuerpo margen: 0; relleno: 0; font-family: "opensans", Arial, sans-serif; fondo: # F5F2F0; tamaño de fuente: 13px; #análisis arriba: 0; izquierda: 0; posición: absoluta; ancho: 100%; altura: 100%; margen: 0; relleno: 0; #análisis div ancho: 33.33%; altura: 50%; flotador izquierdo; relleno: 10px 20px; tamaño de caja: caja de borde; desbordamiento: auto; #function ancho: 100%! importante;
A continuación necesitamos una plantilla, así que comencemos con la plantilla más simple posible, solo un poco de texto estático:
Abrir esta página en su navegador debería dar como resultado que la plantilla se muestre en el cuadro de resultados como se esperaba, no hay nada diferente todavía, ahora tenemos que escribir el código para analizar el proceso en cada una de las otras tres etapas.
El primer paso que realizan los manillares en su plantilla es tokenizar la fuente, lo que esto significa es que necesitamos separar la fuente en sus componentes individuales para que podamos manejar cada pieza de manera adecuada. Entonces, por ejemplo, si hubiera un texto con un marcador de posición en el medio, entonces Handlebars separaría el texto antes de que el marcador de posición lo colocara en un token, entonces el marcador de posición se colocaría en otro token, y finalmente todo el texto después del marcador de posición sería colocado en una tercera ficha. Esto se debe a que esas piezas deben conservar el orden de la plantilla, pero también deben procesarse de manera diferente.
Este proceso se realiza utilizando el Manillares.parse ()
función, y lo que obtienes es un objeto que contiene todos los segmentos o 'declaraciones'.
Para ilustrar mejor de qué estoy hablando, vamos a crear una lista de párrafos para cada uno de los tokens sacados:
// Mostrar tokens var tokenizer = Handlebars.parse (src); var tokenStr = ""; para (var i en tokenizer.statements) var token = tokenizer.statements [i]; tokenStr + = ""+ (parseInt (i) +1) +") "; switch (token.type) case" content ": tokenStr + =" [string] - \ "" + token.string + "\" "; break; caso "bigote": tokenStr + = "[marcador de posición] -" + token.id.string; break; caso "bloque": tokenStr + = "[bloque] -" + token.mustache.id.string; documento. getElementById ("tokens"). innerHTML + = tokenStr;
Así que comenzamos ejecutando la fuente de plantillas en Manillar.parse
para obtener la lista de fichas. Luego, hacemos un ciclo a través de todos los componentes individuales y construimos un conjunto de cadenas legibles por humanos basadas en el tipo de segmento. El texto sin formato tendrá un tipo de "contenido" que, a continuación, podemos mostrar la cadena entre comillas para mostrar lo que es igual. Los marcadores de posición tendrán un tipo de "bigote" que luego podemos mostrar junto con su "id" (nombre del marcador de posición). Y por último, pero no menos importante, los ayudantes de bloque tendrán un tipo de "bloque" que también podemos mostrar simplemente los bloques "id" internos (nombre del bloque).
Actualizando esto ahora en el navegador, deberías ver un solo token de 'cadena', con el texto de nuestra plantilla.
Una vez que el manillar tiene la colección de fichas, recorre cada una de ellas y "genera" una lista de operaciones predefinidas que deben realizarse para que la plantilla se compile. Este proceso se realiza utilizando el Manillar.Compilador ()
objeto, pasando en el objeto token del paso 1:
// Operaciones de visualización var opSequence = new Handlebars.Compiler (). Compile (tokenizer, ); var opStr = ""; para (var i en opSequence.opcodes) var op = opSequence.opcodes [i]; opStr + = ""+ (parseInt (i) +1) +") - "+ op.opcode; document.getElementById (" operaciones "). innerHTML + = opStr;
Aquí estamos compilando los tokens en la secuencia de operaciones de la que hablé, y luego repasamos cada uno de ellos y creamos una lista similar a la del primer paso, excepto que aquí solo tenemos que imprimir el código de operación. El código de operación es la "operación" o el "nombre" de la función que debe ejecutarse para cada elemento en la secuencia.
De vuelta en el navegador, ahora debería ver una sola operación llamada 'appendContent' que agregará el valor al 'búfer' o 'cadena de texto' actual. Hay muchos códigos de operación diferentes y no creo que esté calificado para explicar algunos de ellos, pero hacer una búsqueda rápida en el código fuente para un código de operación determinado le mostrará la función que se ejecutará para ello.
La última etapa es tomar la lista de códigos de operación y convertirlos en una función, lo hace leyendo la lista de operaciones y concatenando el código de forma inteligente para cada una. Aquí está el código requerido para obtener la función para este paso:
// Función de visualización var outputFunction = new Handlebars.JavaScriptCompiler (). Compile (opSequence, , undefined, true); document.getElementById ("source"). innerHTML = outputFunction.toString (); Prism.highlightAll ();
La primera línea crea el compilador que pasa en la secuencia op, y esta línea devolverá la función final utilizada para generar la plantilla. Luego convertimos la función en una cadena y le decimos a Prism que la sintaxis la resalte.
Con este código final, su página debería verse así:
Esta función es increíblemente simple, ya que solo hubo una operación, solo devuelve la cadena dada; ahora veamos cómo editar la plantilla y cómo estos pasos individuales son sencillos, se agrupan para formar una abstracción muy poderosa.
Comencemos con algo simple, y simplemente reemplacemos la palabra 'Mundo' con un marcador de posición; Su nueva plantilla debe verse como la siguiente:
Y no olvide pasar la variable para que la salida se vea bien:
// Display Output var t = Handlebars.compile (src); document.getElementById ("output"). innerHTML + = t (name: "Gabriel");
Ejecutando esto, encontrará que al agregar un simple marcador de posición, se complica el proceso bastante.
La sección si / else es complicada porque no sabe si el marcador de posición es de hecho un marcador de posición o un método auxiliar.
Si todavía no estaba seguro de qué son los tokens, debería tener una mejor idea ahora; Como puede ver en la imagen, separa el marcador de posición de las cadenas y crea tres componentes individuales.
A continuación, en la sección de operaciones, hay bastantes adiciones. Si recuerda de antes, para simplemente enviar algo de texto, Handlebars usa la operación 'appendContent', que es lo que puede ver ahora en la parte superior e inferior de la lista (tanto para "Hola" como para "!"). El resto en el medio son todas las operaciones necesarias para procesar el marcador de posición y agregar el contenido escapado.
Finalmente, en la ventana inferior, en lugar de simplemente devolver una cadena, esta vez crea una variable de búfer y maneja un token a la vez. La sección complicada if / else es porque no sabe si el marcador de posición es de hecho un marcador de posición o un método auxiliar. Por lo tanto, trata de ver si existe un método auxiliar con el nombre dado, en cuyo caso llamará al método auxiliar y establecerá 'pila1' al valor. En el caso de que sea un marcador de posición, asignará el valor del contexto pasado (aquí denominado 'depth0') y si se pasa una función, colocará el resultado de la función en la variable 'stack1'. Una vez hecho esto, lo escapa como vimos en las operaciones y lo agrega al búfer.
Para nuestro próximo cambio, simplemente probemos la misma plantilla, excepto esta vez sin escapar de los resultados (para hacer esto, agregue otra llave) "nombre"
)
Refrescando la página, ahora verá que se eliminó la operación para escapar de la variable y en lugar de eso simplemente la agrega, esto forma una burbuja hacia abajo en la función que ahora simplemente verifica para asegurarse de que el valor no sea un valor falso (además de 0) y luego lo agrega sin escapar.
Así que creo que los marcadores de posición son bastante sencillos, ahora echemos un vistazo a las funciones de ayuda..
No tiene sentido hacer esto más complicado de lo que tiene que ser, simplemente creamos una función simple que devolverá el duplicado de un número pasado, así que reemplace la plantilla y agregue un nuevo bloque de script para el ayudante (antes del otro código ):
Decidí no escapar, ya que hace que la función final sea un poco más fácil de leer, pero puede probar ambas si lo desea. De todos modos, ejecutar esto debería producir lo siguiente:
Aquí puede ver que sabe que es una ayuda, por lo que en lugar de decir 'invokeAmbiguous' ahora dice 'invokeHelper' y, por lo tanto, en la función ya no hay un bloque if / else. Sin embargo, todavía se asegura de que exista el ayudante e intente recurrir al contexto para una función con el mismo nombre en caso de que no lo haga..
Otra cosa que vale la pena mencionar es que puede ver los parámetros para que los ayudantes se pasen directamente y, de ser posible, se codifican de forma rígida cuando se genera la función (el número 3 en la función duplicada).
El último ejemplo que quiero cubrir es sobre los ayudantes de bloque..
Los ayudantes de bloque le permiten envolver otros tokens dentro de una función que puede establecer su propio contexto y opciones. Veamos un ejemplo usando el ayudante de bloque predeterminado "if":
Aquí estamos verificando si "nombre" está establecido en el contexto actual, en cuyo caso lo mostraremos, de lo contrario emitiremos "¡Mundo!". Al ejecutar esto en nuestro analizador, verá solo dos fichas aunque haya más; esto se debe a que cada bloque se ejecuta como su propia 'plantilla', por lo que todas las fichas dentro de él (como nombre
) no formará parte de la llamada externa, y deberá extraerla del propio nodo del bloque.
Además de eso, si echas un vistazo a la función:
Puede ver que en realidad compila las funciones del ayudante de bloque en la función de la plantilla. Hay dos porque uno es la función principal y el otro es la función inversa (cuando el parámetro no existe o es falso). La función principal: "program1" es exactamente lo que teníamos antes cuando solo teníamos un texto y un único marcador de posición, porque como mencioné, cada una de las funciones de ayuda de bloque se construye y trata exactamente como una plantilla normal. Luego se ejecutan a través del ayudante "if" para recibir la función apropiada que luego se agregará al búfer externo.
Como antes, vale la pena mencionar que el primer parámetro para un ayudante de bloque es la clave en sí, mientras que el parámetro 'esto' se establece en todo el contexto pasado, lo que puede ser útil cuando se crean sus propios ayudantes de bloque..
En este artículo, es posible que no hayamos echado un vistazo práctico a cómo lograr algo en Handlebars, pero espero que tenga una mejor comprensión de lo que está sucediendo detrás de escena, lo que le permitirá crear mejores plantillas y ayudantes con esta nueva información conocimiento.
Espero que hayas disfrutado leyendo, como siempre, si tienes alguna pregunta, no dudes en contactarme en Twitter (@GabrielManricks) o en Nettuts + IRC (#nettuts en freenode).