简易版mustache的实现
- 最近在学习mustache的实现,以下简单模拟了一版mustache
- 实现效果如下:
// 通过render方法,实现数据以及模板的整合(也是一个字符串) // templateStr(模板字符串),data(数据) var templateStr = '这是一个{{infor}}数据,{{infor}}数据在这,{{a.b.c}}' var data = { infor:'模拟', a:{ b:{ c:100 } } } MyMustAche.render(templateStr,data) // 得到结果 这是一个模拟数据,模拟数据在这,100
该简易包目录结构如下
- index.js 入口文件
- lookup.js // 该函数功能是通过连续点符号,找到该属性
- nestTokens.js // 折叠tokens,把一维的tokens数组,折叠成具有循环结构的多维tokens数组
- parseArray.js // 遍历tokens每一项时,如果遇到toekn为数组的话,通过该函数处理。
- parseTempalteToTokens.js // 该方法将模板字符串变为tokens数组
- renderTemplate.js // 把折叠号的tokens转为最后处理过的字符串
- Scanner.js // 找到 {{ 以及 }} 字符的索引 以及之前 之后的字符串,通过parseTempalteToTokens整合为tokens数组
index.js
// import Scanner from './Scanner'
import parseTempalteToTokens from './parseTempalteToTokens'
import renderTemplate from './renderTemplate'
window.MyMustAche = {
render(templateStr,data){
// 通过parseTempalteToTokens转为tokens
var tokens = parseTempalteToTokens(templateStr)
// 调用renderTemplate函数,让tokens数组变为dom字符串
var domStr = renderTemplate(tokens,data)
return domStr
}
}
parseTempalteToTokens
import Scanner from './Scanner.js'
import nestTokens from './nestTokens'
// 将模板字符串变为tokens数组
export default function parseTempalteToTokens(){
var tokens = []
// 创建扫描器
var scanner = new Scanner(templateStr)
var words;
// 让扫描器工作
while (!scanner.eos()) {
// 收集开始标记出现之前的文字
words = scanner.scanUtil('{{')
if(words!=''){
// 去掉空格 不能去除标签中的空格,智能去除文本中的空格
var isInJJH = false
var _words =''
for (let i = 0; i < words.length; i++) {
if(words[i]=="<"){
isInJJH = true
} else if(words[i]=='>'){
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!=''){
// 现在的words就是{{}}之前的内容,
//判断一下首字符,
if(words[0]=='#'){
tokens.push(['#',words.substring(1)])
} else if(words[0] == '/'){
tokens.push(['/',words.substring(1)])
} else {
tokens.push(['name',words])
}
}
// 过}}
scanner.scan('}}')
}
// 返回折叠搜集的tokens
// 当前的tokens是一个一维数组
// 通过nestTokens处理成具有循环结构的二维数组
return nestTokens(tokens)
}
没有通过nestTokens处理的tokens

通过nestTokens处理的数组
Scanner
export default class Scanner {
constructor(templateStr){
// 将templateStr写入this
this.templateStr = templateStr
// 指定,当前位置,默认为0
this.pos = 0;
// 尾巴,一开始就是整个模板字符串
this.tail = templateStr
}
// 就是走过指定内容,没有返回值
scan(tag){
if(this.tail.indexOf(tag)==0){
// tag有多长,比如tag是{{,那就让pos后移几位
this.pos += tag.length
// 尾巴后移
this.tail = this.templateStr.substring(this.pos)
}
}
// 让指针进行扫描,直到遇到指定内容结束,并且返回结束之前扫描路过的文字
scanUtil(stopTag){
// 记录一下执行本方法的时候的pos的值
const pos_backup = this.pos
// 当尾巴的开头不是stopTag的时候,就说明还没扫描到stopTag
while (!this.eos() && this.tail.indexOf(stopTag) != 0 ) {
this.pos ++;
this.tail = this.templateStr.substring(this.pos)
}
// 返回的上一次pos和这一次pos之间的数据
return this.templateStr.substring(pos_backup,this.pos)
}
// 指针是否已经到头,返回布尔值
eos(){
return this.pos>=this.templateStr.length
}
}
nestTokens
// 函数的功能就是折叠tokens,将#和/ 之间的tokens整合起来,
// 因为# 和 / 之间是循环的逻辑(在这个简易的mustache包中)
export default function nestTokens(tokens){
var nestTokens = []
// 栈结构,存放小的tokens
// 栈顶(靠近端口的,最新进入的)tokens数组中当前操作的tokens小数组
var sections = []
// 收集器, 天生指向nestTokens结果数组,引用类型值,
// 所以指向的是同一个数组
var collector = nestTokens;
for (let i = 0; i < tokens.length; i++) {
let token = tokens[i];
switch (token[0]) {
case '#':
// 收集器中放入token
collector.push(token)
// 入栈
sections.push(token)
// 收集器改变
// 给token添加下标为2的项,并让收集器指向他
collector = token[2] = []
break;
case '/':
// 出栈,pop会返回刚刚弹出的项
sections.pop()
// 改变收集器为栈结构队尾那项的下标为2的数组
collector = sections.length>0?sections[sections.length-1][2]:nestTokens
break;
default:
collector.push(token)
break;
}
}
// 返回折叠的tokens
return nestTokens
}
renderTemplate
import lookup from './lookup'
import parseArray from './parseArray'
export default function renderTemplate(tokens,data) {
// 结果字符串
var resulteStr = ''
// 遍历tokens
for (let i = 0; i < tokens.length; i++) {
const token = tokens[i];
//看类型
if(token[0] == 'text'){
//如果是text类型,不处理
resulteStr += token[1]
} else if(token[0]=='name'){
// 如果是name,直接拿数据
resulteStr +=lookup(data,token[1])
} else if(token[0]=='#'){
// 如果是#代表是数组,要递归
resulteStr += parseArray(token,data)
}
}
return resulteStr
}
lookup
// 该函数功能是在dataObj中,可以通过连续点符号的keyName属性,比如
// obj = {
// a:{
// b:{
// c:100
// }
// }
// }
// 要识别出a.b.c = 100
export default function lookup(dataObj,keyName){
if(keyName.indexOf('.')!='-1'&&keyName != '.'){
var keys = keyName.split('.');
var temp = dataObj;
for (let i = 0; i < keys.length; i++) {
temp = temp[keys[i]];
}
return temp
}
return dataObj[keyName]
}
parseArray
// 处理数组
// 这个函数接收的是token,而不是tokens
import lookup from "./lookup";
import renderTemplate from "./renderTemplate";
// 这里的token就是['#',studengs:[]]形式
export default function parseArray(token,data){
var v =lookup(data,token[1]);
var resultStr = ''
for (let i = 0; i < v.length; i++) {
// 这里要补一个.的识别
// 因为模板在使用# / 循环的时候,可以使用.表示当前项的数据
resultStr += renderTemplate(token[2],{
...v[i],
'.':v[i]
})
}
return resultStr
}
本文档详细介绍了如何实现一个简易版的Mustache模板引擎,包括Scanner、parseTempalteToTokens、nestTokens、renderTemplate等核心方法,以及lookup和parseArray辅助函数。通过这些函数,实现了模板字符串与数据的结合,支持#(循环)、/(闭合循环)、name(变量)等基本功能。示例展示了如何处理嵌套的数据结构并输出结果。
1392

被折叠的 条评论
为什么被折叠?



