vue模板引擎mustache03__实现(笔记)

本文详细介绍了HTML模板引擎的工作流程,包括Scanner类的使用来将HTML拆分成tokens,nextTokens方法处理tokens形成嵌套结构,以及如何通过renderTemplate和lookup、parseArray方法将数据与tokens结合生成DOM。整个过程涉及模板字符串的扫描、tokens的处理和数据的渲染,揭示了模板引擎在前后端交互中的关键作用。
摘要由CSDN通过智能技术生成

目录

两个几个问题

HTML模板如何变为tokens形式的嵌套格式的数组?

数据data又怎们和tokens结合变为变为理想的dom然后上树的?

流程

parseTemplateToTokens(将HTML变为tokens)

Scanner扫描器

substring()方法

nextTokens(处理tokens为嵌套结构)

renderTemplate`(将tokens和data数据结合,返回处理好的dom然后上树)

lookup(寻找用连续点符号的keyName属性)

parseArray(循环数组渲染dom)

梳理

总结


两个几个问题

HTML模板如何变为tokens形式的嵌套格式的数组?

数据data又怎们和tokens结合变为变为理想的dom然后上树的?

流程

  • www文件夹下的index为入口文件,全局封装对象Fel_TemplateEngine(自定义)

  • 内部暴露一个render函数作为入口,供页面调用接收两个参数,模板(templateStr)和数据(data)

  • Scanner类,作为扫描器将模板编译为tokens(无嵌套格式,一个数组走到末尾)

  • nextTokens类,编译好的tokens做处理,当做

  • 建立parseTemplateToTokens类,

parseTemplateToTokens(将HTML变为tokens)

  • 理解parseTemplateToTokens将Scanner扫描器和nextTokens(零星的tokens嵌套起来)

  • scanner在的作用是扫描模板字符串,遇到"{{" 、"}}"、"#"、"/"、"^"等符号进行拆分,并整合到一个数组中,但是结果是一个常规的数组,并未分层,和嵌套数组所应该有的嵌套结构不符,所以就有了nextTokens()方法,将这个无嵌套的数组转为原本模板对应的嵌套结构

  • import Scanner from './Scanner.js'
    import nextTokens from './nextTokens.js'
    /**
     * 将模板字符串变为数组
     */
    export default function parseTemplateToTokens(templateStr){
      var tokens = [];
      // 创建扫描器
      var scanner = new Scanner(templateStr);
      var words;
      //让扫描器工作
      while(!scanner.eos()){
        // 收集开始标记
        words = scanner.scanUtil("{{");
        // 保存
        if(words!=''){
          tokens.push(['text',words]);
        }
        // 过双大括号
        scanner.scan("{{")
          // 收集开始标记
          words = scanner.scanUtil("}}");
          // 保存
          if(words!=''){
            // 这时的words就是{{}}中间的东西,判断首字符
            if(words[0] == "#"){
              // 存起来,从下标为1的项开始存,因为下标为0的像是#
              tokens.push(["#",words.substring(1)]);
            }else if(words[0] == '/'){
              // 存起来,从下标为1的项开始存,因为下标为0的项是/
              tokens.push(["/",words.substring(1)]);
            }else{
              // 存起来
              tokens.push(['name',words]);
            }
          }
           // 过双大括号
        scanner.scan("}}")
      }
      // 返回折叠收集的tokens
      console.log(tokens);
      return nextTokens(tokens);
    }

Scanner扫描器

  • 单独建立Scanner.js文件,作为扫描器类

  • 内部三个函数

    • constructor构造函数接收一个模板(templateStr

    • scan扫描方法

    • scanUtil方法

  • 大致流程

    以此模板为例

    我买了一个{{thing}},好{{mod}}阿

    左侧起始位置一个指针,指针往右移动从"我"到"个"的这个过程为scanUtil ,然后以scan扫描"{{",指针做过"thing"的过程为scanUtil....直至末尾

  • 相关知识点

    • substring()方法

      1、定义和用法
      substring() 方法用于提取字符串中介于两个指定下标之间的字符。
      substring() 方法返回的子串包括 开始 处的字符,但不包括 结束 处的字符。
      2、语法
      string.substring(from, to)
      3、参数
      from    必需。一个非负的整数,规定要提取的子串的第一个字符在 string Object 中的位置。
      to  可选。一个非负的整数,比要提取的子串的最后一个字符在 string Object 中的位置多 1。
      如果省略该参数,那么返回的子串会一直到字符串的结尾。
    • indexOf()方法

      1、定义和用法
      indexOf() 方法可返回某个指定的字符串值在字符串中首次出现的位置。
      如果没有找到匹配的字符串则返回 -1。
      注意: indexOf() 方法区分大小写。
      提示: 同样你可以查看类似方法 lastIndexOf() 。
      2、语法
      string.indexOf(searchvalue,start)
      3、参数
      searchvalue 必需。规定需检索的字符串值。
      start   可选的整数参数。规定在字符串中开始检索的位置。它的合法取值是 0 到 string Object.length - 1。如省略该参数,则将从字符串的首字符开始检索。
      4、返回值
      Number类型 查找指定字符串第一次出现的位置,如果没找到匹配的字符串则返回 -1。

  • Scanner.js代码

    /**
     * 扫描类
     */
    export default class Scanner{
      constructor(templateStr){
        console.log(templateStr);
        // 将模板字符串写到实例身上
        this.templateStr = templateStr;
        // 指针
        this.pos =0;
        // 尾巴
        this.tail = templateStr;
      }
      // 功能弱,就是走过指定内容,没有返回值  
      scan(tag){
        if(this.tail.indexOf(tag)==0){
          //  tag有多长,比如{{长度是2,就让指针后移多少位
          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);
        }
        return this.templateStr.substring(pos_backup,this.pos);
      }
      // 指针是否到头,返回布尔值
      eos(){
        return this.pos >= this.templateStr.length;
      }
    }

nextTokens(处理tokens为嵌套结构)

  • 利用栈的先入后出的思想,结合对应的储存从而实现结构的更改,比较精妙的还有收集器的创建,一直指向最终要返回的数组,类似于一种引用,看似一直操作的是这个收集器,其实也相当于对要返回的数组进行添加,通过收集器指向的改变控制返回数组的结构。

  • /**
     * 函数的功能是折叠tokens,将#和/之间的tokens能够整合起来,最为他的下标为3的项
     */
    export default function nextTokens(tokens){
      // 结果数组
      var nextedTokens = [];
      // 栈结构,存放小的tokens,栈顶(靠近端口的,最新进入的)的tokens数组中当前的操作的这个tokens小数组
      var sections = [];
      // 收集器,天生指向nextedTokens结果数组,引用类型值,所以指向的是同一个数组
      // 收集器的指向会变化,当遇到#的时候,收集器会指向这个下标为2的新数组
      var collector = nextedTokens;
      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的项,并且让收集器指向他
          token[2]=[];
          collector=token[2];
            break;
          case '/':
            // 出栈,pop()会返回刚刚弹出的项
            sections.pop();
            // 改变收集器为栈结构队尾(队尾为栈顶) 那项的下标为2的数组
            collector = sections.length>0?sections[sections.length-1][2]:nextedTokens;
            break;
          default:
            collector.push(token);
       }
      }
      return nextedTokens;
    }

renderTemplate`(将tokens和data数据结合,返回处理好的dom然后上树)

  • 内部通过对tokens的循环,判断当前token的第一个值如果text则直接加在需要返回的数据后面,如果是name则说明要添加data数据,不过在添加之前需要用lookup函数处理,防止识别错误造成渲染失败,遇到#号则说明需要循环数组遍历添加数据,结合parseArray函数来回嵌套形成递归调用从而将处理数组及其嵌套的数据渲染

  • import lookup from "./lookup.js";
    import parseArray from "./parseArray.js";
    /**
     * 函数的功能是让tokens数组变为dom字符串
     */
    export default function renderTemplate(tokens,data){
      console.log(tokens,data);
      // 结果字符串
      var resultStr = '';
      // 遍历tokens
      for(let i=0;i<tokens.length;i++){
        let token = tokens[i];
    ​
        // 看类型
        if(token[0] == 'text'){
          resultStr +=token[1];
        }else if(token[0] == 'name'){
          // 如果是name类型,直接使用他的值,使用lookup
          // 因为防止出现'a.b.c'的形式造成的无法识别问题
          resultStr += lookup(data, token[1]);
        }else if(token[0] == '#'){
          console.log(token);
          resultStr += parseArray(token,data)
        }
      }
      return resultStr;
    }

