当前位置:   article > 正文

vue原理--mustache模板引擎_vue mustache

vue mustache

1. mustache模板引擎

因为该库的模板语法采用的 {{}} 酷似胡子,所以起名mustache (从mustache的git仓库搬来的一张图)
在这里插入图片描述

1-1 what ?

模板引擎是什么?
模板引擎是将数据转换为视图的一种解决方案
至于转换成什么样的视图,完全由程序员自己写出来的模板决定,如下一套数据,就可以转换为很多不同的视图
在这里插入图片描述

1-2 why ?

为什么要学习模板引擎?
mustache是最早的模板引擎库,它的底层实现机理在当时是非常有创造性的、轰动性的,为后续模板引擎的发展提供了崭新的思路。要想学会其他更丰富类库的模板引擎,学习好mustache的模板引擎是很有必要的。

1-3 how ?

怎样学习mustache模板引擎的底层原理呢?

1-3-1 学会使用mustache模板引擎库

该库的使用方法和vue很相似,简单的给大家总结一下该库的使用:
templateStr中的模板字符串,要使用mustache的模板语法

  1. 简单数据的嵌入
//简单插入直接使用{{变量名}}
var templateStr = `<h1>我买了一个{{thing}},好{{mood}}啊</h1>`;
var data = {
   thing: '华为手机',
    mood: '开心'
};
var domStr = mustache.render(templateStr, data);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  1. 循环数据
// {{#arr}}...{{/arr}} 表示循环该部分内容,只有一个点代表数组中是基本数据
var templateStr = `
            <ul>
                {{#arr}}
                    <li>{{.}}</li>    
                {{/arr}}
            </ul>
        `;

        var data = {
            arr: ['A', 'B', 'C']
        };

        var domStr = Mustache.render(templateStr, data);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  1. 复杂循环嵌套数据的嵌入
// 不需要使用students.name
let templateStr = `
        <div>
            {{#students}}
                <div class="myli">
                    学生{{name}}的爱好是
                    <ul>
                        {{#hobbies}}
                        <li>{{.}}</li>
                        {{/hobbies}}
                    </ul>
                </div>
            {{/students}}
            
        </div>
        `
        let data = {
            students:[
                {name:'Tom',age:15,hobbies:['吃饭',"睡觉"]},
                {name:'Alice',age:15,hobbies:['吃饭',"睡觉","学习"]}
            ]
        }
        var domStr = mustache.render(templateStr, data);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  1. 布尔值的使用:
//该使用类似于vue中的v-if:当m的值为false是该结构不显示,也不存在于dom树上,为ture是显示该结构
var templateStr = `
            {{#m}}
                <h1>你好</h1>
            {{/m}}
        `;
var data = {
     m: false
 };
var domStr = Mustache.render(templateStr, data);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  1. 对象
var templateStr = `<h1>我买了一个{{a.thing}}</h1>`;
var data = {
   a:{
   		thing:"华为手机"
   }
};
var domStr = mustache.render(templateStr, data);

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

1-3-2 掌握模板引擎的核心机理

在这里插入图片描述

第一步:将模板字符串解析为tokens数组

  1. tokens数组是什么?
    js的一个嵌套数组,用来表示模板字符串。
    ”{{“ 和 ”}}“ 不会体现在tokens中,模板语法插入的数据为name,其余普通字符为text,循环为”#“,空格换行也会包含在其中
1.模板字符串:
<h1>我买了一个{{thing}},好{{mood}}</h1>

1.tokens:
[
["text", "<h1>我买了一个"],
["name", "thing"],
["text", "好"],
["name", "mood"],
["text", "啊</h1>"],
]

