当前位置:   article > 正文

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

mustache substring

目录

两个几个问题

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()方法,将这个无嵌套的数组转为原本模板对应的嵌套结构

    1. import Scanner from './Scanner.js'
    2. import nextTokens from './nextTokens.js'
    3. /**
    4. * 将模板字符串变为数组
    5. */
    6. export default function parseTemplateToTokens(templateStr){
    7. var tokens = [];
    8. // 创建扫描器
    9. var scanner = new Scanner(templateStr);
    10. var words;
    11. //让扫描器工作
    12. while(!scanner.eos()){
    13.   // 收集开始标记
    14.   words = scanner.scanUtil("{{");
    15.   // 保存
    16.   if(words!=''){
    17.     tokens.push(['text',words]);
    18.   }
    19.   // 过双大括号
    20.   scanner.scan("{{")
    21.     // 收集开始标记
    22.     words = scanner.scanUtil("}}");
    23.     // 保存
    24.     if(words!=''){
    25.       // 这时的words就是{{}}中间的东西,判断首字符
    26.       if(words[0] == "#"){
    27.         // 存起来,从下标为1的项开始存,因为下标为0的像是#
    28.         tokens.push(["#",words.substring(1)]);
    29.       }else if(words[0] == '/'){
    30.         // 存起来,从下标为1的项开始存,因为下标为0的项是/
    31.         tokens.push(["/",words.substring(1)]);
    32.       }else{
    33.         // 存起来
    34.         tokens.push(['name',words]);
    35.       }
    36.     }
    37.       // 过双大括号
    38.   scanner.scan("}}")
    39. }
    40. // 返回折叠收集的tokens
    41. console.log(tokens);
    42. return nextTokens(tokens);
    43. }

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代码

    1. /**
    2. * 扫描类
    3. */
    4. export default class Scanner{
    5. constructor(templateStr){
    6.   console.log(templateStr);
    7.   // 将模板字符串写到实例身上
    8.   this.templateStr = templateStr;
    9.   // 指针
    10.   this.pos =0;
    11.   // 尾巴
    12.   this.tail = templateStr;
    13. }
    14. // 功能弱,就是走过指定内容,没有返回值  
    15. scan(tag){
    16.   if(this.tail.indexOf(tag)==0){
    17.     // tag有多长,比如{{长度是2,就让指针后移多少位
    18.     this.pos += tag.length;
    19.     // 尾巴也改变.改变尾巴位当前指针这个字符开始,到最后的全部字符
    20.     this.tail = this.templateStr.substring(this.pos)
    21.   }
    22. }
    23. // 让指针进行扫描,直到指定内容结束,并且能够返回结束之前路过的文字
    24. scanUtil(stopTag){
    25.   // 记录一下执行本方法的时候的pos的值
    26.   const pos_backup = this.pos;
    27.   // 当尾巴的开头不是stopTag的时候,说明还没有扫描到stopTag
    28.   // 写&&很必要,因为防止找不到,那么寻找到最后也要停下来
    29.   while( !this.eos() && this.tail.indexOf(stopTag) !=0 ){
    30.     this.pos++
    31.     // 改变尾巴为从当前指针这个字符开始,到最后的全部字符
    32.     this.tail = this.templateStr.substring(this.pos);
    33.   }
    34.   return this.templateStr.substring(pos_backup,this.pos);
    35. }
    36. // 指针是否到头,返回布尔值
    37. eos(){
    38.   return this.pos >= this.templateStr.length;
    39. }
    40. }

nextTokens(处理tokens为嵌套结构)

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

    1. /**
    2. * 函数的功能是折叠tokens,将#和/之间的tokens能够整合起来,最为他的下标为3的项
    3. */
    4. export default function nextTokens(tokens){
    5. // 结果数组
    6. var nextedTokens = [];
    7. // 栈结构,存放小的tokens,栈顶(靠近端口的,最新进入的)的tokens数组中当前的操作的这个tokens小数组
    8. var sections = [];
    9. // 收集器,天生指向nextedTokens结果数组,引用类型值,所以指向的是同一个数组
    10. // 收集器的指向会变化,当遇到#的时候,收集器会指向这个下标为2的新数组
    11. var collector = nextedTokens;
    12. for(let i=0; i<tokens.length; i++){
    13.   let token = tokens[i];
    14.   switch(token[0]){
    15.     case '#':
    16.       // 收集器中放入token
    17.       collector.push(token);
    18.     // 入栈
    19.     sections.push(token);
    20.     // 收集器换人,给token添加下表为2的项,并且让收集器指向他
    21.     token[2]=[];
    22.     collector=token[2];
    23.       break;
    24.     case '/':
    25.       // 出栈,pop()会返回刚刚弹出的项
    26.       sections.pop();
    27.       // 改变收集器为栈结构队尾(队尾为栈顶) 那项的下标为2的数组
    28.       collector = sections.length>0?sections[sections.length-1][2]:nextedTokens;
    29.       break;
    30.     default:
    31.       collector.push(token);
    32.   }
    33. }
    34. return nextedTokens;
    35. }

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

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

    1. import lookup from "./lookup.js";
    2. import parseArray from "./parseArray.js";
    3. /**
    4. * 函数的功能是让tokens数组变为dom字符串
    5. */
    6. export default function renderTemplate(tokens,data){
    7. console.log(tokens,data);
    8. // 结果字符串
    9. var resultStr = '';
    10. // 遍历tokens
    11. for(let i=0;i<tokens.length;i++){
    12.   let token = tokens[i];
    13.   // 看类型
    14.   if(token[0] == 'text'){
    15.     resultStr +=token[1];
    16.   }else if(token[0] == 'name'){
    17.     // 如果是name类型,直接使用他的值,使用lookup
    18.     // 因为防止出现'a.b.c'的形式造成的无法识别问题
    19.     resultStr += lookup(data, token[1]);
    20.   }else if(token[0] == '#'){
    21.     console.log(token);
    22.     resultStr += parseArray(token,data)
    23.   }
    24. }
    25. return resultStr;
    26. }

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

    1. /**
    2. * 功能是可以在dataObj对象中,寻找用连续点符号的keyName属性
    3. * 比如dataObj是
    4. * {
    5. * a:{
    6. *   b:{
    7. *     c:100
    8. *   }
    9. * }
    10. * }
    11. * 那么lookup(dataObj,'a.b.c')结果是abc
    12. */
    13. export default function lookup(dataObj, keyName) {
    14. // 首先判断是否含有点符号,但不能是点本身
    15. if(keyName.indexOf('.') !=-1 && keyName!=".") {
    16.   // 如果有点符号,那么拆开
    17.   var keys = keyName.split('.');
    18.   // 临时变量,用于周转,一层一层往下找
    19.   var temp = dataObj;
    20.   for(let i=0; i<keys.length; i++){
    21.     temp =temp[keys[i]];
    22.   }
    23.   return temp;
    24. }
    25. // 如果没有点符号
    26. return dataObj[keyName]
    27. }

parseArray(循环数组渲染dom

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

梳理

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

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

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

总结

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

  • 学习地址

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/610775
推荐阅读
相关标签
  

闽ICP备14008679号