lookup(寻找用连续点符号的keyName属性)

  • /**
     * 功能是可以在dataObj对象中,寻找用连续点符号的keyName属性
     * 比如dataObj是
     * {
     *  a:{
     *    b:{
     *      c:100
     *    } 
     *  }
     * }
     * 那么lookup(dataObj,'a.b.c')结果是abc
     */
    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(循环数组渲染dom

  • import lookup from "./lookup.js"
    import renderTemplate from "./renderTemplate.js"
    /**
     * 处理数组,结合renderTemplate实现递归
     * 注意函数接收的参数是token不是tokens
     * 
     * 这个函数要递归调用renderTemplate函数,调用次数由data界定
     */
    export default function parseArray(token,data){
      // 得到整体数组中这个数据要使用的部分
      console.log(token,data);
      var v=  lookup(data,token[1]);
      console.log(v);
      // 结果字符串
      var resultStr = '';
      // 遍历v数组,v可能为数组也可能为布尔值,此处布尔值忽略
      // 这个循环可能是这个包最那思考的一个循环
      // 他是遍历数据而不是遍历tokens
      for(let i=0; i< v.length; i++) {
        resultStr += renderTemplate(token[2],{
          // 现在这个数据小对象是v[i]的展开,就是v[i]本身
          ...v[i],
          '.':v[i]
        })
      }
      console.log(resultStr);
      return resultStr
    }

梳理

  • mustache中的模板引擎的功能主要是将HTML模板代码和需要渲染的数据相结合,然后是返回模板和数据结合后的DOM,模板引擎在工作中可以分为两大块,一个是将事先准备好的HTML解析为tokens数组的形式,方便后面和数据之间的结合(parseTemplateToTokens),另一个是就是将data和tokens结合返回真实的dom了(renderTemplate)。

  • parseTemplateToTokens中它又可以分为两小块,一个是扫描器(scanner.js,Scanner类)一个是nextTokens方法,将初步得到的tokens处理为嵌套的,和原DOM结构嵌套而是一样的形式

  • renderTemplate 中也有又两小块,一个是lookup判断处理防止渲染出错,另一个是parseArray方法循环数组渲染dom,利用栈的思想将tokens读取完毕即结束,返回最终dom

总结

  • 尽管是跟着视频把代码敲了出来但是还需要进一步的研究,其中的一些奥妙还是需要继续理解一番,对一些函数的处理理解的并不是很透彻,总结不是很到位,包括这个记录还是又不少问题,比如自身的理解太少、比如内容相对有点拖泥带水感觉,再接再励了。

  • 学习地址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值