mustache 模板引擎 - 03(完结)

1,327 阅读2分钟

本文接上两篇 《mustache 模板引擎 - 01》 《mustache 模板引擎 - 02》
继续分享对于 mustache 的学习笔记

之前的笔记中,我们已经成功实现了将模板字符串编译为 tokens 的过程,剩下的工作就是将 tokens 结合数据解析成 dom 字符串。即下图中的红框部分

2021-04-29_111352.png

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

感谢.gif

点赞.png