2.模板字符串
<div>
	<ul>
		{{#arr}}
			<li>{{.}}</li>
		{{/arr}}
	</ul>
</div>
2.tokens 
 [
["text", "<div><ul>"],
["#", "arr", [
	["text", "<li>"],
	["name", "."],
	["text", "</li>"]
]],
["text", "</ul></div>"]
]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

该部分会有两个核心部分:①得到平铺tokens ②得到nesttokens(最终的token形状)
在这里插入图片描述

1. 根据templateStr模板字符串,得出平铺的tokens数组

平铺的tokens数组,没有嵌套,还不能完全代表templateStr字符串

function parseTemplateToTokens(templateStr){
  const scanner = new Scanner(templateStr)
  let tokens = []
    while(scanner.tail != ""){
      let word = scanner.scanUtil('{{')
      tokens.push(['text',word])
      scanner.scan("{{")
      word = scanner.scanUtil('}}')
      
      // {{内容}} 这里面的内容可能不是name
      if(word[0] === "#"){
        tokens.push(['#',word.substring(1)])
      }else if(word[0] === '/'){
        tokens.push(['/',word.substring(1)])
      }else{
        if(word != "")
          tokens.push(["name",word])
      }
      scanner.scan('}}')
    }
    return tokens   
}
//用于遍历templateStr的类
class Scanner{
  constructor(templateStr){
    this.templateStr = templateStr
    // 遍历需要的指针
    this.pos = 0
    // 未遍历的字符串
    this.tail = templateStr
  }
  // 跳过指定标记
  scan(stopTag){
    this.pos += stopTag.length
    this.tail = this.templateStr.substring(this.pos)
  }
  // 遍历模板字符串,收集指定字符,并返回
  scanUtil(stopTag){
    let startPos = this.pos
    // 不断修改tail,使 {{ 变为最开始的字符
    while(this.pos < this.templateStr.length && this.tail.indexOf(stopTag) != 0){
      this.pos++
      this.tail = this.templateStr.substring(this.pos)
    }
    return this.templateStr.substring(startPos,this.pos)
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

2. 根据平铺的tokens数组,完善为嵌套的tokens数组

这里用到了栈:这里用到的栈本质是一个数组,具有先进后出的特点,

function(tokens){
  // 栈
  let sections = []
  // 结果token
  let nestedTokens = []
  // 当前插入数组:当前正在栈顶的token的插入项token[2]或者nestedTokens
  let collector = nestedTokens

  for(let i=0;i<tokens.length;i++){
    let token = tokens[i]
    switch(token[0]){ 
      case '#':
        collector.push(token)
        sections.push(token)
        collector = token[2] = []
        break
      case '/':
        sections.pop()
        collector = sections.length > 0 ? sections[sections.length-1][2] : nestedTokens
        break
      default:
        collector.push(token)
    }
  }
  return nestedTokens
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

第二步:将tokens数组与data数据整合,得出dom字符串

function renderTemplate(tokens,data){
  let domStr = ''
  for(let i=0;i<tokens.length;i++){
    let token = tokens[i]
    if(token[0] === 'text'){
      domStr += token[1]
    }else if(token[0] === 'name'){
      if(token[1].includes(".")){
        domStr += lookup(data,token[1])
      }else{
        domStr += data[token[1]]
      }
    }else if(token[0] === "#"){
      domStr += parseArray(token,data)
    }
  }
  return domStr
}

//根据str,寻找并返回obj中对应的数据
function lookup(obj,str){
  // ".".split(".") = [[],[]]
  if(str !== "."){
    let strArr = str.split(".")
    for(let i=0;i<strArr.length;i++){
      obj = obj[strArr[i]]
    }
    return obj
  }
  return obj[str]
   
}
//递归解析数组
function parseArray(tokens,data){
  let targetArr = lookup(data,tokens[1])
  let resStr = ''
  for(let i=0;i<targetArr.length;i++){
  	//{...targetArr[i],".":targetArr[i]} 是为了处理{{.}}这种情况
    resStr += renderTemplate(tokens[2],{...targetArr[i],".":targetArr[i]})
  }
  return resStr
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

大功告成!!

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

闽ICP备14008679号