index.js文件
调用parseTemplateToTokens函数,将传进来的模板字符串转为tokens数组
调用renderTemplate函数,把tokens数组变成DOM结构的字符串
import parseTemplateToTokens from './parseTemplateToTokens'
import renderTemplate from './renderTemplate'
window.MY_templateEngine = {
render(renderStr, data){
// parseTemplateToTokens函数 将传进来的模板字符串转为tokens数组
let tokens = parseTemplateToTokens(renderStr)
let domstr = renderTemplate(tokens, data)
return domstr
}
}
parseTemplateToTokens函数
功能是 将传进来的模板字符串转为tokens数组
import Scanner from "./Scanner"
import nextTokens from "./nextTokens"
export default function parseTemplateToTokens(templateStr) {
let tokens = []
let scanner = new Scanner(templateStr)
let words
while (!scanner.eos()) {
// 这个words是{{}}外面的内容 取消前后空格
words = scanner.scanUtil("{{")
if (!!words) {
tokens.push(['text', words.trim()])
}
scanner.scan("{{")
// 这个words是{{}}中间的内容
words = scanner.scanUtil("}}")
if (!!words) {
// {{#arr}}开头
if (words[0] === '#') {
tokens.push(['#', words.substring(1)])
// {{/arr}}结尾
} else if(words[0] === '/'){
tokens.push(['/', words.substring(1)])
} else{
tokens.push(['name', words])
}
}
scanner.scan("}}")
}
return nextTokens(tokens)
}
renderTemplateToDom函数
// 调用renderTemplateToDom函数,把tokens数组变成DOM结构的字符串
// 函数的功能是tokens数组结合data数据 变成dom结构
import lookup from "./lookup";
import parseArray from "./parseArray";
export default function renderTemplateToDom(tokens, data) {
let resultStr = ''
for (let i = 0; i < tokens.length; i++) {
const token = tokens[i];
if (token[0] === 'text') {
resultStr += token[1]
} else if(token[0] === 'name') {
// 调用lookup函数是为了得到这里是a.b.c的形式
resultStr += lookup(data, token[1])
}
else if(token[0] === '#') {
// 标记的tokens 需要递归处理token[2]的嵌套数组
// 处理数组 结合template实现递归
// resultStr += data[token[1]]
resultStr += parseArray(token, data)
}
}
return resultStr
}
lookup函数
功能是 在dataObj对象中,寻找连续点符号的keyName属性
export default function lookup(dataObj, keyName){
// 存在a.b.c的情况 并且不能是.本身
if (keyName !== '.' && keyName.indexOf('.' !== -1)) {
// 方法一
let keys = keyName.split('.')
let temp = dataObj
for (let i = 0; i < keys.length; i++) {
temp = temp[keys[i]]
}
return temp
}
// 如果没有点符号 直接返回
return dataObj[keyName]
}
nextTokens函数
功能是 折叠tokens 处理嵌套的数组
1、就是说,如果是遇到#,就进栈,遇到/就出栈,而/刚好是结束标签,
2、而出栈的是#那一项,因为现在没有将出栈的收集起来,所以default做的是将text的收集
export default function nextTokens(tokens){
let nextTokens = []
let sections = []
let collector = nextTokens
for (let i = 0; i < tokens.length; i++) {
const 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] : nextTokens
break;
default:
collector.push(token)
}
}
return nextTokens
}
parseArray函数
功能是 处理数组 结合renderTtemplate实现递归
import lookup from "./lookup";
import renderTemplateToDom from "./renderTemplate";
export default function parseArray (token, data) {
let result = ''
let v = lookup(data, token[1])
for (let i = 0; i < v.length; i++) {
// 这里要考虑{{.}} 以及 a.b.c的点属性
// 实现v['.'] = v[i]
result += renderTemplateToDom(token[2], {
...v[i],
'.': v[i]
})
}
return result
}
scanner类
创建scanner类,功能是处理扫描字符串,进行截断和返回
export default class Scanner {
constructor(templateStr){
// 指针
this.pot = 0
// 尾巴
this.tail = templateStr
// 输入的字符串
this.templateStr = templateStr
}
//scan 功能弱 走过指定内容 没有返回值
scan(tag) {
if (this.tail.indexOf(tag) == 0) {
this.pot += tag.length
this.tail = this.templateStr.substring(this.pot)
}
}
//scanUtil 让指针进行扫描,直到遇见指定内容结束,并且能够遇见返回结束之前的文字
scanUtil(stopTag){
// 记录pot的值
const cur_pot = this.pot
while (!this.eos() && this.tail.indexOf(stopTag) !== 0 ) {
this.pot++
// 改变尾巴从当前这个指针开始,到最后的字符串
this.tail = this.templateStr.substring(this.pot)
}
return this.templateStr.substring(cur_pot, this.pot)
}
// 判断指针是否到头
eos(){
return this.pot >= this.templateStr.length
}
}