模板引擎的定义
模板引擎就是将数据变为视图最优雅的解决方案 例如:VUE的v-for、mustache 历史上数据变为视图方法:
- 纯DOM方法
- 数组join
- ES6的反引号法:``${a} `
- 模板引擎
mustache的基本使用
官方git:https://github.com/janl/mustache.js 例子:
let arr = [
{ name: "小红", sex: "女", age: 18 },
{ name: "小黑", sex: "男", age: 19 },
{ name: "小白", sex: "女", age: 17 },
];
1.引入mustache库 2.语法
<ul>
{{#arr}}
<li>
<div class="hd">{{name}}的基本信息</div>
<div class="bd">
<p>姓名:{{name}}</p>
<p>性别:{{sex}}</p>
<p>年龄:{{age}}</p>
</div>
</li>
{{/arr}}
</ul>
Mustache.render(templateStr,data)
手写原理代码(简化版)
没有包含布尔值,其它基本实现 index:
import parseTempalteToTokens from "./parseTempalteToTOkens.js";
import renderTamplate from "./renderTamplate.js";
Window.templateEngine = {
render(templateStr, data) {
let tokens = parseTempalteToTokens(templateStr);
let domStr = renderTamplate(tokens, data);
return domStr
},
};
parseTempalteToTokens:
import Scanner from "./Scanner";
import nestTokens from "./nestTokens";
export default function parseTempalteToTokens(templateStr) {
let tokens = [];
let scanner = new Scanner(twmplateStr);
let words;
while (!scanner.eos()) {
words = scanner.scanUtil("{{");
if (words != " ") {
let isInJJH = false;
let _words = "";
for (let i = 0; i < words.length; i++) {
if (words[i] == "<") {
isInJJH = true;
} else {
isInJJH = false;
}
if (!/\s/.test(words[i])) {
_words += words[i];
} else {
if (isInJJH) {
_words += " ";
}
}
}
tokens.push(["text", words]);
}
scanner.scan("{{");
words = scanner.scanUtil("}}");
if (words != "") {
if (words[0] == "#") {
tokens.push(["#", words.substring(1)]);
} else if (words[0] == "/") {
tokens.push(["/", words.substring(1)]);
} else {
tokens.push(["name", words.substring(1)]);
}
}
scanner.scan("}}");
}
return nestTokens(tokens);
}
Scanner:
export default class Scannner {
constructor(templateStr) {
this.templateStr = templateStr;
console.log(templateStr);
this.pos = 0;
this.tail = templateStr;
}
scan(tag) {
if (this.tail.indexOf(tag) == 0) {
this.pos += tag.length;
this.tail = this.templateStr.substring(this.pos);
}
}
scanUtil(stopTag) {
let pos_backyp = this.pos;
while (this.tail.indexOf(stopTag) && !this.eos()) {
this.pos++;
this.tail = this.templateStr.substr(this.pos);
}
return this.templateStr.substring(pos_backyp, this.pos);
}
eos() {
return this.pos >= this.templateStr.length;
}
}
nestTokens:
export default function nestTokens(tokens) {
let nestedTokens = [];
let collector = nestedTokens;
let sections = [];
for (let i = 0; i < tokens.length; i++) {
let token = tokens[i];
switch (token[0]) {
case "#":
collector.push(token);
sections.push(token);
collector = token[2] = [];
break;
case "/":
sections.pop();
collector =
sections.length > 0 ? sections[sections.length - 1][2] : nestedTokens;
break;
default:
collector.push(token);
}
}
return nestedTokens;
}
renderTamplate:
import lookup from "./lookup.js";
import parseArray from "./parseArray.js";
export default function renderTamplate(tokens, data) {
let resultStr = "";
for (let i = 0, len = tokens.length; i < len; i++) {
let token = tokens[i];
if (token[0] == "text") {
resultStr += token[1];
} else if (token[0] == "name") {
resultStr += lookup(data, token[1]);
} else if (token[0] == "#") {
resultStr += parseArray(token, data);
}
}
return resultStr;
}
parseArray:
import lookup from "./lookup.js";
import renderTemplate from "./renderTamplate.js";
export default function parseArray(token, data) {
let v = lookup(data, token[1]);
let resultStr = "";
for (let i = 0, len = v.length; i < len; i++) {
resultStr += renderTemplate(token[2], {
...v[i],
".": v[i],
});
}
return resultStr;
}
lookup:
export default function lookup(dataObj, keyName) {
if (keyName.indexOf(".") != -1 && keyName != ".") {
let keys = keyName.split(".");
let tmp = dataObj;
for (let i = 0, len = keys.length; i < len; i++) {
tmp = tmp[keys[i]];
}
return tmp;
}
return dataObj[keyName];
}
|