Vue源码学习笔记-mustache模板引擎(七)-解决数据中对象“.”的链式调用

151 阅读2分钟

解决数据中对象“.”的链式调用

最终 数据结合结果 “我想painting,painting使我快乐!特殊[鼠绘]”

// lookUp.js 解决对象层级的"."的问题,通过临时存储
/**
 * 功能是可以在dataObj对象中,寻找连续点符号的keyName属性
 * 比如,dataObj是
 * {
 *    a:{
 *      b:{
 *        c: 100
 *      }
 *    }
 * }
 * 那么lookup(dataObj,'a.b.c)
 */
export default function (keyName, dataObj) {
  if (keyName.indexOf(".") != -1) {
    let keys = keyName.split(".");
    // 设置临时变量
    let temp = dataObj;
    let i = 0;
    while (i < keys.length) {
      // 每找一层都要重新存储
      temp = temp[keys[i]];
      i++;
    }
    return temp;
  }
  // 没有点
  return dataObj[keyName]
}

// renderTp.js
// 字符串与数据互相转换
import LookUp from './lookUp'
/**
 * 让tokens变为dom字符串
 */
export default function (tokens, data) {
  // 结果字符串
  let convertedStr = ''
  tokens.forEach(i => {
    // 看类型
    if (i[0] === 'text') {
      convertedStr += i[1]
    } else if (i[0] === 'name') {
      // 如果是 name 类型,则要使用lookup 防止 a.b.c有点符号号行是
      convertedStr += LookUp(i[1], data)
    }
  })
  return convertedStr
}
// 数据和字符串模板
 const tmpStr = `我想{{doSth}},{{doSth}}使我快乐!特殊[{{m.b.c}}]`; // 特殊的数据
      const data = {
        doSth: "painting",
        m: {
          b: {
            c: "鼠绘",
          },
        },
      };
      TpEngine.render(tmpStr, data);
// nextTokens.js 折叠token方法
/**
 * 折叠tokens
 * 栈底数组第一位,栈顶-数组尾
 */
export default function (tokens) {
  // console.log(tokens)
  // 结果数组
  let nestedTokens = []
  // 栈数字组,存放小token,栈顶(靠近端口的,最新进入的)tokens数组中当前操作的这个tokens小数组
  let sections = []
  // 收集器,初始指向结果数组,引用类型值,所以指向同一个数组
  // 收集器指向会变化,当遇见#的时候,收集器会指向这个token的下表为2的新数组
  let collector = nestedTokens
  for (let i = 0; i < tokens.length; i++) {
    let token = tokens[i]
    switch (token[0]) {
      case '#':
        // console.log(token, '------------')
        // 收集器里放入token,由于是引用类型,所以同时可以push到nestedTokens最终结果中
        collector.push(token)
        // 入栈
        sections.push(token)
        // console.log('collector',  collector,' nestedTokens', nestedTokens, '-----#---0----')
        // 收集器更换, 给token添加下标为2的数组,并更换收集器指向,改变了collector的引用指向,切断了collector,与最终结果的关系
        // 下面等同于 token[2] = []; collector = token[2]
        collector = token[2] = []
        //  console.log('collector',  collector,' nestedTokens', nestedTokens, '-----#----1---')
        // console.log(nestedTokens)
        break;
      case '/':
        // console.log(sections, '-------------')
        // 出栈,pop()会返回弹出的项目
        sections.pop()
        // 改变收集器为栈结构队尾(队尾为栈顶),那项的下标为2的数组
        collector = sections.length > 0 ? sections[sections.length -1][2] : nestedTokens
        break;
      default:
       //  console.log('collector',  collector,' nestedTokens', nestedTokens, '-----default-------')
        // 如果 没进入栈,则直接结果里存,如果如栈了 就要往栈里 也就是 栈的 【2】数组里存
        collector.push(token)
    }
  }
 return nestedTokens
}
// parseTp2Tokens.js 无变化
import Scanner from './scaner'
import NextTokens from './nextTokens'
import RenderTp  from './renderTp'
export default function (tpStr, data) {
  const tokens = []
  // 创建扫描器
  const ScannerInit = new Scanner(tpStr)
  let words;
  // 扫描器
  while (!ScannerInit.eos()) {
    // 收集开始出现前的文字
    words = ScannerInit.scanUtil('{{')
    if (words !== '') {
      // 存储
      tokens.push(['text', words])
    }
    // 跳过大括号
    ScannerInit.scan('{{')
    words = ScannerInit.scanUtil('}}')
    // 收集标记之间的
    if (words !== '') {
      // 存储
      if (words[0] === '#') {
        // 从下标为1 跳过#号开始存
        tokens.push(['#', words.substring(1)])
      } else if (words[0] === '/') {
        tokens.push(['/', words.substring(1)])
      } else {
        tokens.push(['name', words])
      }
    }
    // 跳过大括号
    ScannerInit.scan('}}')
  }
  const nextTokens = NextTokens(tokens)
  // 折叠token
  return RenderTp(nextTokens, data)
}
// 扫描类未改变
// 创建扫描类
export default class Scanner {
  constructor(tpStr) {
    console.log('render函数里 调用Scanner')
    this.tpStr = tpStr
    // 建立指针
    this.pos = 0
    // 尾部字符串,默认为整个字符串
    this.tail = tpStr
  }
  scan(tag) {
    if (this.tail.indexOf(tag) === 0) {
      // tag 有多长,指针就往后移动几位
      this.pos += tag.length
      // tail1也要调整
      this.tail = this.tpStr.substring(this.pos)
    }
  }
  // 让指针扫描吗,直到内容结束,并返回之前查到的文字
  scanUtil(stopTag) {
    // 记录指针原始位置
    const pos_backup = this.pos
    // 当尾巴的开头不是 stopTag的时候,就说明没有扫描到stopTag
    // 写 && 很有必要,防止越界 
    while (this.tail.indexOf(stopTag) !== 0 && !this.eos()) {
      this.pos++
      //
      // console.log('pos--------------', this.pos)
      // 重新计算尾巴,从指针开始,到最后一位(第一个参数是启始,第二个是个数,如果省略了该参数)
      this.tail = this.tpStr.substr(this.pos)
    }
    // 返回位于 String 对象中指定位置的子字符串(两个指定下标之间的字符,如果省略该参数,那么返回的子串会一直到字符串的结尾)
    return this.tpStr.substring(pos_backup, this.pos)
  }
  // 指针防止越界
  eos() {
    return this.pos >= this.tpStr.length
  }
}