前面已经实现了 模板字符串转化为 tokens ,这篇我们将实现 tokens 到 dom字符串的转化
1.获取对象嵌套的数据
当出现一个需要展示的数据为被多层对象嵌套时,由于js不支持 data[a.b],使得无法获取需要展示的数据,所以需要进行必要的处理
lookup(data, key) {
//含有嵌套的数据,进行处理来获取数据
if (key.includes() != -1&&key!=".") {
let keyArr = key.split(".")
let temp = data
keyArr.forEach(item=>{
temp = temp[item]
})
console.log(temp) //150
return temp
}
//不含嵌套的数据,直接返回即可
return data[key]
}
let data = {
a: {
b: {
c: {
d: 150
}
}
}
}
let key = "a.b.c.d"
lookup(data, key)
2.将 tokens 转化为dom字符串
如下:将
转化为
代码:
import lookup from "./lookup"
export default function renderTemplate(templateStr, data) {
let resultStr = '';
for (const item of templateStr) {
if (item[0] == 'text') {
resultStr += item[1]
}
if (item[0] == "name") {
resultStr += lookup(data, item[1])
}
if (item[0] == "#") {
for(let i =0;i<data[item[1]].length;i++){
//简单的数组循环可以 " . " 代替索引
data[item[1]][i] = {
...data[item[1]][i],
".":data[item[1]][i]
}
resultStr += renderTemplate(item[2],data[item[1]][i])
}
}
}
return resultStr.trim()
}
简易版musache完整版
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div class="box"></div>
<script src="/xuni/bundle.js"></script>
<script>
let data = {
arr: [
{ name: "小王", roles: ['教师', "前端高级工程师"] },
{ name: "小李", roles: ['项目经理', "hr", '母亲'] },
]
}
let templateStr = `
<ul >
{{#arr}}
<li class="a">姓名:{{name}}</li>
<li>角色:
<ol>
{{#roles}}
<li>{{.}}</li>
{{/roles}}
</ol>
</li>
{{/arr}}
</ul>
`
let htmlStr = LLz_Template.render(templateStr, data)
console.log(htmlStr)
document.querySelector(".box").innerHTML = htmlStr
</script>
</body>
</html>
import becomeEasyToken from "./becomeEasyToken.js"
import renderTemplate from "./renderTemplate.js"
window.LLz_Template = {
render(templateStr,data){
templateStr = templateStr.trim()
let tokens = becomeEasyToken(templateStr)
let domStr = renderTemplate(tokens,data)
return domStr
}
}
export default class Scanner {
constructor (templateStr ){
//将模板字符串写到实例身上
this.templateStr = templateStr
//指针
this.pos = 0
//尾巴,刚开始为字符串本身
this.tail = templateStr
}
//让指针跳过目标,进而扫描后面的内容
scan(target){
this.pos += target.length
this.tail = this.templateStr.substring(this.pos)
}
//扫描字符串,直到扫描到目标,返回目标之前的字符串
scanUtil(target) {
let recordPosValue = this.pos
//如果该字符串的第一个元素即该目标的索引不为0时,说明指针还需要往右走
while(this.tail.indexOf(target)!=0&&this.pos<this.templateStr.length){
this.pos++;
//尾巴变为pos后面的部分
this.tail = this.templateStr.substring(this.pos)
}
return this.templateStr.substring(recordPosValue,this.pos)
}
}
import Scanner from "./Scanner"
import foldToken from "./foldToken";
export default function becomeEasyToken (templateStr){
let token = []
//实例化一个扫描器,针对模板字符串工作
let scanner = new Scanner(templateStr)
while(scanner.pos<templateStr.length){
let word;
word = scanner.scanUtil('{{');
if(word !=''){
token.push(["text",word])
}
scanner.scan('{{')
word = scanner.scanUtil("}}")
if(word !=''){
if(word[0] == "#"){
token.push(["#",word.substring(1)])
}else if(word[0]=="/"){
token.push(['/',word.substring(1)])
}else{
token.push(["name",word])
}
}
scanner.scan("}}")
}
return foldToken(token)
}
export default function foldToken(tokens) {
//结果数组
let nestedTokens = []
//栈结构,存放小tokens
let section = [];
//与nestedTokens指向的是同一数组,该数组为一级数组
let collentor = nestedTokens
for (const item of tokens) {
switch (item[0]) {
case "#":
//进栈
section.push(item)
collentor.push(item)
//创建新一级的数组
collentor = item[2] = []
break;
case "/":
//出栈
section.pop(item)
//如果都出完了,则回到一级数组,还没出完则回到其上一级
collentor = section.length>0?section[section.length-1][2]:nestedTokens
break;
default:
//仅负责给各级数组添加 "text" 元素
collentor.push(item)
}
}
return nestedTokens;
}
//识别对象嵌套的数据
export default function lookup(data, key) {
//含有嵌套的数据,进行处理来获取数据
if (key.includes() != -1&&key!=".") {
let keyArr = key.split(".")
let temp = data
keyArr.forEach(item=>{
temp = temp[item]
})
return temp
}
//不含嵌套的数据,直接返回即可
return data[key]
}
import lookup from "./lookup"
export default function renderTemplate(templateStr, data) {
let resultStr = '';
for (const item of templateStr) {
if (item[0] == 'text') {
resultStr += item[1]
}
if (item[0] == "name") {
resultStr += lookup(data, item[1])
}
if (item[0] == "#") {
for(let i =0;i<data[item[1]].length;i++){
//简单的数组循环可以 " . " 代替索引
data[item[1]][i] = {
...data[item[1]][i],
".":data[item[1]][i]
}
resultStr += renderTemplate(item[2],data[item[1]][i])
}
}
}
return resultStr.trim()
}
mustache告以段落,下一节虚拟DOM和diff算法