本文接上两篇 《mustache 模板引擎 - 01》 《mustache 模板引擎 - 02》
继续分享对于 mustache 的学习笔记
之前的笔记中,我们已经成功实现了将模板字符串编译为 tokens 的过程,剩下的工作就是将 tokens 结合数据解析成 dom 字符串。即下图中的红框部分
tokens 结合数据解析为 dom 字符串
大致思路是遍历 tokens 数组,根据每条 token 的第一项的值来做不同的处理,为 text 就直接把 token[1] 加入到最终输出的 dom 字符串,为 name 则根据 token[1] 去 data 里获取数据,结合进来。
当 data 里存在多层嵌套的数据结构,比如 data = { test: { a: { b: 10 } } }
,这时如果某个 token 为 ["name", "test.a.b"],即代表数据的 token 的第 2 项元素是 test.a.b 这样的有多个点符号的值,那么我们直接通过 data[test.a.b]
是无法拿到正确的值的,因为 js 不认识这种写法。我们需要提前准备一个 lookup
函数,用以正确获取数据。
定义 lookup 函数
// lookup.js
// 思路就是先获取 test.a 的值, 比如说是 temp, 再获取 temp.b 的值, 一步步获取
export default (data, key) => {
// 如果传入的 key 里有点符号而且不是仅仅只是点符号
if (key.indexOf('.') !== -1 && key !== '.' ) {
const keys = key.split('.') // 将 key 用 . 分割成一个数组
return keys.reduce((acc, cur) => {
return acc[cur] // 一步步获取
}, data)
}
// 如果传入的 key 没有点符号,直接返回
return data[key]
}
定义 renderTemplate 函数
接下来就可以开始写个 renderTemplate
函数将 tokens 和 data 作为参数传入,解析为 dom 字符串了。
// renderTemplate.js
import lookup from './lookup.js'
import parseArray from './parseArray.js'
export default (tokens, data) => {
let domString = ''
tokens.forEach(token => {
switch (token[0]) {
case 'text':
domString += token[1]
break
case 'name':
domString += lookup(data, token[1])
break
case '#':
domString += parseArray(token[2], data[token[1]])
break
default:
break
}
})
return domString
}
需要注意的是遇到循环的情况,也就是当某个 token 的第一项为 #
时,要再次递归调用 renderTemplate
函数。这里我们新定义了一个 parseArray
函数来处理。
// parseArray.js
import renderTemplate from './renderTemplate.js'
export default (tokens, data) => {
let domString = ''
data.forEach(itemData => {
domString += renderTemplate(tokens, {
...itemData,
'.': itemData // 针对简单数组的情况,即模板字符串里的 {{.}}
})
})
return domString
}
到此,本系列的笔记就算是完结了。能力一般,水平有限,难免会有纰漏不足的地方,还请各位看官斧正。
One More Thing
这里对上篇最后提出连等赋值的问题做个解答
let a = {n:1};
a.x = a = {n:2};
console.log(a.x); // 输出?
a.x
之所以为 undefined
的关键原因在于 js 的运算中.
和 =
运算符同时出现,会先执行 .
运算,所以 a.x = a = {n:2}
这一步是先给 a
增加了一个属性 x
,而此时 a
还是指向 {n:1}
对象。也就是说原来的 {n:1}
对象现在变成了 {n:1, x:a}
, 之后 a
被重新赋值了指向了 {n:2}
对象,所以 {n:1, x:a}
就没有任何变量引用被 GC 回收了。故而 a.x
的值是 undefined
。