mustache基本使用
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>mustache基本使用</title>
<script src="https://cdn.bootcdn.net/ajax/libs/mustache.js/4.1.0/mustache.js"></script>
</head>
<body>
<div id="container"></div>
<script>
let templateStr = `
<div>
<h1>{{title}}</h1>
{{#user}}
<p>姓名:{{name}}</p>
<p>性别:{{sex}}</p>
<p>爱好:
{{#hobby}}
<span>{{.}}</span>
{{/hobby}}
<p>
{{/user}}
{{#isShow}}
<p>卷不动啦,好烦啊</p>
{{/isShow}}
</div>
`;
let data = {
title: "名单",
user: [
{
name: "www",
sex: "男",
hobby: ["1", "2", "3"],
},
{
name: "qqq",
sex: "女",
hobby: ["1", "2", "3"],
},
],
isShow: false,
};
let domStr = Mustache.render(templateStr, data);
let container = document.getElementById("container");
container.innerHTML = domStr;
</script>
</body>
</html>
mustache源码仿写
index.js
import getTokens from './getTokens.js'
import renderTemplate from './renderTemplate.js'
window.mustache = {
render(templateStr, data) {
// 将模板字符串转换成tokens
let tokens = getTokens(templateStr)
// 将tokens转换成dom字符串
let domStr = renderTemplate(tokens, data)
return domStr
}
}
scanner.js
// 创建扫描器类
export default class Scanner {
constructor(str) {
this.templateStr = str
this.pos = 0
this.tail = str
}
// 扫描模板字符串,返回扫描内容(源码中scanUtil方法)
scan(stopTag) {
let startPos = this.pos
while (this.pos < this.templateStr.length && this.tail.indexOf(stopTag) !== 0) {
this.pos++
this.tail = this.templateStr.substr(this.pos)
}
return this.templateStr.slice(startPos, this.pos)
}
// 跳过大括号(源码中scan方法)
jump(tag) {
if (this.tail.indexOf(tag) === 0) {
this.pos = this.pos + tag.length
this.tail = this.templateStr.substr(this.pos)
}
}
}
getTokens.js
import Scanner from './scanner.js';
export default function getTokens(templateStr) {
let lastTokens = []
let scanner = new Scanner(templateStr)
let str = ""
while (scanner.pos < templateStr.length) {
str = scanner.scan("{{")
if (str !== "") {
lastTokens.push(['text', str])
}
scanner.jump("{{")
str = scanner.scan("}}")
if (str !== "") {
if (str[0] === "#") {
lastTokens.push(['#', str.substr(1)])
} else if (str[0] === "/") {
lastTokens.push(['/', str.substr(1)])
} else {
lastTokens.push(['name', str])
}
}
scanner.jump("}}")
}
let nextTonkens = []
let sections = []
let collector = nextTonkens
for (let i in lastTokens) {
let token = lastTokens[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] : nextTonkens
break
default:
collector.push(token)
}
}
return nextTonkens
}
renderTemplate.js
function handleLoop(token, data) {
let result = ""
let arr = data[token[1]]
for (let i in arr) {
result = result + renderTemplate(token[2], {
...arr[i],
".":arr[i]
})
}
return result
}
export default function renderTemplate(tokens, data) {
let result = ""
for (let i in tokens) {
let token = tokens[i]
if (token[0] == "text") {
result = result + token[1]
} else if (token[0] == "name") {
result = result + data[token[1]]
} else if (token[0] == "#") {
result = result + handleLoop(token, data)
}
}
return result
}