O design
O design inicial do mecanismo de template será bastante simples. Ele simplesmente interpola valores de um
dataobjeto. Vai usar{{valueName}} para interpolar valores.


Renderização Simples
Primeiro, vamos criar uma função de renderização simples que pega o modelo e os dados e renderiza o valor.



var render = (template, data) => {
return template.replace(/{{(.*?)}}/g, (match) => {
return data[match.split(/{{|}}/).filter(Boolean)[0]]
})
}Basicamente, tudo o que faz é pesquisar qualquer coisa que esteja entre colchetes e substitui-o pelo nome dentro
data. Você pode escrever seus modelos como este e irá retirá-los do objeto de dados.


Hi, my name is {{name}}!render("Hi, my name is {{name}}!", {
name: "shadowtime2000"
});Mas há um problema, você não pode ter espaços nas interpolações.



render("Hi, my name is {{ name }}!", {
name: "shadowtime2000"
})
/*
Hi, my name is undefined!
*/Isso requer que você tenha espaços dentro do objeto de dados, o que não é tão limpo. Podemos fazer com que permita espaços cortando os espaços em branco iniciais e finais do nome dos dados antes da interpolação.



var render = (template, data) => {
return template.replace(/{{(.*?)}}/g, (match) => {
return data[match.split(/{{|}}/).filter(Boolean)[0].trim()]
})
}Isso é muito bom, mas para modelos maiores não seria tão rápido porque tem que analisá-lo o tempo todo. É por isso que muitos motores de template suportam compilação, onde o template é compilado em uma função JS mais rápida que pode pegar os dados e interpolar. Vamos adicionar compilação ao nosso mecanismo de template, mas antes disso, precisamos adicionar uma função de análise especial.



Análise
Já que a análise pode ser um pouco entediante, vamos apenas reutilizar algum código de outro mecanismo de modelo JS. Eu teria usado o mecanismo de análise Eta, mas ele foi extremamente otimizado e pode ser muito confuso para as pessoas. Portanto, vamos usar outro código de análise de mecanismo de modelo JS popular, mde / ejs . Lembre-se de atribuí-los ao mecanismo de análise.



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;
}O que isso basicamente faz é repetir a execução do padrão regex no modelo e adicionar as coisas a uma estrutura de dados. Esta é a aparência dessa estrutura de dados:



["Hi my name is ", "{{ name }}", "!"]Agora que a análise foi concluída, vamos prosseguir para a compilação.



Compilação
Vamos dar uma rápida visão geral do resultado da compilação. Imagine que você insira este modelo:



Hi my name is {{ name }}!Ele lhe dará esta função:



function (data) {
return "Hi my name is "+data.name+"!";
}Vamos primeiro criar uma função para analisar e, em seguida, criar uma string que pode ser usada. Primeiro, temos que analisar o modelo.



const compileToString = (template) => {
const ast = template;
}Também temos que criar uma string que será usada como função.



const compileToString = (template) => {
const ast = template;
let fnStr = `""`;
}O motivo pelo qual estamos usando aspas no início é porque quando ele está compilando os modelos e outros, todos eles começarão com um
+. Agora temos que iterar no 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}"`;
}
});
}A parte final desta função é retornar a string da função.



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;
}Então, se for este o modelo:



Hi my name is {{ name }}!Ele retornará este:



""+"Hello my name is "+data.name+"!"Agora que isso está feito, criar uma função de compilação é relativamente simples.



const compile = (template) => {
return new Function("data", "return " + compileToString(template))
}Agora concluímos a compilação de nosso mecanismo de modelo.
Sem comentários:
Enviar um comentário
Comente de forma construtiva...
Nota: só um membro deste blogue pode publicar um comentário.