Si te gusta este artículo, te sugiero que le eches un vistazo a mis otros en mi blog .
Hola, soy yo, @shadowtime2000 , uno de los mantenedores de Eta , un motor de plantillas rápido e integrable. En este tutorial, mostraré cómo crear un motor de plantilla de JavaScript isomorfo (navegador/nodo).
El diseño inicial del motor de plantillas será bastante simple. Simplemente interpolará valores de un
data
objeto. usará {{valueName}}
para interpolar valores.Primero, creemos una función de representación simple que toma la plantilla y los datos y representará el valor.
var render = ( template, data ) => { return template.replace( /{{(.*?)}}/g , (match) => { return data[match.split( /{{|}}/ ).filter( Boolean )[ 0 ]] }) }
Básicamente, todo lo que hace es buscar cualquier cosa que esté entre corchetes y lo reemplaza con el nombre dentro
data
. Puede escribir sus plantillas de esta manera y las tomará del objeto de datos. Hi, my name is {{name}} !
render( "Hi, my name is {{name}}!" , { name : "shadowtime2000" });
Pero hay un problema, no puedes tener espacios en las interpolaciones.
render( "Hi, my name is {{ name }}!" , { name : "shadowtime2000" }) /* Hi, my name is undefined! */
Esto requiere que tenga espacios dentro del objeto de datos, que no está tan limpio. Podemos hacer que permita espacios recortando los espacios en blanco iniciales y finales del nombre de los datos antes de la interpolación.
var render = ( template, data ) => { return template.replace( /{{(.*?)}}/g , (match) => { return data[match.split( /{{|}}/ ).filter( Boolean )[ 0 ].trim()] }) }
Esto es bastante bueno, pero para plantillas más grandes no sería tan rápido porque tiene que analizarlo todo el tiempo. Es por eso que muchos motores de plantillas admiten la compilación, donde la plantilla se compila en una función JS más rápida que puede tomar los datos e interpolarlos. Agreguemos compilación a nuestro motor de plantillas, pero antes de hacerlo, debemos agregar una función de análisis especial.
Dado que el análisis puede ser un poco aburrido, reutilicemos un poco de código de otro motor de plantilla JS. Habría usado el motor de análisis Eta, pero se ha optimizado extremadamente y puede ser bastante confuso para las personas. Por lo tanto, usemos otro código de análisis de motor de plantilla JS popular, mde/ejs . Recuerde atribuirlos al motor de análisis.
var parse = ( template ) => { let result = /{{(.*?)}}/g .exec(template); const arr = []; let firstPos; while (result) { firstPos = result.index; if (firstPos !== 0 ) { arr.push(template.substring( 0 , firstPos)); template = template.slice(firstPos); } arr.push(result[ 0 ]); template = template.slice(result[ 0 ].length); result = /{{(.*?)}}/g .exec(template); } if (template) arr.push(template); return arr; }
Lo que esto hace básicamente es repetir la ejecución del patrón de expresiones regulares en la plantilla y agregar las cosas a una estructura de datos. Así es como se vería esa estructura de datos:
[ "Hi my name is " , "{{ name }}" , "!" ]
Ahora que hemos terminado el análisis, pasemos a la compilación.
Echemos un vistazo rápido a lo que generaría la compilación. Imagina que ingresas esta plantilla:
Hi my name is {{ name }} !
Te dará esta función:
function ( data ) { return "Hi my name is " +data.name+ "!" ; }
Primero creemos una función para analizar y luego creemos una cadena que se pueda usar. Primero tenemos que analizar la plantilla.
const compileToString = ( template ) => { const ast = template; }
También tenemos que crear una cadena que se utilizará como función.
const compileToString = ( template ) => { const ast = template; let fnStr = `""` ; }
La razón por la que usamos comillas al principio es porque cuando se compilan las plantillas y demás, todas comenzarán con un
+
. Ahora tenemos que iterar sobre el AST. const compileToString = ( template ) => { const ast = template; let fnStr = `""` ; ast.map( t => { // checking to see if it is an interpolation if (t.startsWith( "{{" ) && t.endsWith( "}}" )) { // append it to fnStr fnStr += `+data. ${t.split( /{{|}}/ ).filter( Boolean )[ 0 ].trim()} ` ; } else { // append the string to the fnStr fnStr += `+" ${t} "` ; } }); }
La parte final de esta función es devolver la cadena de función.
const compileToString = ( template ) => { const ast = template; let fnStr = `""` ; ast.map( t => { // checking to see if it is an interpolation if (t.startsWith( "{{" ) && t.endsWith( "}}" )) { // append it to fnStr fnStr += `+data. ${t.split( /{{|}}/ ).filter( Boolean )[ 0 ].trim()} ` ; } else { // append the string to the fnStr fnStr += `+" ${t} "` ; } }); return fnStr; }
Entonces, si toma esta plantilla:
Hi my name is {{ name }} !
Devolverá esto:
"" + "Hello my name is " +data.name+ "!"
Ahora que esto está hecho, crear una función de compilación es relativamente simple.
const compile = ( template ) => { return new Function ( "data" , "return " + compileToString(template)) }
Ahora hemos completado la compilación de nuestro motor de plantillas.
En este tutorial mostré cómo: