赞
踩
模板引擎就是将数据变为视图最优雅的解决方案
例如:VUE的v-for、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);
// console.log(tokens);
// 调用renderTemplate函数,让tokens数组变为dom字符串
let domStr = renderTamplate(tokens, data);
return domStr
},
};
parseTempalteToTokens:
import Scanner from "./Scanner"; import nestTokens from "./nestTokens"; /** * * 将模板字符串变为token数组 * */ export default function parseTempalteToTokens(templateStr) { let tokens = []; // 创建扫描器 let scanner = new Scanner(twmplateStr); let words; // scanner没有到头 while (!scanner.eos()) { // 收集开始标记之前的文字 words = scanner.scanUtil("{{"); // 存储 if (words != " ") { // 普通文字中的空格没有意义;标签中的空格保留<div class="box"></div> 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("}}"); // 存储,此时的words为{{}}中的内容 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) { // tag有多长,就让pos后移多少位 this.pos += tag.length; // 改变尾巴为从当前到最后 this.tail = this.templateStr.substring(this.pos); } } // 让指针进行扫描,直到遇到指定内容,并返回结束之前的文字 scanUtil(stopTag) { // 记录执行方法的pos的值 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:
/** * 折叠,将#和/ 之间的tokens整合起来作为它下标为3的向 * 将零散的tokens串起来--->栈:遇见#入栈;遇见/出栈 */ 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 "#": // 收集器存储token 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"; /** * 让tokens数组变为dom字符串 * 递归:#标记的tokens需要处理它下标为1 */ export default function renderTamplate(tokens, data) { // 结果字符串 let resultStr = ""; // 遍历tokens 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") { // 如果试name类型,直接使用它的值,要调用lookup // 防止是a.d.c 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"; /** * 处理token数组,结合renderTemplate实现递归 * 注意:接收参数为token * ['#',student,[]] * * * 这个函数:递归调用renderTemplate,次数有data决定 * */ export default function parseArray(token, data) { // 得到data中数组需要渲染的部分 let v = lookup(data, token[1]); // 结果字符串 let resultStr = ""; // 遍历v,这里只考虑v为数组的情况 // 重点:下面这个循环!遍历的是数据 for (let i = 0, len = v.length; i < len; i++) { // '.'的识别,例子见最后 resultStr += renderTemplate(token[2], { ...v[i], ".": v[i], }); } return resultStr; } /** .的例子 * <ul> {{#arr}} <li> {{.}} </li> {{/arr}} </ul> * * let data={ * arr:['1','2','3'] * } * * */
lookup:
/** * 让dataObj对象中可以用.符号的keyName属性 * 例如:dataObj为: * { * a:{ * b:{ * c:100 * } * } * } * 那么lookup(dataObj,'a.b.c')结果为100 */ export default function lookup(dataObj, keyName) { // 看看keyName中有没有.符号,但keyname不能为. if (keyName.indexOf(".") != -1 && keyName != ".") { // 如果有,拆分属性 let keys = keyName.split("."); // 设置临时变量tmp,用于周转,一层层的找 let tmp = dataObj; for (let i = 0, len = keys.length; i < len; i++) { // 更新tmp tmp = tmp[keys[i]]; } return tmp; } return dataObj[keyName]; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。